我有一个Jedis服务器,我已经制作了一个单独的RedisManager来管理jedis连接。 RedisManager的代码如下
package RedisServerPackage;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisManager {
private static final RedisManager instance = new RedisManager();
private static final JedisPoolConfig poolConfig= new JedisPoolConfig();
private static JedisPool pool = null;
private RedisManager() {}
public final static RedisManager getInstance() {
if(pool == null)
{
poolConfig.setMaxTotal(-1);
pool = new JedisPool(poolConfig,"localhost");
}
return instance;
}
public void release() {
pool.destroy();
}
public Jedis getJedis() {
return pool.getResource();
}
public void returnJedis(Jedis jedis) {
pool.returnResource(jedis);
}
}
现在我执行我的代码,我有大约1000个客户端命中服务器并使用PubSub模型执行某些操作。我监控了redis-server,发现一次最多有45个客户端处于活动状态,最大阻塞客户端大约为39个。运行客户端代码大约5分钟后,我得到异常
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:50)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:88)
at RedisServerPackage.RedisManager.getJedis(RedisManager.java:31)
at RedisServerPackage.RedisQueue.dequeue(RedisQueue.java:45)
at RedisServerPackage.QueueProcessor.run(QueueProcessor.java:22)
at java.lang.Thread.run(Thread.java:745)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Address already in use
at redis.clients.jedis.Connection.connect(Connection.java:148)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:75)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1572)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:69)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:861)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.util.Pool.getResource(Pool.java:48)
... 5 more
Caused by: java.net.ConnectException: Address already in use
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:142)
... 12 more
我无法找出导致此异常的原因。另外,我正在重用jedis实例。示例代码是
public void JedisExample(String temporaryString) {
Jedis jedis = manage.getJedis();
try {
// Some code here
} catch (Exception e) {
System.out.println(e);
}finally{
manager.returnJedis(jedis);
// manage is an instance of RedisManager class provided before.
}
}
答案 0 :(得分:0)
在尝试负载测试服务器应用程序时,我在MacOS上间歇性地发生了此异常。
事实证明,问题与以下事实有关:macOS只有16K端口可用,直到通过套接字TIME_WAIT才能释放。 TIME_WAIT的默认超时为15秒。
您可以通过
检查您的sysctl net.inet.tcp.msl
为了临时修复它以便进行负载测试,我使用了
sudo sysctl -w net.inet.tcp.msl=1000
这将TIME_WAIT减少到1秒,从而允许更快地创建和释放连接,这又使我能够让Tomcat以大约4000 qps的速率将REST请求转换为Redis PUBSUB消息,并在轰炸4小时后出现0个错误16个并发的攻城线程。以前,大约有1%的请求会出错,但上述情况除外。
问题的作者没有说明操作系统,但是我希望这个答案可能对遇到类似情况的其他人有所帮助,因为在Jedis中搜索此类异常时,该条目位于顶部。基本上,无论使用什么操作系统,在进行负载测试时都要检查TIME_WAIT。
更新
警告。 请勿在生产中使用它! 理想情况下,在工作站上进行负载测试后,将其增加到15秒。减少TIME_WAIT可能很危险,因为套接字在关闭后变得更快可用,并且一些延迟的数据包可能到达新打开的连接,从而导致无法预料的错误甚至损害安全性。在决定遵循上述说明或咨询网络工程师之前,请阅读有关TCP / IP和TIME_WAIT的更多信息。