Introduction to pool technology
In the process of using database, we often use database connection pool instead of directly using database connection to operate. This is because the cost of creating and destroying each database connection is expensive, and pooling technology creates resources in advance, which are reusable, so as to ensure that only a specified number of resources can be used in the case of multiple users, Avoid a user to create a connection resource, resulting in the program running overhead.
Implementation principle of connection pool
Only a simple connection pool is realized here. More complex requirements can be improved according to the connection pool. The main parameters of the connection pool are as follows:
- A busy queue
- An idle queue idle
- Max active connections in connection pool maxActive
- Connection pool Max wait
- Number of active connections in connection pool activeSize
The procedure flow chart is as follows:
code implementation
Generic interface ConnectionPool.java
public interface ConnectionPool<T> { /** * Initialize pool resources * @param maxActive Maximum number of active connections in the pool * @param maxWait Maximum waiting time */ void init(Integer maxActive, Long maxWait); /** * Get resources from pool * @return Link resources */ T getResource() throws Exception; /** * Release connection * @param connection Connection in use */ void release(T connection) throws Exception; /** * Release connection pool resources */ void close(); }
Take zookeeper as an example to implement zookeeper connection pool, ZookeeperConnectionPool.java
public class ZookeeperConnectionPool implements ConnectionPool<ZooKeeper> { //Maximum active connections private Integer maxActive; //Maximum waiting time private Long maxWait; //Free Queue private LinkedBlockingQueue<ZooKeeper> idle = new LinkedBlockingQueue<>(); //Busy queue private LinkedBlockingQueue<ZooKeeper> busy = new LinkedBlockingQueue<>(); //Number of connection pool active connections private AtomicInteger activeSize = new AtomicInteger(0); //Connection pool close flag private AtomicBoolean isClosed = new AtomicBoolean(false); //Total connections obtained private AtomicInteger createCount = new AtomicInteger(0); //Counters waiting for zookeeper client creation to complete private static ThreadLocal<CountDownLatch> latchThreadLocal = ThreadLocal.withInitial(() -> new CountDownLatch(1)); public ZookeeperConnectionPool(Integer maxActive, Long maxWait) { this.init(maxActive, maxWait); } @Override public void init(Integer maxActive, Long maxWait) { this.maxActive = maxActive; this.maxWait = maxWait; } @Override public ZooKeeper getResource() throws Exception { ZooKeeper zooKeeper; Long nowTime = System.currentTimeMillis(); final CountDownLatch countDownLatch = latchThreadLocal.get(); //Whether idle queue idle has connection if ((zooKeeper = idle.poll()) == null) { //Determine whether the number of connections in the pool is less than maxActive if (activeSize.get() < maxActive) { //First increase the number of connections in the pool, and then judge whether it is less than or equal to maxActive if (activeSize.incrementAndGet() <= maxActive) { //Create a zookeeper connection zooKeeper = new ZooKeeper("localhost", 5000, (watch) -> { if (watch.getState() == Watcher.Event.KeeperState.SyncConnected) { countDownLatch.countDown(); } }); countDownLatch.await(); System.out.println("Thread:" + Thread.currentThread().getId() + "Get connection:" + createCount.incrementAndGet() + "strip"); busy.offer(zooKeeper); return zooKeeper; } else { //If it is found that it is greater than maxActive after increase, subtract the increased activeSize.decrementAndGet(); } } //Wait for busy queue to release connection if active thread is full try { System.out.println("Thread:" + Thread.currentThread().getId() + "Waiting for idle resources"); Long waitTime = maxWait - (System.currentTimeMillis() - nowTime); zooKeeper = idle.poll(waitTime, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new Exception("Waiting exception"); } //Judge whether it is timeout if (zooKeeper != null) { System.out.println("Thread:" + Thread.currentThread().getId() + "Get connection:" + createCount.incrementAndGet() + "strip"); busy.offer(zooKeeper); return zooKeeper; } else { System.out.println("Thread:" + Thread.currentThread().getId() + "Get connection timeout, please try again!"); throw new Exception("Thread:" + Thread.currentThread().getId() + "Get connection timeout, please try again!"); } } //The idle queue has a connection and returns directly busy.offer(zooKeeper); return zooKeeper; } @Override public void release(ZooKeeper connection) throws Exception { if (connection == null) { System.out.println("connection Empty"); return; } if (busy.remove(connection)){ idle.offer(connection); } else { activeSize.decrementAndGet(); throw new Exception("Release failure"); } } @Override public void close() { if (isClosed.compareAndSet(false, true)) { idle.forEach((zooKeeper) -> { try { zooKeeper.close(); } catch (InterruptedException e) { e.printStackTrace(); } }); busy.forEach((zooKeeper) -> { try { zooKeeper.close(); } catch (InterruptedException e) { e.printStackTrace(); } }); } } }
test case
Here, create 20 thread concurrent test connection pools, Test.java
public class Test { public static void main(String[] args) throws Exception { int threadCount = 20; Integer maxActive = 10; Long maxWait = 10000L; ZookeeperConnectionPool pool = new ZookeeperConnectionPool(maxActive, maxWait); CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < threadCount; i++) { new Thread(() -> { countDownLatch.countDown(); try { countDownLatch.await(); ZooKeeper zooKeeper = pool.getResource(); Thread.sleep(2000); pool.release(zooKeeper); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }).start(); } while (true){ } } }