/** * */ 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; } }