Java geek
1. Dynamic scaling of resource pools
1. To improve the performance of the resource pool, set the minimum number of idle resources to complete the initialization of the resource pool. When the resource used exceeds the minimum idle resource number, the consumer will shrink to the minimum idle resource number after releasing it back into the pool for a certain time.
2. To avoid overload caused by unlimited resource application, set the maximum number of resources. The maximum number of resources in the pool cannot exceed the maximum number of resources.
2. Dynamically scale related class structures
class | Duties and responsibilities |
---|---|
HouseKeeper | The Runnable interface is implemented to dynamically scale pool resources. |
ScheduledExecutorService | Responsible for scheduling fixtures. |
ScheduledFuture | Returned by ScheduledExecutorService, the HouseKeeper can be terminated. |
HikariPool | Responsible for initializing and calling the above classes in the constructor. |
3. The source code
3.1. HikariPool
// Initialize ScheduledExecutorService
this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
// scheduling, delay and periodic scheduling
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
Copy the code
3.2.HouseKeeper
private final class HouseKeeper implements Runnable
{
private volatile long previous = plusMillis(currentTime(), -housekeepingPeriodMs);
@Override
public void run(a)
{
try {
// refresh values in case they changed via MBeanconnectionTimeout = config.getConnectionTimeout(); validationTimeout = config.getValidationTimeout(); leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold()); catalog = (config.getCatalog() ! =null && !config.getCatalog().equals(catalog)) ? config.getCatalog() : catalog;
final long idleTimeout = config.getIdleTimeout();
final long now = currentTime();
// Detect retrograde time, allowing +128ms as per NTP spec.
if (plusMillis(now, 128) < plusMillis(previous, housekeepingPeriodMs)) {
logger.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",
poolName, elapsedDisplayString(previous, now));
previous = now;
softEvictConnections();
return;
}
else if (now > plusMillis(previous, (3 * housekeepingPeriodMs) / 2)) {
// No point evicting for forward clock motion, this merely accelerates connection retirement anyway
logger.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now));
}
previous = now;
String afterPrefix = "Pool ";
if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {
logPoolState("Before cleanup ");
afterPrefix = "After cleanup ";
// Get an unused resource
final List<PoolEntry> notInUse = connectionBag.values(STATE_NOT_IN_USE);
// If the unused resource is greater than the configured minimum idle resource, close the excess resource
int toRemove = notInUse.size() - config.getMinimumIdle();
for (PoolEntry entry : notInUse) {
if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {
closeConnection(entry, "(connection has passed idleTimeout)");
toRemove--;
}
}
}
logPoolState(afterPrefix);
// In addition to dynamically reducing resources, here dynamically expanding resources
fillPool(); // Try to maintain minimum connections
}
catch (Exception e) {
logger.error("Unexpected exception in housekeeping task", e); }}}Copy the code
Dynamic resource expansion:
//HikariPool.java
private synchronized void fillPool(a)
{
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); }}Copy the code
3.3. Load-balanced across
Releasing a resource in ConcurrentBag changes the state of the resource and does not change the number of available resources in the resource pool.
public void requite(final T bagEntry)
{
// This is only a modification of the resource state. It does not reduce the number of available resources in the resource pool.
bagEntry.setState(STATE_NOT_IN_USE);
for (int i = 0; waiters.get() > 0; i++) {
if(bagEntry.getState() ! = STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {return;
}
else if ((i & 0xff) = =0xff) { // 0xff is 255, which is entered at 256 intervals
parkNanos(MICROSECONDS.toNanos(10));
}
else{ yield(); }}final List<Object> threadLocalList = threadList.get();
if (threadLocalList.size() < 50) {
threadLocalList.add(weakThreadLocals ? newWeakReference<>(bagEntry) : bagEntry); }}Copy the code
4. To summarize
- The minimum number of resources in a resource pool is initialized.
- Dynamic scaling of resources can be accomplished by scheduling threads using the JUC tool ScheduledExecutorService, without the need for third-party timers.
- When a consumer releases a resource, it does not immediately reduce the amount of available resources in the resource pool, because it is likely that other consumers will apply for resources again. To avoid reducing unnecessary resource creation operations, the released resource should be closed after a certain period of time.
end.
<– Thanks for the triple punch, left likes and followings.
Related reading: HikariPool source code (a) first HikariPool source code (two) design ideas for reference HikariPool source code (four) resource state HikariPool source code (five) thread and related tools HikariPool source code (six) to use some useful JAVA features
Java geek site: javageektour.com/