package org.rx.socks;

import lombok.extern.slf4j.Slf4j;
import org.rx.common.LogWriter;
import org.rx.common.NQuery;
import org.rx.beans.DateTime; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket; import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque; import static org.rx.common.Contract.require; @Slf4j
public final class SocketPool extends Traceable implements AutoCloseable {
public static final class PooledSocket implements AutoCloseable {
private final SocketPool owner;
private DateTime lastActive;
public final Socket socket; public boolean isConnected() {
return !owner.isClosed() && !socket.isClosed() && socket.isConnected();
} public DateTime getLastActive() {
return lastActive;
} public void setLastActive(DateTime lastActive) {
this.lastActive = lastActive;
} private PooledSocket(SocketPool owner, Socket socket) {
this.owner = owner;
this.socket = socket;
lastActive = DateTime.utcNow();
} @Override
public void close() {
owner.returnSocket(this);
}
} public static final SocketPool Pool = new SocketPool();
private static final int DefaultConnectTimeout = 30000;
private static final int DefaultMaxIdleMillis = 120000;
private static final int DefaultMaxSocketsCount = 64;
private final ConcurrentHashMap<InetSocketAddress, ConcurrentLinkedDeque<PooledSocket>> pool;
private volatile int connectTimeout;
private volatile int maxIdleMillis;
private volatile int maxSocketsCount;
private final Timer timer;
private volatile boolean isTimerRun; public int getConnectTimeout() {
return connectTimeout;
} public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
} public int getMaxIdleMillis() {
return maxIdleMillis;
} public void setMaxIdleMillis(int maxIdleMillis) {
if (maxIdleMillis <= 0) {
maxIdleMillis = DefaultMaxIdleMillis;
}
this.maxIdleMillis = maxIdleMillis;
} public int getMaxSocketsCount() {
return maxSocketsCount;
} public void setMaxSocketsCount(int maxSocketsCount) {
if (maxSocketsCount < 0) {
maxSocketsCount = 0;
}
this.maxSocketsCount = maxSocketsCount;
} private SocketPool() {
pool = new ConcurrentHashMap<>();
connectTimeout = DefaultConnectTimeout;
maxIdleMillis = DefaultMaxIdleMillis;
maxSocketsCount = DefaultMaxSocketsCount;
String n = "SocketPool";
timer = new Timer(n, true);
LogWriter tracer = new LogWriter();
tracer.setPrefix(n + " ");
tracer.info("started..");
setTracer(tracer);
} @Override
protected void freeObjects() {
clear();
} private void runTimer() {
if (isTimerRun) {
return;
}
synchronized (timer) {
if (isTimerRun) {
return;
} long period = 90000;
timer.schedule(new TimerTask() {
@Override
public void run() {
clearIdleSockets();
}
}, period, period);
isTimerRun = true;
}
getTracer().info("runTimer..");
} private void clearIdleSockets() {
for (Map.Entry<InetSocketAddress, ConcurrentLinkedDeque<PooledSocket>> entry : NQuery.of(pool.entrySet())) {
ConcurrentLinkedDeque<PooledSocket> sockets = entry.getValue();
if (sockets == null) {
continue;
} for (PooledSocket socket : NQuery.of(sockets)) {
if (!socket.isConnected()
|| DateTime.utcNow().subtract(socket.getLastActive()).getTotalMilliseconds() >= maxIdleMillis) {
sockets.remove(socket);
getTracer().info("clear idle socket[local=%s, remote=%s]..",
Sockets.getId(socket.socket, false), Sockets.getId(socket.socket, true));
}
}
if (sockets.isEmpty()) {
pool.remove(entry.getKey());
}
}
if (pool.size() == 0) {
stopTimer();
}
} private void stopTimer() {
synchronized (timer) {
timer.cancel();
timer.purge();
isTimerRun = false;
}
getTracer().info("stopTimer..");
} private ConcurrentLinkedDeque<PooledSocket> getSockets(InetSocketAddress remoteAddr) {
ConcurrentLinkedDeque<PooledSocket> sockets = pool.get(remoteAddr);
if (sockets == null) {
pool.put(remoteAddr, sockets = new ConcurrentLinkedDeque<>());
runTimer();
}
return sockets;
} public PooledSocket borrowSocket(InetSocketAddress remoteAddr) {
checkNotClosed();
require(remoteAddr); boolean isExisted = true;
ConcurrentLinkedDeque<PooledSocket> sockets = getSockets(remoteAddr);
PooledSocket pooledSocket;
if ((pooledSocket = sockets.pollFirst()) == null) {
Socket sock = new Socket();
try {
sock.connect(remoteAddr, connectTimeout);
} catch (IOException ex) {
throw new SocketException(remoteAddr, ex);
}
pooledSocket = new PooledSocket(this, sock);
isExisted = false;
}
if (!pooledSocket.isConnected()) {
if (isExisted) {
sockets.remove(pooledSocket);
}
return borrowSocket(remoteAddr);
}
Socket sock = pooledSocket.socket;
getTracer().info("borrow %s socket[local=%s, remote=%s]..", isExisted ? "existed" : "new",
Sockets.getId(sock, false), Sockets.getId(sock, true));
return pooledSocket;
} public void returnSocket(PooledSocket pooledSocket) {
checkNotClosed();
require(pooledSocket); String action = "return";
try {
if (!pooledSocket.isConnected()) {
action = "discard closed";
return;
}
pooledSocket.setLastActive(DateTime.utcNow());
ConcurrentLinkedDeque<PooledSocket> sockets = getSockets(
(InetSocketAddress) pooledSocket.socket.getRemoteSocketAddress());
if (sockets.size() >= maxSocketsCount || sockets.contains(pooledSocket)) {
action = "discard contains";
return;
} sockets.addFirst(pooledSocket);
} finally {
Socket sock = pooledSocket.socket;
getTracer().info("%s socket[local=%s, remote=%s]..", action, Sockets.getId(sock, false),
Sockets.getId(sock, true));
}
} public void clear() {
checkNotClosed(); for (Socket socket : NQuery.of(pool.values()).selectMany(p -> p).select(p -> p.socket)) {
try {
getTracer().info("clear socket[local=%s, remote=%s]..", Sockets.getId(socket, false),
Sockets.getId(socket, true));
Sockets.close(socket);
} catch (Exception ex) {
log.error("SocketPool clear", ex);
}
}
pool.clear();
}
}

java 简单 SocketPool的更多相关文章

  1. java简单词法分析器(源码下载)

    java简单词法分析器 : http://files.cnblogs.com/files/hujunzheng/%E7%AE%80%E5%8D%95%E8%AF%8D%E6%B3%95%E5%88%8 ...

  2. !!转!!java 简单工厂模式

    举两个例子以快速明白Java中的简单工厂模式: 女娲抟土造人话说:“天地开辟,未有人民,女娲抟土为人.”女娲需要用土造出一个个的人,但在女娲造出人之前,人的概念只存在于女娲的思想里面.女娲造人,这就是 ...

  3. JAVA简单Swing图形界面应用演示样例

    JAVA简单Swing图形界面应用演示样例 package org.rui.hello; import javax.swing.JFrame; /** * 简单的swing窗体 * @author l ...

  4. 多元线性回归----Java简单实现

    http://www.cnblogs.com/wzm-xu/p/4062266.html 多元线性回归----Java简单实现   学习Andrew N.g的机器学习课程之后的简单实现. 课程地址:h ...

  5. java简单数据类型转化

    java简单数据类型,有低级到高级为:(byte,short,char)→int→long→float→double (boolean不参与运算转化) 转化可以分为 低级到高级的自动转化 高级到低级的 ...

  6. 预防和避免死锁的方法及银行家算法的java简单实现

    预防死锁 (1) 摒弃"请求和保持"条件 基本思想:规定所有进程在开始运行之前,要么获得所需的所有资源,要么一个都不分配给它,直到所需资源全部满足才一次性分配给它. 优点:简单.易 ...

  7. Java简单聊天室

    实现Java简单的聊天室 所用主要知识:多线程+网络编程 效果如下图 /** * * @author Administrator * * 简单的多人聊天系统——重点:同时性,异步性 * 1.客户端:发 ...

  8. Java简单工厂模式

    Java简单工厂模式 在阎宏博士的<JAVA与模式>一书中开头是这样描述简单工厂模式的:简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式.简 ...

  9. java简单web爬虫(网页图片)

    java简单web爬虫(网页图片)效果,执行main()方法后图片就下载道C盘的res文件夹中.没有的话创建一个文件夹代码里的常量根据自己的需求修改,代码附到下面. package com.sinit ...

随机推荐

  1. prototype、proto和constructor 关系

    记录: 1.构造函数:如下,Foo()就是一个构造函数 function Foo(){} 2.prototype:每一个构造函数都有一个属性叫prototype,相当于一个指针,指向它的原型对象 Fo ...

  2. scrapy入门实战-爬取代理网站

    入门scrapy. 学习了有这几点 1.如何使用scrapy框架对网站进行爬虫: 2.如何对网页源代码使用xpath进行解析: 3.如何书写spider爬虫文件,对源代码进行解析: 4.学会使用scr ...

  3. Apache2.2+mod_encoding解决URL中文编码问题

    我们经常在论坛上看到这样的求救贴: 为什么我看不了网站上中文文件名的文件?这时一定会有好心的大侠告诉说,到IE6的工具,Internet选项, 高级里,把"总是以UTF-8发送URL&quo ...

  4. spring+freemarker 乱码解决办法

    这样应该可以了~ <!-- freemarker config --> <bean id="freemarkerConfig" class="org.s ...

  5. Push to GitHub:could not resolve host: github.com

    系统:Mac os x 10.11.3 操作:Push to GitHub 错误如下: git push origin ssh: Could not resolve hostname ssh.gith ...

  6. SSO 实现博客系统的单点登录

    https://blog.csdn.net/qq1350048638/article/details/78933375 https://blog.csdn.net/yejingtao703/artic ...

  7. cocos2d之创建自己的场景类

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. 1. 首先创建.h的头文件,然后在将一些图片声音素材加到resource文件夹内,最后在创建.cpp文件:         .h头文件中创 ...

  8. Python 如何debug

    一.常见错误: 1.漏了末尾的冒号,如 if语句,循环语句,定义函数 2.缩进错误,该缩进的时候没有缩进 3.把英文符号写成中文符号,如:  ' ' () , 4.字符串拼接,把字符串和数字拼接一起 ...

  9. 27-python基础-python3-异常处理(try except)

    到目前为止,在 Python 程序中遇到错误,或“异常”,意味着整个程序崩溃.不希望这发生在真实世界的程序中. 相反,希望程序能检测错误,处理它们,然后继续运行.   实例1: 当试图用一个数除以零时 ...

  10. 12-vim-撤销和删除命令-02-删除文本

    删除文本 命令 英文 功能 x cut 删除光标所在字符 d(移动命令) delete 删除移动命令对应的内容 dd delete 删除光标所在行 D delete 从光标位置删除至行尾 注: 如果使 ...