Generic Object Pool and Generic Keyed Object Pool

Keywords: Apache Database

The first two articles talked about how to use commons-pool libraries simply. One of the problems we need to consider here is that in many cases our objects in the pool are heavier and most of the scarce resources, such as database connections, so if we keep some connections in the pool and don't return them, it will take up a lot of resources. Some resource utilization is reduced, so how to better manage the resources in the pool? commons-pool provides a GenericObjectPool class, which makes the above problems solved. Similarly, for the GenericObjectPool class, there is a corresponding GenericKeyedObjectPool class.

Here's another example.

A Connection class can be imagined as a remote connection, such as a database connection. These include creating connections, closing connections, and a print method.

package com.googlecode.garbagecan.commons.pool.sample3;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class MyConnection {  private static Logger logger = LoggerFactory.getLogger(MyConnection.class);  private String name; private boolean connected; public MyConnection(String name) {  this.name = name; } public void connect() {  this.connected = true;  logger.info(name + ": " + connected); } public void close() {  this.connected = false;  logger.info(name + ": " + connected); } public boolean isConnected() {  return this.connected; }  public String getName() {  return this.name; }  public void print() {  logger.info(this.name); }}
An implementation class of PoolableObjectFactory interface provides makeObject, activateObject, passivateObject, validateObject, destroyObject methods.

package com.googlecode.garbagecan.commons.pool.sample3;import org.apache.commons.pool.PoolableObjectFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class MyConnectionPoolableObjectFactory implements PoolableObjectFactory private static Logger logger = LoggerFactory.getLogger(MyConnectionPoolableObjectFactory.class);  private static int count = 0;  public Object makeObject() throws Exception {  MyConnection myConn = new MyConnection(generateName());  logger.info(myConn.getName());  myConn.connect();  return myConn; }  public void activateObject(Object obj) throws Exception {  MyConnection myConn = (MyConnection)obj;  logger.info(myConn.getName()); } public void passivateObject(Object obj) throws Exception {  MyConnection myConn = (MyConnection)obj;  logger.info(myConn.getName()); }  public boolean validateObject(Object obj) {  MyConnection myConn = (MyConnection)obj;  logger.info(myConn.getName());  return myConn.isConnected(); }  public void destroyObject(Object obj) throws Exception {  MyConnection myConn = (MyConnection)obj;  logger.info(myConn.getName());  myConn.close(); }  private synchronized String generateName() {  return "conn_" + (++count); }}
A test class

package com.googlecode.garbagecan.commons.pool.sample3;import org.apache.commons.pool.ObjectPool;import org.apache.commons.pool.PoolableObjectFactory;import org.apache.commons.pool.impl.GenericObjectPool;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Test {  private static Logger logger = LoggerFactory.getLogger(Test.class);  public static void main(String[] args) {  //test1();  //test2();  //test3(); }  private static void test1() {  PoolableObjectFactory factory = new MyConnectionPoolableObjectFactory();  GenericObjectPool.Config config = new GenericObjectPool.Config();  config.lifo = false;  config.maxActive = 5;  config.maxIdle = 5;  config.minIdle = 1;  config.maxWait = 5 * 1000;    ObjectPool pool = new GenericObjectPool(factory, config);  for (int i = 0; i < 10; i++) {   Thread thread = new Thread(new MyTask(pool));   thread.start();  }  //closePool(pool); }  private static void test2() {  PoolableObjectFactory factory = new MyConnectionPoolableObjectFactory();  GenericObjectPool.Config config = new GenericObjectPool.Config();  config.lifo = false;  config.maxActive = 5;  config.maxIdle = 5;  config.minIdle = 1;  config.maxWait = 20 * 1000;  ObjectPool pool = new GenericObjectPool(factory, config);  for (int i = 0; i < 10; i++) {   Thread thread = new Thread(new MyTask(pool));   thread.start();  }  //closePool(pool); } private static void test3() {  PoolableObjectFactory factory = new MyConnectionPoolableObjectFactory();  GenericObjectPool.Config config = new GenericObjectPool.Config();  config.lifo = false;  config.maxActive = 5;  config.maxIdle = 0;  config.minIdle = 0;  config.maxWait = -1;  ObjectPool pool = new GenericObjectPool(factory, config);  Thread thread = new Thread(new MyTask(pool));  thread.start();  try {   Thread.sleep(60L * 1000L);  } catch (Exception e) {   e.printStackTrace();  }    //closePool(pool); } private static void closePool(ObjectPool pool) {  try {   pool.close();  } catch (Exception e) {   e.printStackTrace();  } }  private static class MyTask implements Runnable {  private ObjectPool pool;    public MyTask(ObjectPool pool) {   this.pool = pool;  }    public void run() {   MyConnection myConn = null;   try {    myConn = (MyConnection)pool.borrowObject();    try {     myConn.print();    } catch(Exception ex) {     pool.invalidateObject(myConn);     myConn = null;    }    Thread.sleep(10L * 1000L);   } catch(Exception ex) {    logger.error("Cannot borrow connection from pool.", ex);   } finally {    if (myConn != null) {     try {      pool.returnObject(myConn);     } catch (Exception ex) {      logger.error("Cannot return connection from pool.", ex);     }    }   }  } }}
There are three methods, and three cases are tested respectively.
  • The class contains an internal class that implements the Runnable interface. The purpose is to start several threads to simulate the use of connection classes. In order to be as real as possible, sleep in the run method for 10 seconds.
  • Running test method test1() first, you can see that the first five threads can get the Connection class very well while the last five threads can't get the connection and throw an exception because "config.maxActive = 5;" and "config.maxWait = 5 * 1000;" are working because the maximum active connections are configured to be five, and subsequent applications are not available. The waiting time for a valid connection is 5 seconds, so the last five threads in the test1 method throw all the exceptions after waiting 5 seconds, indicating that they cannot apply for a connection.
  • Now run the test2() method, in test2, change "config.maxWait = 20 * 1000;" to 20 seconds, and it takes 10 seconds for each thread in our program to use the connection, so the next five threads will all get the connection after waiting 10 seconds, so the program will finally run successfully.
  • Look at the test 3 () method, which changes maxIdle and minIdle to 0, which means that the connection is returned immediately when the connection is not used. For database connection, it means closing the physical connection, while maxWait is changed to - 1, which means that if there is no application for connection, it will always wait. Run the test 3 () method, and observe the log, you can see that the program will call MyConnectionPoola after the user connects the object. The bleObjectFactory. destroyObject () and MyConnection.close() methods destroy objects. So if you use such a configuration, it's equivalent to a physical connection every time, closing the connection when it's used up. Of course, here's an extreme example. In reality, maxIdle and minIdle are not set to zero.

In fact, there are many configuration parameters for GenericObjectPool.Config class and GenericKeyedObjectPool.Config class. Here are just a few of the simplest commonly used, specific reference to official documents. _

Posted by Penelope on Mon, 22 Apr 2019 13:57:35 -0700