HikariPoolソースコード(5)作業スレッドと関連ツール

Java Geek | Author /   Kang Ran Yi Ye
これはJava Geekによる56番目のオリジナル記事です

1. HikariPoolでのワーカースレッドスケジューリング

HikariPoolの作業スレッドはThreadPoolExecutorを介してスケジュールされ、合計3つのThreadPoolExecutorインスタンスがあります。

ThreadPoolExecutor 責任 過負荷後の治療戦略
houseKeepingExecutorService 責任
1.データベース接続プールの動的スケーリングでデータベース接続を削減する
2.データベース接続リークを
監視する3.最大寿命超えてデータベース接続を監視する
放棄する
addConnectionExecutor データベース接続プールの動的なスケーリング時に新しいデータベース接続を追加するなど、データベース接続の作成を担当します。 放棄する
closeConnectionExecutor データベース接続を閉じる責任があります。 成功するまで実行を繰り返す

1.1。houseKeepingExecutorService

インスタンス化:

//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接続リークの監視

//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接続プールの動的スケーリング

//HikariPool.java
      // HouseKeeper是负载连接池动态伸缩的工作线程
      this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
复制代码

1.1.3データベース接続の最大存続期間の監視

         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

インスタンス化:

//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;
   }
复制代码

接続を追加:

//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

インスタンス化:

//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;
   }
复制代码

接続を閉じます。

   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.関連ツール

クラス 責任
ThreadPoolExecutor スレッドエグゼキュータ
BlockingQueue スレッドプールによって使用されるバッファキュー。キューの長さは、バッファリングできるワーカースレッドの最大数を決定します
ThreadFactory ワーカースレッドのスレッドファクトリを作成する
ScheduledThreadPoolExecutor スケジュールされたスケジューリングをサポートするスレッドプールエグゼキューターは、遅延実行と定期実行を指定できます。このようにして、遅延時間を最大寿命に設定して、データベース接続が最大寿命を超えているかどうかを監視できます。
DefaultThreadFactory HikariPoolに実装されているデフォルトのスレッドファクトリは、スレッド名を設定し、そのスレッドをスプライトスレッドとして設定します。
RejectedExecutionHandler スレッドエグゼキューターのスレッドキューがいっぱいになったときに新しいスレッドを追加するための処理戦略インターフェイス
DiscardOldestPolicy スレッドキュー内で最も古い未実行のワーカースレッドを破棄し、HikariPoolで使用されていない新しいワーカースレッドを追加します。
CallerRunsPolicy 成功するまで実行を繰り返し、closeConnectionExecutorで使用します。
AbortPolicy スレッドキューの負荷を超えるワーカースレッドを破棄し、例外をスローします。ひかりプールでは使用しません。
DiscardPolicy スレッドキューを超えて何もしない複雑なワーカースレッドを無視します。houseKeepingExecutorServiceおよびhouseKeepingExecutorServiceで使用されます。

3.コアクラス

3.1 ThreadPoolExecutor

コンストラクタ:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
复制代码

パラメータの説明:

パラメータ 解説
int corePoolSize スレッドプールエグゼキューターに保持するスレッドの最小数
int maximumPoolSize スレッドプールエグゼキューターが許可するスレッドの最大数
長いkeepAliveTime 未使用スレッドの保持時間。この時間を超え、スレッド数が最小スレッド数より大きい場合、スレッドは解放されます
TimeUnitで 未使用スレッド保持時間単位
BlockingQueue スレッドバッファキュー。このキューの効果はmaximumPoolSizeの影響を受け、スレッド数が十分な場合はキューに入れられません。
ThreadFactory ワーカースレッドを生成するためのスレッドファクトリインターフェイス
RejectedExecutionHandler ワーカースレッドの数がキャッシュキューに配置された後の処理戦略インターフェイスは、キャッシュキューの容量を超えています

ここで注意すべき点がいくつかあります。

  1. スレッドバッファーキューは、無限の増加によるメモリオーバーフローを回避するために、制限付きキューとして設定する必要があります。
  2. 上記と同じ理由で、スレッドの最大数もInteger.MAX_VALUEに設定しないように適切に制御する必要があります
  3. スレッドバッファーキューの処理ロジックは、corePoolSizeおよびmaximumPoolSizeの影響を受けます。簡単に言うと、十分な使用可能なスレッドがある場合、ワーカースレッドはスレッドバッファーキューに入れられません。

例:

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;
        }
    }
}
复制代码

出力:

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) {
复制代码

スレッドの実行を遅らせて接続リークを監視したり、最大寿命を超えたりする機能を借りることができます。

4.まとめ

  1. ThreadPoolExecutorおよびScheduledThreadPoolExecutor関連クラスは、スレッド実行パフォーマンスを向上させるためのコアツールクラスであり、適切に使用する必要があります。
  2. スレッドツールを最大限に活用してリソースプールを管理し、作業スレッドを合理的に配置します。

終わり。


<-左側のように注意を払って、トリプルコンボをありがとう。


関連
資料HikariPoolソースコード(1)
HikariPoolソースコード理解する(2)
HikariPoolソースコードから借用した設計アイデア(3)リソースプールの動的スケーリング
HikariPoolソースコード(4)リソースステータス
HikariPoolソースコード(6)いくつかの便利なJAVA機能


Javaオタクサイト: javageektour.com/

おすすめ

転載: juejin.im/post/5e9079b1e51d4546e347e191