apache-common-pool2对象池的简单使用
概述
大多时候,我们获取对象的方法都是直接new一个。但是,对于大对象的构造,或者构造耗时比较久的对象,比如数据库的连接对象、redis的连接对象、Http连接请求对象等等,我们每次要使用都去new一个是很不科学的。在设计模式中有一个专门的模式来解决这种场景下的问题,即享元模式。
享元模式其实很好理解,也就是构造一个对象池,这个对象池中维护一定数量的对象,需要的时候就从这个对象池中获取对象,使用完后返还给对象池。这样就避免构造对象所带来的耗时,提升了系统的性能。
举个栗子
之前的做法,虽然不会出什么问题,但是毕竟有线程在定时刷新,也是先当浪费资源的
private static List<InfluxDBConnection> conns = new LinkedList<InfluxDBConnection>();
static {
Collections.synchronizedList(conns);
final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(new Runnable() {
public void run() {
refreshConn();
}
}, 0, 3, TimeUnit.MINUTES);
}
public synchronized static InfluxDBConnection getConn() {
if (conns.size() <= 0) {
refreshConn();
}
InfluxDBConnection conn = null;
try {
conn = conns.get(0);
} catch (Exception e) {
}
return conn;
}
private synchronized static void refreshConn() {
if (conns.size() < 5) {
InfluxDBConnection conn = createConn();
if (conn != null && conn.ping()) {
conns.add(conn);
} else {
createInfluxConnErrEvent();
}
} else {
try {
Thread.sleep(10000);
} catch (Exception e) {
}
conns.get(0).close();
conns.remove(0);
}
}
5种对象池
common-pool2给我们提供了5种不同类型的对象池实现来给我们使用:
- GenericObjectPool
- SoftReferenceObjectPool
- GenericKeyedObjectPool
- ProxiedObjectPool
- ProxiedKeyedObjectPool
以GenericKeyedObjectPool为例
以GenericKeyedObjectPool为例的不同数据库连接对象池
Mysql 连接配置类
/**
* Mysql连接配置
*/
@Data
public class MysqlConfig {
private String host;
private Integer port = 3306;
private String username;
private String password;
private String database;
private Date startTime;
public MysqlConfig(String host, Integer port, String username, String password, String database) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.database = database;
this.startTime = new Date();
}
}
Mysql连接工厂
/**
* Mysql连接工厂
*/
@Slf4j
public class MysqlConnectionFactory implements KeyedPooledObjectFactory<MysqlConfig, Connection> {
@Override
public PooledObject<Connection> makeObject(MysqlConfig mysqlConfig) throws Exception {
System.out.println("makeObject: " + mysqlConfig.getHost());
Connection connection = null;
try {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://" + mysqlConfig.getHost() + ":" + mysqlConfig.getPort() + "/"
+ mysqlConfig.getDatabase() + "?characterEncoding=utf8&useSSL=true";
Class.forName(driver);
connection = DriverManager.getConnection(url,
mysqlConfig.getUsername(),
mysqlConfig.getPassword());
} catch (Exception e) {
log.error("MysqlConnectionFactory makeObject exception: {}.", e.toString());
}
if (!Objects.isNull(connection)) {
return new DefaultPooledObject<Connection>(connection);
}
return null;
}
@Override
public void destroyObject(MysqlConfig mysqlConfig, PooledObject<Connection> pooledObject) throws Exception {
System.out.println("destroyObject: " + mysqlConfig.getHost());
Connection conn = pooledObject.getObject();
if (Objects.isNull(conn)) return;
if (!conn.isClosed()) {
conn.close();
}
}
@Override
public boolean validateObject(MysqlConfig mysqlConfig, PooledObject<Connection> pooledObject) {
Connection conn = pooledObject.getObject();
boolean isColsed = false;
try {
isColsed = conn.isClosed();
} catch (SQLException e) {
isColsed = false;
}
boolean isInvalid = false;
if (!isColsed) {
Date startTime = mysqlConfig.getStartTime();
if (System.currentTimeMillis() - startTime.getTime() > 10 * 1000) {
isInvalid = true;
}
}
System.out.println("validateObject: " + mysqlConfig.getHost() + ",isclosed: " + isColsed + ", isInvalid: " + isInvalid + " [" + (System.currentTimeMillis() - mysqlConfig.getStartTime().getTime()) + "]");
if (!isColsed || isInvalid) {
try {
destroyObject(mysqlConfig, pooledObject);
} catch (Exception e) {
}
}
return !isColsed || isInvalid;
}
@Override
public void activateObject(MysqlConfig mysqlConfig, PooledObject<Connection> pooledObject) throws Exception {
System.out.println("activateObject: " + mysqlConfig.getHost());
// Connection conn = pooledObject.getObject();
// try {
// String driver = "com.mysql.jdbc.Driver";
// String url = "jdbc:mysql://" + mysqlConfig.getHost() + ":" + mysqlConfig.getPort() + "/"
// + mysqlConfig.getDatabase() + "?characterEncoding=utf8&useSSL=true";
// Class.forName(driver);
// conn = DriverManager.getConnection(url,
// mysqlConfig.getUsername(),
// mysqlConfig.getPassword());
// } catch (Exception e) {
// e.printStackTrace();
// }
}
@Override
public void passivateObject(MysqlConfig mysqlConfig, PooledObject<Connection> pooledObject) throws Exception {
//no method body
}
}
Mysql连接池
/**
* Mysql连接池
*/
public class MysqlConnectionPool extends GenericKeyedObjectPool<MysqlConfig, Connection> {
private static GenericKeyedObjectPool<MysqlConfig, Connection> pool = null;
public MysqlConnectionPool(KeyedPooledObjectFactory<MysqlConfig, Connection> factory, GenericKeyedObjectPoolConfig<Connection> config) {
super(factory, config);
}
public static GenericKeyedObjectPool<MysqlConfig, Connection> getInstance() {
if (Objects.isNull(pool)) {
initPool();
}
return pool;
}
public synchronized static void initPool() {
MysqlConnectionFactory factory = new MysqlConnectionFactory();
GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
config.setMaxIdlePerKey(10);
config.setMaxTotalPerKey(100);
config.setMaxTotal(500);
config.setMinIdlePerKey(10);
// config.setTestOnBorrow(true);
config.setTestOnReturn(true);
pool = new GenericKeyedObjectPool<>(factory, config);
}
}
测试:
每3秒会模拟即用归还连接,并打印pool的状态
public class Test {
public static void main(String[] args) throws Exception {
simulate();
printPoolInfo();
}
private static void simulate() throws Exception {
Thread t = new Thread() {
public void run() {
GenericKeyedObjectPool<MysqlConfig, Connection> pool = MysqlConnectionPool.getInstance();
MysqlConfig[] mysqlConfigArr = {new MysqlConfig("192.168.2.37", 3306, "root", "pigoss", "pigoss"),
new MysqlConfig("192.168.2.219", 3306, "root", "pigoss", "pigoss")};
int i = 0;
while (true) {
MysqlConfig mysqlConfig = mysqlConfigArr[i % 2];
Connection connection = null;
try {
connection = pool.borrowObject(mysqlConfig);
} catch (Exception e) {
e.printStackTrace();
}
ResultSet result = null;
query(connection, "select count(*) from tuser;");
pool.returnObject(mysqlConfig, connection);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
i++;
}
}
};
t.start();
}
private static void printPoolInfo() {
Thread t = new Thread() {
public void run() {
while (true) {
GenericKeyedObjectPool<MysqlConfig, Connection> pool = MysqlConnectionPool.getInstance();
synchronized (pool){
System.out.println("============");
System.out.println("CreatedCount: " + pool.getCreatedCount());
System.out.println("BorrowedCount: " + pool.getBorrowedCount());
System.out.println("ReturnedCount: " + pool.getReturnedCount());
System.out.println("DestroyedCount: " + pool.getDestroyedCount());
System.out.println();
}
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
private static void query(Connection connection, String sql) {
ResultSet result = null;
try {
PreparedStatement ps = connection.prepareStatement(sql);
result = ps.executeQuery();
while (result.next()) {
System.out.println("user count: " + result.getString(1));
}
} catch (Exception e) {
}
}
}
测试结果
makeObject: 192.168.2.37
activateObject: 192.168.2.37
user count: 4
validateObject: 192.168.2.37,isclosed: false, isInvalid: false [373]
destroyObject: 192.168.2.37
makeObject: 192.168.2.219
activateObject: 192.168.2.219
user count: 9
validateObject: 192.168.2.219,isclosed: false, isInvalid: false [3654]
destroyObject: 192.168.2.219
============
CreatedCount: 2
BorrowedCount: 2
ReturnedCount: 2
DestroyedCount: 0
NumIdle: 2