java Concurrent practice: connection pool implementation

Keywords: Java Zookeeper Database less

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:

  1. A busy queue
  2. An idle queue idle
  3. Max active connections in connection pool maxActive
  4. Connection pool Max wait
  5. 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){

        }
    }
}

Posted by acirilo on Sun, 01 Dec 2019 04:03:57 -0800