1. Planification des threads de travail dans HikariPool
Les threads de travail dans HikariPool sont planifiés via ThreadPoolExecutor, il y a 3 instances de ThreadPoolExecutor au total,
ThreadPoolExecutor | Responsabilités | Stratégie de traitement après surcharge |
---|---|---|
houseKeepingExecutorService | Responsable 1. Réduire les connexions à la base de données dans la mise à l'échelle dynamique du pool de connexions à la base de données 2. Surveiller les fuites de connexion à la base de données 3. Surveiller les connexions à la base de données au-delà de la durée de vie maximale |
Abandonner |
addConnectionExecutor | Responsable de la création de connexions à la base de données, y compris l'ajout de nouvelles connexions à la base de données lorsque le pool de connexions à la base de données évolue dynamiquement. | Abandonner |
closeConnectionExecutor | Responsable de la fermeture de la connexion à la base de données. | Répéter l'exécution jusqu'à réussite |
1.1. houseKeepingExecutorService
Instanciation:
//HikariPool.java
private ScheduledExecutorService initializeHouseKeepingExecutorService()
{
if (config.getScheduledExecutor() == null) {
final ThreadFactory threadFactory = Optional.ofNullable(config.getThreadFactory()).orElseGet(() -> new DefaultThreadFactory(poolName + " housekeeper", true));
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.DiscardPolicy());
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
executor.setRemoveOnCancelPolicy(true);
return executor;
}
else {
return config.getScheduledExecutor();
}
}
复制代码
1.1.1 Surveillance des fuites de connexion
//HikariPool.java
this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
复制代码
//ProxyLeakTaskFactory.java
private ProxyLeakTask scheduleNewTask(PoolEntry poolEntry) {
ProxyLeakTask task = new ProxyLeakTask(poolEntry);
// executorService就是houseKeepingExecutorService
task.schedule(executorService, leakDetectionThreshold);
return task;
}
复制代码
//ProxyLeakTask.java
ProxyLeakTask(final PoolEntry poolEntry)
{
this.exception = new Exception("Apparent connection leak detected");
this.threadName = Thread.currentThread().getName();
this.connectionName = poolEntry.connection.toString();
}
public void run()
{
isLeaked = true;
final StackTraceElement[] stackTrace = exception.getStackTrace();
final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 5];
System.arraycopy(stackTrace, 5, trace, 0, trace.length);
exception.setStackTrace(trace);
// 下面是监控到连接泄漏的处理,这里只是记录到日志中,如果通过一个接口处理,并可以让使用者动态实现会更灵活
LOGGER.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
}
复制代码
1.1.2 Mise à l'échelle dynamique du pool de connexions
//HikariPool.java
// HouseKeeper是负载连接池动态伸缩的工作线程
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
复制代码
1.1.3 Surveillance de la durée de vie maximale des connexions à la base de données
final long maxLifetime = config.getMaxLifetime();
if (maxLifetime > 0) {
// variance up to 2.5% of the maxlifetime
final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;
final long lifetime = maxLifetime - variance;
poolEntry.setFutureEol(houseKeepingExecutorService.schedule(
() -> {
if (softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false /* not owner */)) {
addBagItem(connectionBag.getWaitingThreadCount());
}
},
lifetime, MILLISECONDS));
}
复制代码
1.2. addConnectionExecutor
Instanciation:
//HikariPool.java
this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());
复制代码
//UtilityElf.java
public static ThreadPoolExecutor createThreadPoolExecutor(final BlockingQueue<Runnable> queue, final String threadName, ThreadFactory threadFactory, final RejectedExecutionHandler policy)
{
if (threadFactory == null) {
threadFactory = new DefaultThreadFactory(threadName, true);
}
ThreadPoolExecutor executor = new ThreadPoolExecutor(1 /*core*/, 1 /*max*/, 5 /*keepalive*/, SECONDS, queue, threadFactory, policy);
executor.allowCoreThreadTimeOut(true);
return executor;
}
复制代码
Ajouter une connexion:
//HikariPool.java
public void addBagItem(final int waiting)
{
final boolean shouldAdd = waiting - addConnectionQueue.size() >= 0; // Yes, >= is intentional.
if (shouldAdd) {
addConnectionExecutor.submit(poolEntryCreator);
}
}
// 连接词动态伸缩增加连接
private synchronized void fillPool()
{
final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections())
- addConnectionQueue.size();
for (int i = 0; i < connectionsToAdd; i++) {
addConnectionExecutor.submit((i < connectionsToAdd - 1) ? poolEntryCreator : postFillPoolEntryCreator);
}
}
复制代码
1.3. closeConnectionExecutor
Instanciation:
//HikariPool.java
this.closeConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
复制代码
//UtilityElf.java
public static ThreadPoolExecutor createThreadPoolExecutor(final int queueSize, final String threadName, ThreadFactory threadFactory, final RejectedExecutionHandler policy)
{
if (threadFactory == null) {
threadFactory = new DefaultThreadFactory(threadName, true);
}
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(1 /*core*/, 1 /*max*/, 5 /*keepalive*/, SECONDS, queue, threadFactory, policy);
executor.allowCoreThreadTimeOut(true);
return executor;
}
复制代码
Fermez la connexion:
void closeConnection(final PoolEntry poolEntry, final String closureReason)
{
if (connectionBag.remove(poolEntry)) {
final Connection connection = poolEntry.close();
closeConnectionExecutor.execute(() -> {
quietlyCloseConnection(connection, closureReason);
if (poolState == POOL_NORMAL) {
fillPool();
}
});
}
}
复制代码
2. Outils associés
Classe | Responsabilités |
---|---|
ThreadPoolExecutor | Exécuteur du fil |
BlockingQueue | La file d'attente de mémoire tampon utilisée par le pool de threads, la longueur de la file d'attente détermine le nombre maximal de threads de travail pouvant être mis en mémoire tampon |
ThreadFactory | Créer une fabrique de threads pour les threads de travail |
ScheduledThreadPoolExecutor | Un exécuteur de pool de threads qui prend en charge la planification planifiée peut spécifier une exécution différée et une exécution périodique. De cette façon, vous pouvez définir le temps de retard sur la durée de vie maximale pour contrôler si la connexion à la base de données dépasse la durée de vie maximale |
DefaultThreadFactory | La fabrique de threads par défaut implémentée dans HikariPool définit le nom du thread et définit le thread en tant que thread sprite |
RejectedExecutionHandler | Une interface de stratégie de traitement pour ajouter de nouveaux threads lorsque la file d'attente de threads dans l'exécuteur de thread est pleine |
JeterOldestPolicy | Jetez le plus ancien thread de travail non exécuté dans la file d'attente des threads et ajoutez un nouveau thread de travail, qui n'est pas utilisé dans HikariPool. |
CallerRunsPolicy | Répétez l'exécution jusqu'à ce qu'elle soit réussie, utilisée dans closeConnectionExecutor. |
AbortPolicy | Abandonnez le thread de travail qui dépasse la charge de la file d'attente des threads et lâchez une exception. Non utilisé dans HikariPool. |
JeterPolitique | Ignorez les threads de travail complexes qui dépassent la file d'attente des threads et ne faites rien. Utilisé dans houseKeepingExecutorService et houseKeepingExecutorService. |
3. Classe de base
3.1 ThreadPoolExecutor
Constructeur:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
复制代码
Description des paramètres:
Paramètre | Explication |
---|---|
int corePoolSize | Le nombre minimum de threads à conserver dans l'exécuteur du pool de threads |
int maximumPoolSize | Le nombre maximal de threads autorisés par l'exécuteur du pool de threads |
long keepAliveTime | Temps de rétention des threads inutilisés, si ce temps est dépassé et que le nombre de threads est supérieur au nombre minimum de threads, le thread sera libéré |
TimeUnit | Unité de temps de rétention des threads inutilisés |
BlockingQueue | File d'attente du tampon de threads, l'effet de cette file d'attente sera affecté par maximumPoolSize et ne sera pas placé dans la file d'attente lorsque le nombre de threads sera suffisant. |
ThreadFactory | Interface de fabrique de threads pour générer des threads de travail |
RejectedExecutionHandler | Interface de stratégie de traitement une fois que le nombre de threads de travail est placé dans la file d'attente de cache dépasse la capacité de la file d'attente de cache |
Il y a quelques points à noter ici:
- La file d'attente du tampon de threads doit être définie comme une file d'attente limitée pour éviter un débordement de mémoire dû à une augmentation infinie.
- Le nombre maximal de threads doit également être correctement contrôlé pour éviter de définir la valeur Integer.MAX_VALUE, pour les mêmes raisons que ci-dessus.
- La logique de traitement de la file d'attente du tampon de threads est affectée par corePoolSize et maximumPoolSize. Autrement dit, lorsqu'il y a suffisamment de threads disponibles, le thread de travail ne sera pas placé dans la file d'attente du tampon de threads.
Exemples:
import java.util.concurrent.*;
import static java.util.concurrent.ThreadPoolExecutor.*;
public class ThreadPoolExecutorTest {
private static int runableNum = 1;
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue(3);
// 修改maximumPoolSize和maximumPoolSize的大小可以看到对queue处理逻辑的影响
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 300, TimeUnit.SECONDS,
queue, new DefaultThreadFactory(), new DefaultDiscardPolicy());
while(true) {
System.out.println("runableNum: " + runableNum);
executor.execute(new DefaultRunnable("id-" + runableNum));
runableNum++;
quietlySleep(500);
}
}
private static void quietlySleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
private static class DefaultRunnable implements Runnable {
private String name;
public DefaultRunnable(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Runnable-" + name + " run.");
quietlySleep(3000);
}
public String getName() {
return this.name;
}
}
private static class DefaultDiscardPolicy extends DiscardPolicy {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
super.rejectedExecution(r, e);
if (r instanceof DefaultRunnable) {
DefaultRunnable defaultRunnable = (DefaultRunnable)r;
System.out.println("Runnable-" + defaultRunnable.getName() + " be discard.");
}
}
}
private static class DefaultThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
return thread;
}
}
}
复制代码
Sortie:
runableNum: 1
Runnable-id-1 run.
runableNum: 2
runableNum: 3
runableNum: 4
runableNum: 5
Runnable-id-5 run.
runableNum: 6
Runnable-id-6 run.
Runnable-id-2 run.
runableNum: 7
runableNum: 8
Runnable-id-8 be discard.
runableNum: 9
Runnable-id-9 be discard.
runableNum: 10
Runnable-id-10 be discard.
Runnable-id-3 run.
runableNum: 11
Runnable-id-4 run.
runableNum: 12
Runnable-id-7 run.
runableNum: 13
runableNum: 14
Runnable-id-14 be discard.
复制代码
3.2 ScheduledThreadPoolExecutor
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
复制代码
Vous pouvez emprunter sa capacité à retarder l'exécution des threads pour surveiller les fuites de connexion ou dépasser la durée de vie maximale.
4. Résumé
- Les classes liées à ThreadPoolExecutor et ScheduledThreadPoolExecutor sont les classes d'outils principales pour améliorer les performances d'exécution des threads et doivent être bien utilisées.
- Utilisez pleinement les outils de threads pour gérer les pools de ressources et organiser les threads de travail de manière raisonnable.
fin.
<-Merci pour le triple combo, comme et attention à gauche.
Lecture associée:
Code source HikariPool (1) Première connaissance du
code source HikariPool (2) Idées de conception empruntées au
code source HikariPool (3) Mise à l'échelle dynamique du pool de ressources
Code source HikariPool (4) Statut des ressources
Code source HikariPool (6) Quelques fonctionnalités JAVA utiles utilisées
Site geek Java: javageektour.com/