티스토리 뷰

JVM/Spring

[Hikari CP] 光 살펴보기 - 3

글을 쓰는 개발자 2022. 4. 28. 08:46
반응형

HikariPool 생성자

      super(config);

      this.connectionBag = new ConcurrentBag<>(this);
      this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;

      this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();

super(this)

HikariPool은 PoolBase를 상속받아 구현한 클래스로서 기본 값을 설정합니다.

   PoolBase(final HikariConfig config)
   {
      this.config = config;

      this.networkTimeout = UNINITIALIZED;
      this.catalog = config.getCatalog();
      this.schema = config.getSchema();
      this.isReadOnly = config.isReadOnly();
      this.isAutoCommit = config.isAutoCommit();
      this.exceptionOverride = UtilityElf.createInstance(config.getExceptionOverrideClassName(), SQLExceptionOverride.class);
      this.transactionIsolation = UtilityElf.getTransactionIsolation(config.getTransactionIsolation());

      this.isQueryTimeoutSupported = UNINITIALIZED;
      this.isNetworkTimeoutSupported = UNINITIALIZED;
      this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
      this.isIsolateInternalQueries = config.isIsolateInternalQueries();

      this.poolName = config.getPoolName();
      this.connectionTimeout = config.getConnectionTimeout();
      this.validationTimeout = config.getValidationTimeout();
      this.lastConnectionFailure = new AtomicReference<>();

      initializeDataSource();
   }

suspendResumeLock

config.isAllowPoolSuspension()의 값은 따로 설정하지 않았다면 디폴트 값인 false입니다.
따로 설정 값을 주지 않는다면 SuspendResumeLock.FAUX_LOCK이 선택되며 별다른 구현체가 없이 껍데기만 존재하게 됩니다.

   public static final SuspendResumeLock FAUX_LOCK = new SuspendResumeLock(false) {
      @Override
      public void acquire() {}

      @Override
      public void release() {}

      @Override
      public void suspend() {}

      @Override
      public void resume() {}
   };

allowPoolSuspension 설정값을 true로 하지 않는다면 아무런 동작을 하지 않는 것과 동일하다.

houseKeepingExecutorService

      this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
   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();
      }
   }
   public static final class DefaultThreadFactory implements ThreadFactory {

      private final String threadName;
      private final boolean daemon;

      public DefaultThreadFactory(String threadName, boolean daemon) {
         this.threadName = threadName;
         this.daemon = daemon;
      }

      @Override
      public Thread newThread(Runnable r) {
         Thread thread = new Thread(r, threadName);
         thread.setDaemon(daemon);
         return thread;
      }
   }

daemon

데몬 스레드는 주 스레드의 작업을 돕는 보조적 역할을 담당하는 쓰레드로서 주 스레드가 종료했을 때 데몬 스레드도 같이 종료한다.

ScheduledThreadPoolExecutor

어떤 작업을 일정 시간 지연 후에 수행하거나, 일정 시간 간격으로 주기적으로 실행해야할 때 사용

HikariCP에서는 scheduleWithFixedDelay 메소드를 사용하며, 작업이 완료되면 일정 시간 뒤에 다시 실행시키는 작업을 진행합니다.


      checkFailFast();

      if (config.getMetricsTrackerFactory() != null) {
         setMetricsTrackerFactory(config.getMetricsTrackerFactory());
      }
      else {
         setMetricRegistry(config.getMetricRegistry());
      }

      setHealthCheckRegistry(config.getHealthCheckRegistry());

      handleMBeans(this, true);

      ThreadFactory threadFactory = config.getThreadFactory();

checkFailFast

설정값 확인

      final long initializationTimeout = config.getInitializationFailTimeout();
      if (initializationTimeout < 0) {
         return;
      }

createPoolEntry

해당 메서드에서 maxLifetime 과 keepaliveTime 값에 따라 ScheduledThreadPoolExecutor의 값들을 poolEntry에 쌓는 작업을 진행합니다.

      final long startTime = currentTime();
      do {
         final PoolEntry poolEntry = createPoolEntry();
         if (poolEntry != null) {
            if (config.getMinimumIdle() > 0) {
               connectionBag.add(poolEntry);
               logger.debug("{} - Added connection {}", poolName, poolEntry.connection);
            }
            else {
               quietlyCloseConnection(poolEntry.close(), "(initialization check complete and minimumIdle is zero)");
            }

            return;
         }

         if (getLastConnectionFailure() instanceof ConnectionSetupException) {
            throwPoolInitializationException(getLastConnectionFailure().getCause());
         }

         quietlySleep(SECONDS.toMillis(1));
      } while (elapsedMillis(startTime) < initializationTimeout);

connectionBag.add를 통해 해당 poolEntry를 넣는 작업을 진행합니다. 초기시간이 될 때까지..

개인적인 생각으로 메소드명은 validation만 할 것 같았는데 poolEntry를 connectionBag에 넣는 작업을 할 줄은 몰랐다..


생성자 중간 부분

      handleMBeans(this, true);

      ThreadFactory threadFactory = config.getThreadFactory();

      final int maxPoolSize = config.getMaximumPoolSize();
      LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize);
      this.addConnectionQueueReadOnlyView = unmodifiableCollection(addConnectionQueue);
      this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
      this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize, poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

      this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);

      this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);

handleMBeans(this, true)

MBean을 등록하는 메서드

JMX

Java Management Extension

MBean: Manged Bean => 플랫폼 혹은 사용자에 의해 만들어진 모니터링용 객체

Instrumentation 계층 - 관리 Bean(MBean)이라고 하는 특수 Java Bean 내에서 자원을 랩핑할 수 있는 방법을 지시합니다.

agent 계층 - 관리 인프라 구조를 제공하는 에이전트 및 MBean 서버로 구성됩니다. 구현된 서비스는 다음과 같습니다.

  • 모니터링
  • 이벤트 알림
  • 타이머
    management 계층 - 외부 관리 애플리케이션이 프로토콜, API 등의 관점에서 기본적인 계층과 상호작용할 수 있는 방법을 정의합니다. 이 계층은 J2EE(Java 2 Platform, Enterprise Edition) 스펙의 일부인 분산 서비스 스펙(JSR-077)의 구현을 사용합니다.

이미지 참고: https://www.ibm.com/docs/ko/was-zos/9.0.5?topic=scripting-using-wsadmin-java-management-extensions-jmx

ThreadFactory

내용참고: ThreadFactory

  • 스레드에 대해서 의미있는 이름을 부여할 수 있다. 즉 어떤 목적을 가지고 작업을 하는 것에 대해서 파악하기 쉽다.
  • 스레드 수 및 기타 세부 정보와 같이 생성된 스레드에 대한 통계를 얻을 수 있습니다. 통계를 기반으로 새 스레드 생성을 제한할 수 있습니다.
  • 스레드의 데몬 상태를 설정할 수 있습니다.
  • 스레드 우선 순위를 설정할 수 있습니다.
  • 모든 기능을 하나의 클래스로 제한할 수 있습니다.

LinkedBlockingQueue

일반 큐와 다른 점이 있다면 다음과 같다.

    private final ReentrantLock takeLock = new ReentrantLock();

    private final Condition notEmpty = takeLock.newCondition();

    private final ReentrantLock putLock = new ReentrantLock();

    private final Condition notFull = putLock.newCondition();

ReentrantLock 은 기본적으로 synchronized와 같이 동기화 목적으로 사용되는 lock으로서 동일한 성격을 가지고 있다.
차이점이 있다면 특정 자원에 대해서 관리할 수 있다는 점이다.

나머지 부분은 중간에 한 번 설명한 적이 있는 것 같아 넘어가도록 하겠습니다.

다음번에 HikariCP getConnection()에 대해서 알아보도록 하겠습니다. 그럼 20000

반응형

'JVM > Spring' 카테고리의 다른 글

[Spring] Scope (Prototype & Singleton)  (2) 2022.09.13
[Hikari CP] 光 살펴보기 - 4  (0) 2022.04.29
[Hikari CP] 光 살펴보기 - 2  (0) 2022.04.27
[Hikari CP] 光 살펴보기 - 1  (0) 2022.04.26
[Spring] AOP  (0) 2022.04.05
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함