danga.memcached2.0.1存在nio管道句柄泄漏问题

某次,服务器出现故障,分析日志:
最先开始的是danga.memcached报”too many open files”,句柄数达到上限(ulimit上限已经是加大的), 导致danga.memcached无法创建新的套接字,连接缓存服务器失败, 紧接着压力全部转移到数据库,数据库连接池用完,很快就撑不住, 因为数据库响应慢,线程占用率变高,接收请求的线程池也达到上限,很多请求被拒绝, 页面出现时而可以访问,时而访问不了,重启后恢复正常, lsof查看Java进程持有的句柄,大部分为pipe和event poll, 初步判断是danga.memcached中使用的nio管道未正确关闭。
为了进一步了解问题,分析源码。
首先来看danga是怎么获取套接字的:
SocketIO.getSocket()

SocketChannel sock = SocketChannel.open();
sock.socket().connect( new InetSocketAddress( host, port ), timeout );
return sock.socket(); // 直接拿到Socket使用,把SocketChannel的引用丢了,并且没有注册Selector

再来看应用调用API发生了什么:
MemCachedClient.get()

// 省略了不相关代码
sock.write( cmd.getBytes() );
String line = sock.readLine();

内部调用关系等价于:

// sock.write( cmd.getBytes() ); 等价于
out = new BufferedOutputStream( sock.getOutputStream() ); // 把nio当bio用
out.write( b );
// String line = sock.readLine(); 等价于
in  = new DataInputStream( sock.getInputStream() ); // 把nio当bio用
in.read( b );

再看看它是怎么关闭的:

SockIO.trueClose()
// 省略了不相关代码
in.close();
out.close();
sock.close(); // Soket内部会同时关闭Channel,但不会关闭Selector

这种把非阻塞式管道当作阻塞式套接字用法,我们来看看SunJDK是怎么处理的:
SocketChannelImpl.socket().connect()最终调用的是:
SocketAdaptor.connect()

// 省略了部分代码
sel = Util.getTemporarySelector(sc); // 当没有注册Selector时,JDK会获取一个临时Selector
sk = sc.register(sel, SelectionKey.OP_CONNECT);

再看看Util.getTemporarySelector()怎么实现的:

// 省略了部分代码
private static ThreadLocal localSelector = new ThreadLocal(); // 放在ThreadLocal中缓存
SoftReference ref = (SoftReference)localSelector.get(); // 并使用了软引用
sel = sc.provider().openSelector();
localSelector.set(new SoftReference(new SelectorWrapper(sel)));
return sel;

通过上面的代码,基本结论是:
danga.memcached2.0.1使用nio非阻塞式管道, 但却把非阻塞式管道当作阻塞式套接字用,没有注册Selector, JDK会注册一个临时的Selector,使用Soft引用,并使用ThreadLocal做缓存,当线程池剧烈收缩和扩张时,就会出现很多垃圾Soft引用的Selector,如果内存够用,这些Soft引用都不会释放,句柄数就有可能达到上限。
而danga.memcached1.6.0使用的是常规阻塞式套接字,性能稍慢,但资源管理都比较正确,降级使用1.6.0版本后,服务器恢复正常,但1.6.0版本存在memcached服务器重启后,客户端无法自动恢复连接的问题,需将连接超时设置足够短(50ms以内)。
另外,将接收请求的线程池最大值和最小值设为一样,固定大小,避免收缩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值