/**
*
*/
package io.client.thrift.pool;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.NoSuchElementException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.SocketFactory;
/**
* 套接字连接池
*
* @author HouKangxi
*
*/
public class SocketConnectionPool extends SocketFactory {
public static final int DEFAULT_MAX_IDLE = 5;
public static final int DEFAULT_MAX_TOTAL = 5;
public static final long DEFAULT_MAX_WAIT = 8000;
public static final int DEFAULT_MAX_IDLE_TIME = 5000;
public static final int DEFAULT_IDLE_CHECK_GAP = 2000;
/**
* 真正负责创建连接的socketFactory--本类专注做连接池,把创建连接的细节抛给外部socketFactory
*/
private final SocketFactory socketFactory;
/**
* 连接池
*/
private final LinkedBlockingDeque<SocketWrapper> idleObjects;
/**
* 最大空闲连接数
*/
private final int maxIdle;
/**
* 池中最大连接数量
*/
private final int maxTotal;
/**
* 等待可用连接的最长时间(毫秒)
*/
private final long maxWaitMills;
/**
* 等待连接时是否阻塞
*/
private boolean blockWhenExhausted;
/**
* 是否后进先出--如果true,则归还的连接放在队列头部,否则放在末尾
*/
private volatile boolean lifo;
/**
* 负责清理空闲连接的定时器---默认清理超过15秒没有使用的连接,可通过maxIdleTime设置
*/
private Timer idleCheckTimer = new Timer("SocketConnectionIdelCheckTimer", true);
private final AtomicLong borrowedCount = new AtomicLong(0);
private final AtomicLong createdCount = new AtomicLong(0);
private final AtomicLong destroyedCount = new AtomicLong(0);
public SocketConnectionPool(SocketFactory socketFactory) {
this(socketFactory, DEFAULT_MAX_IDLE, DEFAULT_MAX_TOTAL, DEFAULT_MAX_WAIT, true, DEFAULT_MAX_IDLE_TIME);
}
/**
*
* @param socketFactory
* - 真正负责创建连接的socketFactory--本类专注做连接池,把创建连接的细节抛给外部socketFactory
* @param maxIdle
* @param maxTotal
* @param maxWaitMills
* @param blockWhenExhausted
* @param maxIdleTime
*/
public SocketConnectionPool(SocketFactory socketFactory, int maxIdle, int maxTotal, long maxWaitMills,
boolean blockWhenExhausted, final long maxIdleTime) {
if (socketFactory instanceof SocketConnectionPool) {
throw new IllegalArgumentException("socketFactory must not a SocketConnectionPool!");
}
this.socketFactory = socketFactory;
this.maxIdle = maxIdle;
this.maxTotal = maxTotal;
this.maxWaitMills = maxWaitMills;
this.blockWhenExhausted = blockWhenExhausted;
idleObjects = new LinkedBlockingDeque<>(maxTotal);
idleCheckTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
int size = idleObjects.size();
if (size < 1) {
return;
}
// toArray() copy 一个副本,避免
SocketWrapper[] scs = idleObjects.toArray(new SocketWrapper[size]);
for (int i = 0; i < scs.length; i++) {
SocketWrapper sc = scs[i];
if (sc != null && !sc.isWorking && System.currentTimeMillis() - sc.lastUseTime >= maxIdleTime) {
// System.out.println("try删除空闲太久的连接: " + sc);
destroy(sc);
}
}
}
}, 3000, DEFAULT_IDLE_CHECK_GAP);// 检查的开始时间 和 时间间隔--按需调整
}
public long getCreatedCount() {
return createdCount.get();
}
public LinkedBlockingDeque<SocketWrapper> getIdleObjects() {
// TODO just for debug
return idleObjects;
}
@Override
public Socket createSocket() throws IOException {
try {
return borrow();
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
}
throw e;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected SocketWrapper borrow() throws Exception {
SocketWrapper p = null;
while (p == null) {
if (blockWhenExhausted) {
p = idleObjects.pollFirst();
if (p == null) {
if (borrowedCount.get() > 0 && maxWaitMills > 0) {
p = idleObjects.pollFirst(maxWaitMills, TimeUnit.MILLISECONDS);
} else {
p = create();
}
}
if (p == null) {
if (maxWaitMills <= 0) {
p = idleObjects.takeFirst();
} else {
p = idleObjects.pollFirst(maxWaitMills, TimeUnit.MILLISECONDS);
}
}
if (p == null) {
throw new NoSuchElementException("Timeout waiting for idle object");
}
} else {
p = idleObjects.pollFirst();
if (p == null) {
if (borrowedCount.get() > 0 && maxWaitMills > 0) {
p = idleObjects.pollFirst(maxWaitMills, TimeUnit.MILLISECONDS);
} else {
p = create();
}
}
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
}
}
p.isWorking = true;
p.lastUseTime = System.currentTimeMillis();
// System.out.println("borrow socket: " + p);
borrowedCount.incrementAndGet();
return p;
}
protected SocketWrapper create() throws IOException {
long created = createdCount.incrementAndGet();
if ((maxTotal > -1 && created > maxTotal) || created > Integer.MAX_VALUE) {
createdCount.decrementAndGet();
return null;
}
Socket socket = null;
try {
socket = socketFactory.createSocket();
} catch (IOException e) {
createdCount.decrementAndGet();
throw e;
} catch (Exception ex) {
createdCount.decrementAndGet();
throw new RuntimeException(ex);
} catch (Throwable ex) {
createdCount.decrementAndGet();
throw new IOException(ex);
}
return new SocketWrapper(socket, this);
}
/**
* 归还到连接池
*
* @param socketWrapper
*/
public void returnToPool(SocketWrapper p) {
// System.out.println("try returnToPool:" + p);
synchronized (p) {
if (!p.isWorking) {
throw new IllegalStateException("Object has already been returned to this pool or is invalid");
} else {
borrowedCount.decrementAndGet();
}
}
if (maxIdle > -1 && maxIdle <= idleObjects.size()) {
destroy(p);
} else {
p.isWorking = false;
if (lifo) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
}
}
/**
* 销毁连接
*
* @param socketWrapper
*/
protected void destroy(SocketWrapper socketWrapper) {
if (socketWrapper == null || socketWrapper.target == null) {
return;
}
synchronized (socketWrapper) {
// System.out.println("try destroy :" + socketWrapper);
if (!socketWrapper.target.isClosed()) {
socketWrapper.isWorking = false;
try {
socketWrapper.target.close();
} catch (IOException e) {
e.printStackTrace();
}
destroyedCount.incrementAndGet();
createdCount.decrementAndGet();
idleObjects.remove(socketWrapper);
}
}
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return null;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return null;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return null;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return null;
}
}