-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Welcome to the hikari-demo wiki!
HikariCP — это, по сути, пул подключений к базе данных.
Создание и закрытие соединений с базой данных требует больших затрат. HikariCP использует «пулирование» для повторного использования соединений, чтобы снизить накладные расходы.
- HikariCP — самый быстрый доступный пул соединений. Даже популярный BoneCP прекратил его поддерживать и взял на себя инициативу уступить ему место. SpringBoot также устанавливает его в качестве пула соединений по умолчанию.
- HikariCP очень легкий. Размер jar-пакета версии 4.0.3, использованного в этой статье, составляет всего 156 КБ, а его исходный код действительно очень доработан.
Эта статья будет содержать следующее содержание (поскольку она длинная, вы можете прочитать ее по мере необходимости):
- Как использовать HikariCP (начало работы, JMX и т. д.)
- Подробное объяснение параметров конфигурации
- Анализ исходного кода
Используйте HikariCP для получения объекта подключения и выполнения простых добавлений, удалений, изменений и запросов к пользовательским данным.
JDK
:1.8.0_231
maven
:3.6.3
IDE
:Spring Tool Suite 4.6.1.РЕЛИЗ
mysql-connector-java
:8.0.15
mysql
:5.7.28
Hikari
:4.0.3
Тип проекта Проект Maven, метод упаковки jar.
<!-- test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- hikari --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>4.0.3</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <!-- log --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> <type>jar</type> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> <type>jar</type> </dependency>
В этой статье для настройки HikariCP используется файл конфигурации. Конечно, мы также можем настроить его явно в коде, но это не рекомендуется. Поскольку это вводный пример, я привожу здесь только необходимые параметры, а остальные параметры будут подробно описаны позже.
jdbcUrl=jdbc:mysql://localhost:3306/github_demo?characterEncoding=utf8&serverTimezone=GMT%2B8 username=root password=root
При инициализации пула соединений мы можем явно указать файл конфигурации в коде или настроить его через параметры запуска.
// 加载配置文件,也可以无参构造并使用启动参数 hikaricp.configurationFile 指定配置文件(不推荐,后面会说原因) HikariConfig config = new HikariConfig("/hikari2.properties"); HikariDataSource dataSource = new HikariDataSource(config);
После инициализации пула соединений мы можем HikariDataSource.getConnection()
получить объект соединения с помощью методов, а затем выполнить операции добавления, удаления, изменения и запроса. Эта часть здесь не будет показана.
Включите функцию JMX и используйте jconsole для управления пулом соединений.
Добавьте конфигурацию на основе вводного примера. Для этого необходимо установить для параметра RegisterMbeans значение true, и функция JMX будет включена.
#-------------JMX-------------------------------- # 是否开启 JMX # 默认 false registerMbeans=true# 是否允许通过 JMX 挂起和恢复连接池 # 默认为 false allowPoolSuspension=true
# 连接池名称。 # 默认自动生成 poolName=zzsCP
registerMbeans=true
allowPoolSuspension=true
poolName=zzsCP" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
Чтобы увидеть конкретный эффект, дайте основному потоку поспать 20 минут.
public static void main(String[] args) throws InterruptedException { HikariConfig config = new HikariConfig("/hikaricp_base.properties"); HikariDataSource dataSource = new HikariDataSource(config); Thread.sleep(20 * 60 * 1000); dataSource.close(); }
Запустите основной метод, используйте инструмент JDK jconsole для подключения к нашему проекту, и вы увидите наш пул соединений на вкладке MBean. Далее мы можем сделать это:
- Динамически изменять конфигурацию через PoolConfig (разрешено изменять только некоторые параметры);
- Получите количество соединений в пуле соединений (активных, простаивающих и всех) через Пул, получите количество потоков, ожидающих соединений, приостановите и возобновите пул соединений, отбросьте неиспользуемые соединения и т. д.
Если вы хотите узнать больше о функциях JMX, вы можете обратиться к статье моего блога: Как использовать JMX для управления программами?
По сравнению с другими пулами соединений параметры конфигурации HikariCP очень просты, и необходимо отметить несколько особенностей:
- HikariCP вынужден проверять работоспособность соединения при его предоставлении, в отличие от других пулов соединений, где вы можете не проверять;
- По умолчанию будут отмечены значенияdleTimeout и maxLifetime. Вы можете отключить их, но это не рекомендуется;
- По умолчанию параметры KeepaliveTime и LeakDetectionThreshold не отмечены. Их можно включить. Рекомендуется включить LeakDetectionThreshold.
Обратите внимание, что здесь jdbcUrl и dataSourceClassName являются необязательными.
#-------------必需的参数-------------------------------- # JDBC 驱动中 DataSource 的实现类全限定类名。不支持 XA DataSource # 如果指定, HikariCP 将使用 DataSouce.getConnection 获取连接而不是使用 DriverManager.getConnection,官方建议指定(mysql 除外) # dataSourceClassName=# 如果指定, HikariCP 将使用 DriverManager.getConnection 获取连接而不是使用 DataSouce.getConnection jdbcUrl=jdbc:mysql://localhost:3306/github_demo?characterEncoding=utf8&serverTimezone=GMT%2B8
# 用户名和密码 username=root password=root
jdbcUrl=jdbc:mysql://localhost:3306/github_demo?characterEncoding=utf8&serverTimezone=GMT%2B8
username=root password=root" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
# 从池中借出的连接是否默认自动提交事务 # 默认 true autoCommit=true# 当我从池中借出连接时,愿意等待多长时间。如果超时,将抛出 SQLException # 默认 30000 ms,最小值 250 ms。支持 JMX 动态修改 connectionTimeout=30000
# 一个连接在池里闲置多久时会被抛弃 # 当 minimumIdle < maximumPoolSize 才生效 # 默认值 600000 ms,最小值为 10000 ms,0表示禁用该功能。支持 JMX 动态修改 idleTimeout=600000
# 多久检查一次连接的活性 # 检查时会先把连接从池中拿出来(空闲的话),然后调用isValid()或执行connectionTestQuery来校验活性,如果通过校验,则放回池里。 # 默认 0 (不启用),最小值为 30000 ms,必须小于 maxLifetime。支持 JMX 动态修改 keepaliveTime=0
# 当一个连接存活了足够久,HikariCP 将会在它空闲时把它抛弃 # 默认 1800000 ms,最小值为 30000 ms,0 表示禁用该功能。支持 JMX 动态修改 maxLifetime=1800000
# 用来检查连接活性的 sql,要求是一个查询语句,常用select 'x' # 如果驱动支持 JDBC4.0,建议不设置,这时默认会调用 Connection.isValid() 来检查,该方式会更高效一些 # 默认为空 # connectionTestQuery=
# 池中至少要有多少空闲连接。 # 当空闲连接 < minimumIdle,总连接 < maximumPoolSize 时,将新增连接 # 默认等于 maximumPoolSize。支持 JMX 动态修改 minimumIdle=5
# 池中最多容纳多少连接(包括空闲的和在用的) # 默认为 10。支持 JMX 动态修改 maximumPoolSize=10
# 用于记录连接池各项指标的 MetricRegistry 实现类 # 默认为空,只能通过代码设置 # metricRegistry=
# 用于报告连接池健康状态的 HealthCheckRegistry 实现类 # 默认为空,只能通过代码设置 # healthCheckRegistry=
# 连接池名称。 # 默认自动生成 poolName=zzsCP
autoCommit=true
connectionTimeout=30000
idleTimeout=600000
keepaliveTime=0
maxLifetime=1800000
minimumIdle=5
maximumPoolSize=10
poolName=zzsCP" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
# 如果启动连接池时不能成功初始化连接,是否快速失败 TODO # >0 时,会尝试获取连接。如果获取时间超过指定时长,不会开启连接池,并抛出异常 # =0 时,会尝试获取并验证连接。如果获取成功但验证失败则不开启池,但是如果获取失败还是会开启池 # <0 时,不管是否获取或校验成功都会开启池。 # 默认为 1 initializationFailTimeout=1# 是否在事务中隔离 HikariCP 自己的查询。 # autoCommit 为 false 时才生效 # 默认 false isolateInternalQueries=false
# 是否允许通过 JMX 挂起和恢复连接池 # 默认为 false allowPoolSuspension=false
# 当连接从池中取出时是否设置为只读 # 默认值 false readOnly=false
# 是否开启 JMX # 默认 false registerMbeans=true
# 数据库 catalog # 默认由驱动决定 # catalog=
# 在每个连接创建后、放入池前,需要执行的初始化语句 # 如果执行失败,该连接会被丢弃 # 默认为空 # connectionInitSql=
# JDBC 驱动使用的 Driver 实现类 # 一般根据 jdbcUrl 判断就行,报错说找不到驱动时才需要加 # 默认为空 # driverClassName=
# 连接的默认事务隔离级别 # 默认值为空,由驱动决定 # transactionIsolation=
# 校验连接活性允许的超时时间 # 默认 5000 ms,最小值为 250 ms,要求小于 connectionTimeout。支持 JMX 动态修改 validationTimeout=5000
# 连接对象可以被借出多久 # 默认 0(不开启),最小允许值为 2000 ms。支持 JMX 动态修改 leakDetectionThreshold=0
# 直接指定 DataSource 实例,而不是通过 dataSourceClassName 来反射构造 # 默认为空,只能通过代码设置 # dataSource=
# 数据库 schema # 默认由驱动决定 # schema=
# 指定连接池获取线程的 ThreadFactory 实例 # 默认为空,只能通过代码设置 # threadFactory=
# 指定连接池开启定时任务的 ScheduledExecutorService 实例(建议设置setRemoveOnCancelPolicy(true)) # 默认为空,只能通过代码设置 # scheduledExecutor=
# JNDI 配置的数据源名 # 默认为空 # dataSourceJndiName=
initializationFailTimeout=1
isolateInternalQueries=false
allowPoolSuspension=false
readOnly=false
registerMbeans=true
validationTimeout=5000
leakDetectionThreshold=0
dataSourceJndiName=" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
Исходный код HikariCP небольшой и точный, что делает его очень читаемым. Если вы никогда не видели код, похожий на поэзию, взгляните на HikariCP.
Напоминаем, что перед чтением исходного кода HiakriCP вам необходимо освоить использование встроенных классов JDK, таких как , , , , CopyOnWriteArrayList
и AtomicInteger
т SynchronousQueue
. Semaphore
д .AtomicIntegerFieldUpdater
LockSupport
Обратите внимание, что следующий код был отредактирован для обеспечения длины и удобочитаемости.
Пул соединений с базами данных разрабатывается уже давно и считается относительно зрелой технологией. К наиболее широко используемым библиотекам относятсяboneCP, DBCP, C3P0, Druid и т. д. Поскольку пул соединений с базой данных стал узким местом, так называемое улучшение производительности заключается только в оптимизации некоторых деталей кода. В это время появился HikariCP, который быстро стал популярным. По сравнению с другими пулами соединений его скорость не является обычной. Но это происходит очень быстро. Ниже приведены результаты теста JMH ([адрес тестового проекта]( brettwooldridge/HikariCP-benchmark: тесты JHM для пулов соединений JDBC (github.com) )).
Почему HikariCP быстрый? Я вижу много объяснений в Интернете. Например, большое количество инструментов параллельных пакетов JDK используется, чтобы избежать грубых блокировок, использования пользовательских классов, таких как FastList, динамических прокси-классов и т. д. Я не думаю, что это основные причины.
Причина, по которой HikariCP быстр, больше связана с оптимизацией на абстрактном уровне .
Пул соединений, как следует из названия, представляет собой пруд, в котором хранятся объекты соединений. Почти все пулы соединений абстрагируют пруд от уровня кода. Количество соединений в пуле не статично. Например, если соединение не удалось и его необходимо удалить, создается новое соединение, пользователь одалживает или возвращает соединение и т. д., то всего их не более четырех. операции над пулом соединений: заимствовать, возвращать, добавлять, удалять .
Пул соединений обычно устроен следующим образом: действия заимствования и удаления удаляют соединение из пула, а действия добавления и возврата добавляют соединение в пруд. Я называю эту модель «традиционной моделью».
«Традиционная модель» — это относительно стандартная модель. На абстрактном уровне она очень соответствует нашей реальной жизни. Например, если кто-то занимает мои деньги, денег больше нет в моем кошельке. Хорошо известные DBCP, C3P0, Druid и т. д. разработаны на основе «традиционной модели».
Но HikariCP отличается от других. Он не следует старому пути, а оптимизирует «традиционную модель», чтобы действительно ускорить пул соединений.
В «традиционной модели» четыре действия — заимствование, возврат, добавление и удаление — требуют одной и той же блокировки, то есть только одному потоку разрешено управлять пулом одновременно, а переключение потоков будет очень частым, когда параллелизм высокий . Поскольку несколько потоков управляют одним и тем же пулом, соединения, входящие и исходящие из пула, должны быть заблокированы для обеспечения безопасности потоков. Можем ли мы что-нибудь с этим поделать?
HikariCP так и делает. Заимствованное соединение не будет вынесено из пруда, но будет помечено как "заимствовано". При возврате отметка "заимствовано" у этого соединения будет снята. Я называю этот подход «моделированием с тегами». «Модель маркера» может реализовывать действия заимствования и возврата без блокировки. Как именно это делается?
Во-первых, когда я хочу взять в долг, мне нужно посмотреть, какое соединение в пруду можно взять взаймы. Это предполагает операцию чтения пула соединений, поскольку количество соединений в пуле не статично, и для согласованности мы должны его заблокировать. Однако HikariCP не добавляется, почему? Потому что HikariCP допускает несогласованность чтения . При заимствовании мы на самом деле читаем не настоящий пруд, а снимок текущего пруда. Давайте посмотрим, где HikariCP хранит соединения. CopyOnWriteArrayList
Мы знаем, что CopyOnWriteArrayList
это коллекция, которую безопасно записывать и небезопасно читать.
public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable {// 存放连接的集合 private final CopyOnWriteArrayList<T> sharedList; }
Далее, когда мы находим заемное соединение, нам нужно пометить его как заимствованное. Обратите внимание, что в этот момент его могут пометить несколько потоков. Что нам делать? Он будет заперт? Не забывайте, что мы можем использовать механизм CAS для обновления тега соединения, и на данный момент блокировка не требуется. Посмотрите, как реализован HikariCP.
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException { final int waiting = waiters.incrementAndGet(); try { for (T bagEntry : sharedList) { if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) { return bagEntry; } }<span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">return</span> <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">null</span>; } <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">finally</span> { <span class="pl-s1" style="box-sizing: border-box;">waiters</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">decrementAndGet</span>(); }
}
return null;
}
finally {
waiters.decrementAndGet();
}
}" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
Следовательно, в «модели маркера» необходимо блокировать только добавление и удаление, тогда как заимствование и возврат блокировать не нужно . Благодаря этому революционному дизайну производительность пула соединений была значительно улучшена.
Это основная причина, по которой я считаю HikariCP быстрым.
Итак, давайте взглянем на исходный код HikariCP.
В HikariCP не так много классов, самые важные из них — вот эти, как показано на картинке. Нетрудно обнаружить, что эта структура классов очень похожа на DBCP2.
Эти категории можно разделить на четыре части:
-
Пользовательский интерфейс. Пользователи обычно используют
DataSource.getConnection()
для получения объектов подключения. - Поддержка JMX.
-
Информация о конфигурации. Используйте
HikariConfig
загруженные файлы конфигурации илиHikariConfig
параметры, настроенные вручную, которые обычно используются в качестве входных параметров для созданияHikariDataSource
объектов; -
Пул соединений. Процесс получения соединения
HikariDataSource.getConnection()
->HikariPool.getConnection()
->ConcurrentBag.borrow(long, TimeUnit)
. Следует отметить, чтоConcurrentBag
это настоящий пул соединений,HikariPool
но он используется для управления пулом соединений .
ConcurrentBag
Его можно рассматривать как основной класс HikariCP. Это настоящий пул соединений в нижней части HikariCP. Упомянутая выше «модель маркировки» реализуется только им . Если вы не хотите видеть слишком много кода, достаточно просто просмотреть его.
С точки зрения дизайна ConcurrentBag
это относительно общий пул ресурсов. Это может быть пул подключений к базе данных или пул других объектов, при условии, что хранимые объекты ресурсов реализуют IConcurrentBagEntry
интерфейс. Поэтому, если нам нужно самим построить пул в нашем проекте, мы можем напрямую использовать этот готовый компонент.
public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable { private final CopyOnWriteArrayList<T> sharedList; }
Вот краткое введение в ConcurrentBag
несколько областей:
свойство | описывать |
---|---|
CopyOnWriteArrayList общий список | Хранит объекты ресурсов с тремя состояниями: используется, не используется и зарезервировано. |
Список локальных потоков | Хранит объекты ресурсов, возвращаемые текущим потоком. |
SynchronousQueue HandoffQueue | Это блокирующая очередь без емкости. Вы можете выбрать, блокировать ли ее при выходе из очереди или при входе в очередь. |
Официанты AtomicInteger | Количество потоков, ожидающих в данный момент получения элементов |
Чтобы более четко понять значение вышеуказанных полей, я нарисовал простую диаграмму. Она не очень строгая, поэтому просто взгляните на нее. На этом рисунке клиентский поток может вызывать операции заимствования, возврата и удаления, поток houseKeepingExecutorService может вызывать операцию удаления, и только addConnectionExecutor может выполнять операцию добавления.
После освоения двух вышеуказанных классов весь исходный код HikariCP должен быть относительно полным. Давайте поговорим о некоторых интересных местах.
В приведенном ниже коде HikariDataSource
на самом деле их два HikariPool
.
public class HikariDataSource extends HikariConfig implements DataSource, Closeable { private final HikariPool fastPathPool; private volatile HikariPool pool; }
Почему мы это делаем?
Прежде всего, с точки зрения производительности для создания соединения лучше использовать fastPathPool, чем пул, поскольку пул является энергозависимым и для обеспечения видимости нельзя использовать кэширование. Так почему же нам все еще нужно использовать пул?
Открыв его HikariDataSource.getConnection()
, мы видим, что существование пула можно использовать для поддержки блокировок с двойной проверкой. Что меня больше интересует, так это то, почему бы не дать HikariPool
ссылку на fastPathPool? ? Если вам интересно, вы можете изучить этот вопрос.
public Connection getConnection() throws SQLException { if (fastPathPool != null) { return fastPathPool.getConnection(); }<span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">HikariPool</span> <span class="pl-s1" style="box-sizing: border-box;">result</span> = <span class="pl-s1" style="box-sizing: border-box;">pool</span>; <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">if</span> (<span class="pl-s1" style="box-sizing: border-box;">result</span> == <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">null</span>) { <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">synchronized</span> (<span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">this</span>) { <span class="pl-s1" style="box-sizing: border-box;">result</span> = <span class="pl-s1" style="box-sizing: border-box;">pool</span>; <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">if</span> (<span class="pl-s1" style="box-sizing: border-box;">result</span> == <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">null</span>) { <span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">validate</span>(); <span class="pl-s1" style="box-sizing: border-box;">pool</span> = <span class="pl-s1" style="box-sizing: border-box;">result</span> = <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">new</span> <span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">HikariPool</span>(<span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">this</span>); <span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">this</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">seal</span>(); } } } <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">return</span> <span class="pl-s1" style="box-sizing: border-box;">result</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">getConnection</span>();
}
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
pool = result = new HikariPool(this);
this.seal();
}
}
}
return result.getConnection();
}" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
Фактически, эти два HikariPool
объекта имеют две ситуации значения:
Значение первое: fastPathPool = pool = new HikariPool(this)
. При new HikariDataSource(HikariConfig configuration)
создании с помощью параметризованного конструктора HikariDataSource
значение будет выглядеть следующим образом;
Значение второе: fastPathPool = null;pool = new HikariPool(this)
. Это значение появится , когда new HikariDataSource()
оно будет создано посредством конструкции без параметров .HikariDataSource
Итак, я предпочитаю использовать new HikariDataSource(HikariConfig configuration)
этот путь, потому что при этом мы будем использовать fastPathPool для получения соединения.
Код загрузки конфигурации HikariCP очень краток. PropertyElf.setTargetFromProperties(Object, Properties)
Начнем непосредственно с метода, а именно:
// 这个方法就是将properties的参数设置到HikariConfig中 public static void setTargetFromProperties(final Object target, final Properties properties) { if (target == null || properties == null) { return; }<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">// 获取HikariConfig的所有方法</span> <span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">List</span><<span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">Method</span>> <span class="pl-s1" style="box-sizing: border-box;">methods</span> = <span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">Arrays</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">asList</span>(<span class="pl-s1" style="box-sizing: border-box;">target</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">getClass</span>().<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">getMethods</span>()); <span class="pl-s1" style="box-sizing: border-box;">properties</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">forEach</span>((<span class="pl-s1" style="box-sizing: border-box;">key</span>, <span class="pl-s1" style="box-sizing: border-box;">value</span>) -> { <span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">// 如果是dataSource.*的参数,直接加入到dataSourceProperties属性</span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">if</span> (<span class="pl-s1" style="box-sizing: border-box;">target</span> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">instanceof</span> <span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">HikariConfig</span> && <span class="pl-s1" style="box-sizing: border-box;">key</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">toString</span>().<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">startsWith</span>(<span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"dataSource."</span>)) { ((<span class="pl-smi" style="box-sizing: border-box; color: var(--color-prettylights-syntax-storage-modifier-import);">HikariConfig</span>) <span class="pl-s1" style="box-sizing: border-box;">target</span>).<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">addDataSourceProperty</span>(<span class="pl-s1" style="box-sizing: border-box;">key</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">toString</span>().<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">substring</span>(<span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">"dataSource."</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">length</span>()), <span class="pl-s1" style="box-sizing: border-box;">value</span>); } <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">else</span> { <span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">// 找到参数对应的setter方法并赋值</span> <span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">setProperty</span>(<span class="pl-s1" style="box-sizing: border-box;">target</span>, <span class="pl-s1" style="box-sizing: border-box;">key</span>.<span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">toString</span>(), <span class="pl-s1" style="box-sizing: border-box;">value</span>, <span class="pl-s1" style="box-sizing: border-box;">methods</span>); } });
}
// 获取HikariConfig的所有方法
List<Method> methods = Arrays.asList(target.getClass().getMethods());
properties.forEach((key, value) -> {
// 如果是dataSource.*的参数,直接加入到dataSourceProperties属性
if (target instanceof HikariConfig && key.toString().startsWith("dataSource.")) {
((HikariConfig) target).addDataSourceProperty(key.toString().substring("dataSource.".length()), value);
}
else {
// 找到参数对应的setter方法并赋值
setProperty(target, key.toString(), value, methods);
}
});
}" tabindex="0" role="button" style="box-sizing: border-box; position: relative; display: flex !important; padding: 0px !important; font-size: 14px; font-weight: var(--base-text-weight-medium, 500); line-height: 20px; white-space: nowrap; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; color: var(--fgColor-accent, var(--color-accent-fg)); background-color: transparent; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px);">
По сравнению с другими библиотеками классов (особенно druid) процесс загрузки конфигурации HikariCP очень прост. Нет необходимости загружать параметры по одному в соответствии с их именами, что будет проще поддерживать позже. Конечно, мы также можем применить этот метод к реальным проектам.
Кроме того, при настройке HikariCP нельзя писать неверные параметры или добавлять какие-то ненужные параметры, иначе будет сообщено об ошибке, поскольку соответствующий метод установки не может быть найден.
Вышеупомянутое в основном завершает исходный код HikariCP. Я добавлю больше, когда найду другие интересные места в будущем, и вы можете исправить любые недостатки.
Наконец, спасибо за чтение.
20.05.2021 Модификация
Пожалуйста, перейдите к соответствующему исходному коду: https://github.com/ZhangZiSheng001/hikari-demo.
Эта статья является оригинальной. Пожалуйста, прикрепите ссылку на первоисточник для перепечатки: https://www.cnblogs.com/ZhangZiSheng001/p/12329937.html.