国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

這篇具有很好參考價值的文章主要介紹了SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

?引言

1、問題描述

2、SpringBoot默認(rèn)的數(shù)據(jù)庫連接池

3、HikariCP是什么

4、測試依賴

5、配置文件

5.1、數(shù)據(jù)庫連接參數(shù)

5.2、連接池數(shù)據(jù)基本參數(shù)

5.3、連接檢查參數(shù)

5.4、事務(wù)相關(guān)參數(shù)

5.5、JMX參數(shù)

6、HikariCP源碼淺析

6.1、HikariConfig--連接池配置的加載

6.2、HikariPool--連接池

1、HikariPool UML圖

2、PoolBase

3、HikariPool

4、如何獲取一個鏈接對象

6.3、ConcurrentBag--更少的鎖沖突

7、HikariCP為什么快?

7.1、通過代碼設(shè)計和優(yōu)化大幅減少線程間的鎖競爭

7.2、引入了更多 JDK 的特性

7.3、使用 javassist 直接修改 class 文件生成動態(tài)代理

8、JDK 、CGLib 、ASM 、Javassist 性能測試

1、測試代碼

2、測試結(jié)果


SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

?引言

????????咱們開發(fā)項目的過程中用到很多的開源數(shù)據(jù)庫鏈接池,比如druid、c3p0、BoneCP等等,前端時間在部署新服務(wù)的時候發(fā)現(xiàn)了個問題,排查完畢問題正好學(xué)習(xí)學(xué)習(xí)SpringBoot的默認(rèn)的數(shù)據(jù)庫連接池HikariCP的一些知識。HikariCP官網(wǎng)地址:?https://github.com/brettwooldridge/HikariCP

1、問題描述

? ? ? ? 我們新項目部署上線之后在觀察日志的時候發(fā)現(xiàn)了這個警告,經(jīng)過排查是發(fā)現(xiàn)DB方面的問題,保留現(xiàn)場如下。

SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

2、SpringBoot默認(rèn)的數(shù)據(jù)庫連接池

????????Spring-Boot-2.0.0-M1版本將默認(rèn)的數(shù)據(jù)庫連接池從tomcat jdbc pool改為了HikariCP。

SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

3、HikariCP是什么

????????HikariCP 是用于創(chuàng)建和管理連接,利用“池”的方式復(fù)用連接減少資源開銷,和其他數(shù)據(jù)源一樣,也具有連接數(shù)控制、連接可靠性測試、連接泄露控制、緩存語句等功能,另外,和 druid 一樣,HikariCP 也支持監(jiān)控功能。

????????HikariCP 是目前最快的連接池,就連風(fēng)靡一時的 BoneCP 也停止維護,主動讓位給它,SpringBoot 也把它設(shè)置為默認(rèn)連接池。

4、測試依賴

? ? ? ? 既然官網(wǎng)說HikariCP是最快的數(shù)據(jù)庫連接池,不妨我們進行一些嘗試,驗證一下官網(wǎng)放出的狠話。驗證也比較簡單,只需要在項目中添加依賴即可。

 <!-- JNDI數(shù)據(jù)源 -->
    <resource-ref>
        <res-ref-name>jdbc/hikariCP-test</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

5、配置文件

? ? ? ? 上面一步添加完依賴,接下來具體實操之前先了解一下HikariCP的各種配置信息。

5.1、數(shù)據(jù)庫連接參數(shù)

????????注意,這里url在后面拼接了多個參數(shù)用于避免亂碼、時區(qū)報錯問題。

#-------------基本屬性--------------------------------
jdbcUrl=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
username=root
password=root
#JDBC驅(qū)動使用的Driver實現(xiàn)類類名
#默認(rèn)為空。會根據(jù)jdbcUrl來解析
driverClassName=com.mysql.cj.jdbc.Driver

5.2、連接池數(shù)據(jù)基本參數(shù)

#-------------連接池大小相關(guān)參數(shù)--------------------------------
#最大連接池數(shù)量
#默認(rèn)為10??赏ㄟ^JMX動態(tài)修改
maximumPoolSize=10

#最小空閑連接數(shù)量
#默認(rèn)與maximumPoolSize一致??赏ㄟ^JMX動態(tài)修改
minimumIdle=0

5.3、連接檢查參數(shù)

????????注意:針對連接失效的問題,HikariCP 強制開啟借出測試和空閑測試,不開啟回收測試,可選的只有泄露測試。所有的超時時間都可以根據(jù)JMX設(shè)置。

#-------------連接檢測情況--------------------------------
#用來檢測連接是否有效的sql,要求是一個查詢語句,常用select 'x'
#如果驅(qū)動支持JDBC4,建議不設(shè)置,因為這時默認(rèn)會調(diào)用Connection.isValid()方法來檢測,該方式效率會更高
#默認(rèn)為空
connectionTestQuery=select 1 from dual

#檢測連接是否有效的超時時間,單位毫秒
#最小允許值250 ms
#默認(rèn)5000 ms。
validationTimeout=5000

#連接保持空閑而不被驅(qū)逐的最小時間。單位毫秒。
#該配置只有再minimumIdle < maximumPoolSize才會生效,最小允許值為10000 ms。
#默認(rèn)值10000*60 = 10分鐘。
idleTimeout=600000

#連接對象允許“泄露”的最大時間。單位毫秒
#最小允許值為2000 ms。
#默認(rèn)0,表示不開啟泄露檢測。
leakDetectionThreshold=0

#連接最大存活時間。單位毫秒
#最小允許值30000 ms
#默認(rèn)30分鐘??赏ㄟ^JMX動態(tài)修改
maxLifetime=1800000

#獲取連接時最大等待時間,單位毫秒
#獲取時間超過該配置,將拋出異常。最小允許值250 ms
#默認(rèn)30000 ms。
connectionTimeout=300000

5.4、事務(wù)相關(guān)參數(shù)

#-------------事務(wù)相關(guān)的屬性--------------------------------
#當(dāng)連接返回池中時是否設(shè)置自動提交
#默認(rèn)為true
autoCommit=true

#當(dāng)連接從池中取出時是否設(shè)置為只讀
#默認(rèn)值false
readOnly=false

#連接池創(chuàng)建的連接的默認(rèn)的TransactionIsolation狀態(tài)
#可用值為下列之一:NONE,TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE
#默認(rèn)值為空,由驅(qū)動決定
transactionIsolation=TRANSACTION_REPEATABLE_READ

5.5、JMX參數(shù)

#-------------JMX--------------------------------

#是否允許通過JMX掛起和恢復(fù)連接池
#默認(rèn)為false
allowPoolSuspension=false

#是否開啟JMX
#默認(rèn)false
registerMbeans=true

#數(shù)據(jù)源名。
#默認(rèn)自動生成
poolName=

6、HikariCP源碼淺析

6.1、HikariConfig--連接池配置的加載

????????在HikariCP 中,HikariConfig用于加載配置,它的加載要更加簡潔。直接從PropertyElf.setTargetFromProperties(Object, Properties)方法開始看。

// 這個方法就是將properties的參數(shù)設(shè)置到HikariConfig中
public static void setTargetFromProperties(final Object target, final Properties properties)
{
   if (target == null || properties == null) {
      return;
   }

   // 在這里會利用反射獲取
   List<Method> methods = Arrays.asList(target.getClass().getMethods());
   // 遍歷
   properties.forEach((key, value) -> {
      if (target instanceof HikariConfig && key.toString().startsWith("dataSource.")) {
         // 如果是dataSource.*的參數(shù),直接加入到dataSourceProperties屬性
         ((HikariConfig) target).addDataSourceProperty(key.toString().substring("dataSource.".length()), value);
      }
      else {
         // 如果不是,則通過set方法設(shè)置
         setProperty(target, key.toString(), value, methods);
      }
   });
}
private static void setProperty(final Object target, final String propName, final Object propValue, final List<Method> methods)
{
   final Logger logger = LoggerFactory.getLogger(PropertyElf.class);

   // use the english locale to avoid the infamous turkish locale bug
   // 拼接參數(shù)的setter方法名 首字母大寫
   String methodName = "set" + propName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propName.substring(1);
   // 獲取對應(yīng)的Method 對象
   Method writeMethod = methods.stream().filter(m -> m.getName().equals(methodName) && m.getParameterCount() == 1).findFirst().orElse(null);

   // 如果不存在,按另一套規(guī)則拼接參數(shù)的setter方法名 全部大寫
   if (writeMethod == null) {
      String methodName2 = "set" + propName.toUpperCase(Locale.ENGLISH);
      writeMethod = methods.stream().filter(m -> m.getName().equals(methodName2) && m.getParameterCount() == 1).findFirst().orElse(null);
   }

   // 如果該參數(shù)setter方法不存在,則拋出異常,從這里可以看出,HikariCP 中不能存在配錯參數(shù)名的情況
   if (writeMethod == null) {
      logger.error("Property {} does not exist on target {}", propName, target.getClass());
      throw new RuntimeException(String.format("Property %s does not exist on target %s", propName, target.getClass()));
   }


   // 調(diào)用setter方法來配置具體參數(shù)。
   try {
      Class<?> paramClass = writeMethod.getParameterTypes()[0];
      if (paramClass == int.class) {
         writeMethod.invoke(target, Integer.parseInt(propValue.toString()));
      }
      else if (paramClass == long.class) {
         writeMethod.invoke(target, Long.parseLong(propValue.toString()));
      }
      else if (paramClass == boolean.class || paramClass == Boolean.class) {
         writeMethod.invoke(target, Boolean.parseBoolean(propValue.toString()));
      }
      else if (paramClass == String.class) {
         writeMethod.invoke(target, propValue.toString());
      }
      else {
         try {
            logger.debug("Try to create a new instance of \"{}\"", propValue.toString());
            writeMethod.invoke(target, Class.forName(propValue.toString()).newInstance());
         }
         catch (InstantiationException | ClassNotFoundException e) {
            logger.debug("Class \"{}\" not found or could not instantiate it (Default constructor)", propValue.toString());
            writeMethod.invoke(target, propValue);
         }
      }
   }
   catch (Exception e) {
      logger.error("Failed to set property {} on target {}", propName, target.getClass(), e);
      throw new RuntimeException(e);
   }
}

6.2、HikariPool--連接池

????????HikariPool 是一個非常重要的類,它負(fù)責(zé)管理連接。

1、HikariPool UML圖

SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

HikariPoolMXBean:采用JMX控制HikariPool的入口。

/**
 * The javax.management MBean for a Hikari pool instance.
 *
 * @author Brett Wooldridge
 */
public interface HikariPoolMXBean

2、PoolBase

????????HikariPool鏈接池的配置信息。

3、HikariPool

????????連接池的管理。

屬性:

//配置信息。
public final HikariConfig config;
//指標(biāo)記錄器包裝類。HikariCP支持Metrics監(jiān)控
IMetricsTrackerDelegate metricsTracker;
//創(chuàng)建新連接的任務(wù),Callable實現(xiàn)類。一般調(diào)用一次創(chuàng)建一個連接
private final PoolEntryCreator poolEntryCreator = new PoolEntryCreator(null /*logging prefix*/);
//創(chuàng)建新連接的任務(wù),Callable實現(xiàn)類。一般調(diào)用一次創(chuàng)建一個連接,與前者區(qū)別在于它創(chuàng)建最后一個連接,會打印日志
private final PoolEntryCreator postFillPoolEntryCreator = new PoolEntryCreator("After adding ");
private final Collection<Runnable> addConnectionQueueReadOnlyView;
//執(zhí)行PoolEntryCreator任務(wù)的線程池。以addConnectionQueueReadOnlyView作為等待隊列
private final ThreadPoolExecutor addConnectionExecutor;
//執(zhí)行關(guān)閉連接的線程池
private final ThreadPoolExecutor closeConnectionExecutor;
//用于執(zhí)行HouseKeeper(連接檢測任務(wù)和維持連接池大?。┑热蝿?wù)
private final ScheduledExecutorService houseKeepingExecutorService;
//存放連接對象的包。用于borrow、requite、add和remove對象。
private final ConcurrentBag<PoolEntry> connectionBag;

4、如何獲取一個鏈接對象

/**
 * Get a connection from the pool, or timeout after the specified number of milliseconds.
 *
 * @param hardTimeout the maximum time to wait for a connection from the pool
 * @return a java.sql.Connection instance
 * @throws SQLException thrown if a timeout occurs trying to obtain a connection
 */
public Connection getConnection(final long hardTimeout) throws SQLException
{
   // 如果我們設(shè)置了allowPoolSuspension為true,則這個鎖會生效,這個是基于信號量的鎖 MAX_PERMITS = 10000,正常情況不會用完,除非你掛起了連接池(通過JMX等方式),10000個permits會被消耗完
   suspendResumeLock.acquire();
   final long startTime = currentTime();

   try {
      // 剩余超時時間
      long timeout = hardTimeout;
      // 循環(huán)獲取,除非獲取到了連接或者超時
      do {
         // 從ConcurrentBag中拿出一個元素
         PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
         // 前面說過,只有超時情況才會返回空,這時會跳出循環(huán)并拋出異常
         if (poolEntry == null) {
            break; // We timed out... break and throw exception
         }

         final long now = currentTime();
         // 如果
         // 1、元素被標(biāo)記為丟棄
         // 2、空閑時間過長
         // 3、連接無效則會丟棄該元素
         // 1&2&3 --> 4、并關(guān)閉連接
         if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
            closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
            timeout = hardTimeout - elapsedMillis(startTime);
         }
         else {
            metricsTracker.recordBorrowStats(poolEntry, startTime);
            // 創(chuàng)建Connection代理類,該代理類就是使用Javassist生成的
            return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
         }
      } while (timeout > 0L);

      metricsTracker.recordBorrowTimeoutStats(startTime);

      // 超時拋出異常
      throw createTimeoutException(startTime);
   }
   catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
   }
   finally {
      // 釋放一個permit
      suspendResumeLock.release();
   }
}

6.3、ConcurrentBag--更少的鎖沖突

????????在 HikariCP 中ConcurrentBag用于存放PoolEntry對象(封裝了Connection對象,IConcurrentBagEntry實現(xiàn)類),本質(zhì)上可以將它就是一個資源池。

SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP

?屬性:

//存放著當(dāng)前線程返還的PoolEntry對象。如果當(dāng)前線程再次借用資源,會先從這個列表中獲取。注意,這個列表的元素可以被其他線程“偷走”
private final ThreadLocal<List<Object>> threadList;
//添加元素的監(jiān)聽器,由HikariPool實現(xiàn),在該實現(xiàn)中,如果waiting - addConnectionQueue.size() >= 0,則會讓addConnectionExecutor執(zhí)行PoolEntryCreator任務(wù)
private final IBagStateListener listener;
//當(dāng)前等待獲取鏈接的線程數(shù)
private final AtomicInteger waiters;
//元素是否使用弱引用
private final boolean weakThreadLocals;
//這是一個無容量的阻塞隊列,每個插入操作需要阻塞等待刪除操作,而刪除操作不需要等待,如果沒有元素插入,會返回null,如果設(shè)置了超時時間則需要等待。
private final SynchronousQueue<T> handoffQueue;
//存放著狀態(tài)為使用中、未使用和保留三種狀態(tài)的PoolEntry對象。注意,CopyOnWriteArrayList是一個線程安全的集合,在每次寫操作時都會采用復(fù)制數(shù)組的方式來增刪元素,讀和寫使用的是不同的數(shù)組,避免了鎖競爭
private final CopyOnWriteArrayList<T> sharedList;

方法:

????????在以下方法中,唯一可能出現(xiàn)線程切換到就是handoffQueue.poll(timeout, NANOSECONDS)。

/**
 * The method will borrow a BagEntry from the bag, blocking for the
 * specified timeout if none are available.
 *
 * @param timeout how long to wait before giving up, in units of unit
 * @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter
 * @return a borrowed instance from the bag or null if a timeout occurs
 * @throws InterruptedException if interrupted while waiting
 */
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
   // 1. 首先從threadList獲取對象

   // Try the thread-local list first
   // 獲取綁定在當(dāng)前線程的List<Object>對象,注意這個集合的實現(xiàn)一般為FastList,這是HikariCP自己實現(xiàn)的
   final List<Object> list = threadList.get();
   for (int i = list.size() - 1; i >= 0; i--) {
      // 獲取當(dāng)前元素,并將它從集合中刪除
      final Object entry = list.remove(i);
      @SuppressWarnings("unchecked")
      // 如果設(shè)置了weakThreadLocals,則存放的是WeakReference對象
      final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
      // 采用CAS方式將獲取的對象狀態(tài)由未使用改為使用中,如果失敗說明其他線程正在使用它。
      if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
         return bagEntry;
      }
   }

   // 2.如果還沒獲取到,會從sharedList中獲取對象

   // Otherwise, scan the shared list ... then poll the handoff queue
   // 等待獲取連接的線程數(shù)+1
   final int waiting = waiters.incrementAndGet();
   try {
      // 遍歷sharedList
      for (T bagEntry : sharedList) {
         // 采用CAS方式將獲取的對象狀態(tài)由未使用改為使用中,如果當(dāng)前元素正在使用,則無法修改成功,進入下一循環(huán)
         if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            // If we may have stolen another waiter's connection, request another bag add.
            if (waiting > 1) {
               // 通知監(jiān)聽器添加包元素。如果waiting - addConnectionQueue.size() >= 0,則會讓addConnectionExecutor執(zhí)行PoolEntryCreator任務(wù)
               listener.addBagItem(waiting - 1);
            }
            return bagEntry;
         }
      }

      // 通知監(jiān)聽器添加包元素
      listener.addBagItem(waiting);

      // 3.如果還沒獲取到,會輪訓(xùn)進入handoffQueue隊列獲取連接對象

      timeout = timeUnit.toNanos(timeout);
      do {
         final long start = currentTime();
         // 從handoffQueue隊列中獲取并刪除元素。這是一個無容量的阻塞隊列,插入操作需要阻塞等待刪除操作,而刪除操作不需要等待,如果沒有元素插入,會返回null,如果設(shè)置了超時時間則需要等待
         final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
         // 這里會出現(xiàn)三種情況,
         // 1.超時,返回null
         // 2.獲取到元素,但狀態(tài)為正在使用,繼續(xù)執(zhí)行
         // 3.獲取到元素,元素狀態(tài)未未使用,修改未使用并返回
         if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            return bagEntry;
         }

         timeout -= elapsedNanos(start);
      } while (timeout > 10_000);

      // 超時返回null
      return null;
   }
   finally {
      // 等待獲取連接的線程數(shù)-1
      waiters.decrementAndGet();
   }
}

7、HikariCP為什么快?

7.1、通過代碼設(shè)計和優(yōu)化大幅減少線程間的鎖競爭

????????1、元素狀態(tài)的引入,以及使用CAS方法修改狀態(tài)。在ConcurrentBag中,使用使用中、未使用、刪除和保留等表示元素的狀態(tài),而不是使用不同的集合來維護不同狀態(tài)的元素。元素狀態(tài)這一概念的引入非常關(guān)鍵,為后面的幾點提供了基礎(chǔ)。 ConcurrentBag的方法中多處調(diào)用 CAS 方法來判斷和修改元素狀態(tài),這一過程不需要加鎖。

????????2、threadList 的使用。當(dāng)前線程歸還的元素會被綁定到ThreadLocal,該線程再次獲取元素時,在該元素未被偷走的前提下可直接獲取到,不需要去 sharedList 遍歷獲??;

7.2、引入了更多 JDK 的特性

????????尤其是 concurrent 包的工具。相比較于DBCP、C3P0等數(shù)據(jù)庫鏈接池問世較晚,很方便的享受JDK的升級帶來的方便。

????????1、采用CopyOnWriteArrayList來存放元素。在CopyOnWriteArrayList中,讀和寫使用的是不同的數(shù)組,避免了兩者的鎖競爭,至于多個線程寫入,則會加 ReentrantLock 鎖。

????????2、sharedList 的讀寫控制。borrow 和 requite 對 sharedList 來說都是不加鎖的,缺點就是會犧牲一致性。用戶線程無法進行增加元素的操作,只有 addConnectionExecutor 可以,而 addConnectionExecutor 只會開啟一個線程執(zhí)行任務(wù),所以 add 操作不會存在鎖競爭。至于 remove 是唯一會造成鎖競爭的方法,這一點我認(rèn)為也可以參照 addConnectionExecutor 來處理,在加入任務(wù)隊列前把 PoolEntry 的狀態(tài)標(biāo)記為刪除中。

7.3、使用 javassist 直接修改 class 文件生成動態(tài)代理

? ? ? ? 1、使用 javassist 直接修改 class 文件生成動態(tài)代理,精簡了很多不必要的字節(jié)碼,提高代理方法運行速度。尤其JDK1.8優(yōu)化以后JDK的動態(tài)代理,CGlib代理已經(jīng)和javassist、asm等一個數(shù)量級。

8、JDK 、CGLib 、ASM 、Javassist 性能測試

????????環(huán)境:JDK 1.8,CGLib 3.3.0, ASM JDK自帶的ASM包,Javassist 3.26.0-GA。

????????數(shù)據(jù)為執(zhí)行三次,每次調(diào)用5千萬次代理方法的結(jié)果。

1、測試代碼

package cn.zzs.proxy;

import javassist.*;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.DecimalFormat;

/**
 * @author lly
 **/
public class App {
    public static void main(String[] args) throws Exception {
        CountService delegate = new CountServiceImpl();
        long time = System.currentTimeMillis();
        CountService jdkProxy = createJdkDynamicProxy(delegate);
        time = System.currentTimeMillis() - time;
        System.out.println("Create JDK Proxy: " + time + " ms");

        time = System.currentTimeMillis();
        CountService cglibProxy = createCglibDynamicProxy(delegate);
        time = System.currentTimeMillis() - time;
        System.out.println("Create CGLIB Proxy: " + time + " ms");

        time = System.currentTimeMillis();
        CountService javassistProxy = createJavassistDynamicProxy(delegate);
        time = System.currentTimeMillis() - time;
        System.out.println("Create JAVAASSIST Proxy: " + time + " ms");

        time = System.currentTimeMillis();
        CountService javassistBytecodeProxy = createJavassistBytecodeDynamicProxy(delegate);
        time = System.currentTimeMillis() - time;
        System.out.println("Create JAVAASSIST Bytecode Proxy: " + time + " ms");

        time = System.currentTimeMillis();
        CountService asmBytecodeProxy = createAsmBytecodeDynamicProxy(delegate);
        time = System.currentTimeMillis() - time;
        System.out.println("Create ASM Proxy: " + time + " ms");
        System.out.println("================");

        for (int i = 0; i < 3; i++) {
            test(jdkProxy, "Run JDK Proxy: ");
            test(cglibProxy, "Run CGLIB Proxy: ");
            test(javassistProxy, "Run JAVAASSIST Proxy: ");
            test(javassistBytecodeProxy, "Run JAVAASSIST Bytecode Proxy: ");
            test(asmBytecodeProxy, "Run ASM Bytecode Proxy: ");
            System.out.println("----------------");
        }

    }

    private static void test(CountService service, String label)
            throws Exception {
        service.count(); // warm up
        int count = 50000000;
        long time = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            service.count();
        }
        time = System.currentTimeMillis() - time;
        System.out.println(label + time + " ms, " + new DecimalFormat().format(count / time * 1000) + " t/s");
    }

    private static CountService createJdkDynamicProxy(final CountService delegate) {
        CountService jdkProxy = (CountService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                new Class[]{CountService.class}, new JdkHandler(delegate));
        return jdkProxy;
    }

    private static class JdkHandler implements InvocationHandler {

        final Object delegate;

        JdkHandler(Object delegate) {
            this.delegate = delegate;
        }

        public Object invoke(Object object, Method method, Object[] objects)
                throws Throwable {
            return method.invoke(delegate, objects);
        }
    }

    private static CountService createCglibDynamicProxy(final CountService delegate) throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(new CglibInterceptor(delegate));
        enhancer.setInterfaces(new Class[]{CountService.class});
        CountService cglibProxy = (CountService) enhancer.create();
        return cglibProxy;
    }

    private static class CglibInterceptor implements MethodInterceptor {

        final Object delegate;

        CglibInterceptor(Object delegate) {
            this.delegate = delegate;
        }

        public Object intercept(Object object, Method method, Object[] objects,
                                MethodProxy methodProxy) throws Throwable {
            return methodProxy.invoke(delegate, objects);
        }
    }

    private static CountService createJavassistDynamicProxy(final CountService delegate) throws Exception {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(new Class[]{CountService.class});
        Class<?> proxyClass = proxyFactory.createClass();
        CountService javassistProxy = (CountService) proxyClass.newInstance();
        ((ProxyObject) javassistProxy).setHandler(new JavaAssitInterceptor(delegate));
        return javassistProxy;
    }

    private static class JavaAssitInterceptor implements MethodHandler {

        final Object delegate;

        JavaAssitInterceptor(Object delegate) {
            this.delegate = delegate;
        }

        public Object invoke(Object self, Method m, Method proceed,
                             Object[] args) throws Throwable {
            return m.invoke(delegate, args);
        }
    }

    private static CountService createJavassistBytecodeDynamicProxy(CountService delegate) throws Exception {
        ClassPool mPool = new ClassPool(true);
        CtClass mCtc = mPool.makeClass(CountService.class.getName() + "JavaassistProxy");
        mCtc.addInterface(mPool.get(CountService.class.getName()));
        mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
        mCtc.addField(CtField.make("public " + CountService.class.getName() + " delegate;", mCtc));
        mCtc.addMethod(CtNewMethod.make("public int count() { return delegate.count(); }", mCtc));
        Class<?> pc = mCtc.toClass();
        CountService bytecodeProxy = (CountService) pc.newInstance();
        Field filed = bytecodeProxy.getClass().getField("delegate");
        filed.set(bytecodeProxy, delegate);
        return bytecodeProxy;
    }

    private static CountService createAsmBytecodeDynamicProxy(CountService delegate) throws Exception {
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String className = CountService.class.getName() + "AsmProxy";
        String classPath = className.replace('.', '/');
        String interfacePath = CountService.class.getName().replace('.', '/');
        classWriter.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, classPath, null, "java/lang/Object", new String[]{interfacePath});

        MethodVisitor initVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        initVisitor.visitCode();
        initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        initVisitor.visitInsn(Opcodes.RETURN);
        initVisitor.visitMaxs(0, 0);
        initVisitor.visitEnd();

        FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC, "delegate", "L" + interfacePath + ";", null, null);
        fieldVisitor.visitEnd();

        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "count", "()I", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitFieldInsn(Opcodes.GETFIELD, classPath, "delegate", "L" + interfacePath + ";");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, interfacePath, "count", "()I");
        methodVisitor.visitInsn(Opcodes.IRETURN);
        methodVisitor.visitMaxs(0, 0);
        methodVisitor.visitEnd();

        classWriter.visitEnd();
        byte[] code = classWriter.toByteArray();
        CountService bytecodeProxy = (CountService) new ByteArrayClassLoader().getClass(className, code).newInstance();
        Field filed = bytecodeProxy.getClass().getField("delegate");
        filed.set(bytecodeProxy, delegate);
        return bytecodeProxy;
    }

    private static class ByteArrayClassLoader extends ClassLoader {

        public ByteArrayClassLoader() {
            super(ByteArrayClassLoader.class.getClassLoader());
        }

        public synchronized Class<?> getClass(String name, byte[] code) {
            if (name == null) {
                throw new IllegalArgumentException("");
            }
            return defineClass(name, code, 0, code.length);
        }

    }

}

2、測試結(jié)果

Create JDK Proxy: 9 ms
Create CGLIB Proxy: 149 ms
Create JAVAASSIST Proxy: 115 ms
Create JAVAASSIST Bytecode Proxy: 58 ms
Create ASM Proxy: 1 ms
================
Run JDK Proxy: 479 ms, 104,384,000 t/s
Run CGLIB Proxy: 541 ms, 92,421,000 t/s
Run JAVAASSIST Proxy: 754 ms, 66,312,000 t/s
Run JAVAASSIST Bytecode Proxy: 194 ms, 257,731,000 t/s
Run ASM Bytecode Proxy: 202 ms, 247,524,000 t/s
----------------
Run JDK Proxy: 404 ms, 123,762,000 t/s
Run CGLIB Proxy: 325 ms, 153,846,000 t/s
Run JAVAASSIST Proxy: 681 ms, 73,421,000 t/s
Run JAVAASSIST Bytecode Proxy: 179 ms, 279,329,000 t/s
Run ASM Bytecode Proxy: 180 ms, 277,777,000 t/s
----------------
Run JDK Proxy: 381 ms, 131,233,000 t/s
Run CGLIB Proxy: 339 ms, 147,492,000 t/s
Run JAVAASSIST Proxy: 674 ms, 74,183,000 t/s
Run JAVAASSIST Bytecode Proxy: 179 ms, 279,329,000 t/s
Run ASM Bytecode Proxy: 181 ms, 276,243,000 t/s
----------------

資料:

動態(tài)代理方案性能對比 - 梁飛的博客 - ITeye博客

GitHub - wwadge/bonecp: BoneCP is a Java JDBC connection pool implementation that is tuned for high performance by minimizing lock contention to give greater throughput for your applications. It beats older connection pools such as C3P0 and DBCP but SHOULD NOW BE CONSIDERED DEPRECATED in favour of HikariCP.

GitHub - brettwooldridge/HikariCP: 光 HikariCP?A solid, high-performance, JDBC connection pool at last.

JDK動態(tài)代理與CGLib動態(tài)代理相關(guān)問題_程序員面試經(jīng)驗分享的博客-CSDN博客

02Hikari源碼解析之ConcurrentBag、FastList分析_concurrentbag解析_一直打鐵的博客-CSDN博客

數(shù)據(jù)庫連接池性能比對(hikari druid c3p0 dbcp jdbc)_c3p0和hikari那個好_把酒問天的博客-CSDN博客

https://www.cnblogs.com/flyingeagle/articles/7102282.html

使用Javassist來動態(tài)創(chuàng)建,修改和代理類 - 算法之名的個人空間 - OSCHINA - 中文開源技術(shù)交流社區(qū)文章來源地址http://www.zghlxwxcb.cn/news/detail-499024.html

到了這里,關(guān)于SpringBoot 默認(rèn)數(shù)據(jù)庫連接池 HikariCP的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • springboot 數(shù)據(jù)庫連接池配置(hikari)

    JBDC ? ? ? ? JABC是JAVA訪問關(guān)系型數(shù)據(jù)庫的標(biāo)注API,它為各種關(guān)系型數(shù)據(jù)的訪問提供統(tǒng)一的接口標(biāo)準(zhǔn),然后,各個關(guān)系型數(shù)據(jù)庫廠商按照J(rèn)BDC的標(biāo)準(zhǔn),提供能使JAVA訪問的驅(qū)動包。一般情況下,在JAVA中執(zhí)行一條SQL語句,需要以下幾個步驟: 狀態(tài)JDBC驅(qū)動程序 建立數(shù)據(jù)庫連接 創(chuàng)建

    2024年02月09日
    瀏覽(24)
  • Springboot整合Durid-數(shù)據(jù)庫連接池

    一、Springboot使用Durid快速開始 1、 添加依賴 2、添加配置項 (1) durid連接池配置

    2024年02月11日
    瀏覽(23)
  • SpringBoot 是怎樣連接 MySql 數(shù)據(jù)庫的

    SpringBoot 是怎樣連接 MySql 數(shù)據(jù)庫的

    Spring Boot 是一款流行的 Java 開發(fā)框架,它可以輕松地連接各種類型的數(shù)據(jù)庫,包括關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫。本文將介紹 Spring Boot 是如何連接數(shù)據(jù)庫的,包括其原理和代碼示例。 Spring Boot 通過使用 Spring Data JPA 來連接數(shù)據(jù)庫。Spring Data JPA 是 Spring Data 的一部分,是一個

    2024年02月08日
    瀏覽(21)
  • 以Springboot為例,如何連接多個數(shù)據(jù)庫(源)

    在Spring Boot中連接多個數(shù)據(jù)庫,可以通過配置多個數(shù)據(jù)源來實現(xiàn)??梢栽谂渲梦募卸x多個數(shù)據(jù)源的配置,每個數(shù)據(jù)源有一個唯一的名稱和對應(yīng)的數(shù)據(jù)源屬性。然后,通過在需要訪問特定數(shù)據(jù)源的地方,使用對應(yīng)的數(shù)據(jù)源名稱來訪問該數(shù)據(jù)源。 具體步驟如下 1、在配置文件

    2024年02月16日
    瀏覽(34)
  • 解決SpringBoot連接數(shù)據(jù)庫的SSLHandshakeException異常

    ? ? 在改一個新項目,服務(wù)啟動時報錯了,堆棧信息如下: ? ? 這個錯誤一般是由SSL/TLS握手過程中客戶端和服務(wù)器之間支持的協(xié)議或密碼套件不匹配引起的。 ? ? 檢查了下數(shù)據(jù)庫連接串,配置了useSSL=true,表示要求使用SSL/TLS來加密與MySQL數(shù)據(jù)庫之間的連接。 ? ? 在連接字符

    2024年03月23日
    瀏覽(25)
  • 【精·超詳細(xì)】SpringBoot 配置多個數(shù)據(jù)源(連接多個數(shù)據(jù)庫)

    【精·超詳細(xì)】SpringBoot 配置多個數(shù)據(jù)源(連接多個數(shù)據(jù)庫)

    目錄 1.項目路徑 2.pom.xml? 引入依賴: 3.application.yml配置文件: 4.兩個entity類 5.Conroller 6.兩個Service以及兩個ServiceImpl? 7.兩個Mapper及兩個Mapper.xml? 8.運行Application? 然后在瀏覽器請求 9.查看兩個數(shù)據(jù)庫是否有新增數(shù)據(jù) ? ? ? ? ? 總結(jié): 1.pom.xml 引入依賴: dynamic-datasource-spring-b

    2024年02月12日
    瀏覽(42)
  • 【SpringBoot教程】SpringBoot+MybatisPlus數(shù)據(jù)庫連接測試 用戶收貨信息接口開發(fā)

    【SpringBoot教程】SpringBoot+MybatisPlus數(shù)據(jù)庫連接測試 用戶收貨信息接口開發(fā)

    ? 專欄地址 系列教程更新中 ?? 文章介紹: SpringBoot+MybatisPlus組合可以大大加快開發(fā)效率,緊接上一篇文章的內(nèi)容,這篇文章進行數(shù)據(jù)庫的連接與查詢測試,并配置日志輸出調(diào)試 ?? 源碼獲取 : 項目中的資料可以通過文章底部公眾號戳聯(lián)系我獲取 maven聚合工程依賴知識 depend

    2024年02月06日
    瀏覽(21)
  • Springboot項目連接neo4j數(shù)據(jù)庫

    首先創(chuàng)建一個springboot項目,這里不再介紹。 連接 neo4j 數(shù)據(jù)庫的依賴包 spring-boot-starter-data-neo4j依賴包 mybatis-plus依賴包

    2024年02月12日
    瀏覽(23)
  • SpringBoot整合(五)HikariCP、Druid數(shù)據(jù)庫連接池—多數(shù)據(jù)源配置

    SpringBoot整合(五)HikariCP、Druid數(shù)據(jù)庫連接池—多數(shù)據(jù)源配置

    在項目中,數(shù)據(jù)庫連接池基本是必不可少的組件。在目前數(shù)據(jù)庫連接池的選型中,主要是 Druid ,為 監(jiān)控 而生的數(shù)據(jù)庫連接池。 HikariCP ,號稱 性能 最好的數(shù)據(jù)庫連接池。 在Spring Boot 2.X 版本,默認(rèn)采用 HikariCP 連接池。而阿里大規(guī)模采用 Druid 。下面介紹在SpringBoot中使用Hika

    2024年02月17日
    瀏覽(51)
  • SpringBoot整合Druid數(shù)據(jù)庫連接池&多數(shù)據(jù)源&注解切換&動態(tài)添加

    配置好之后 Druid 會通過 DruidDataSourceAutoConfigure 自動裝配 屬性配置 數(shù)據(jù)源枚舉 動態(tài)數(shù)據(jù)源 繼承 AbstractRoutingDataSource 就可以實現(xiàn)動態(tài)數(shù)據(jù)源了 實現(xiàn)了一個動態(tài)數(shù)據(jù)源類的構(gòu)造方法,主要是為了設(shè)置默認(rèn)數(shù)據(jù)源,以及以Map保存的各種目標(biāo)數(shù)據(jù)源。其中Map的key是設(shè)置的數(shù)據(jù)源名稱

    2024年03月22日
    瀏覽(34)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包