`
javatar
  • 浏览: 1700695 次
  • 性别: Icon_minigender_1
  • 来自: 杭州699号
社区版块
存档分类
最新评论

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

阅读更多
转于自己在公司的Blog:
http://pt.alibaba-inc.com/wp/experience_766/danga_memcached_nio_leak.html

某次,服务器出现故障,分析日志:
最先开始的是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以内)。
另外,将接收请求的线程池最大值和最小值设为一样,固定大小,避免收缩。
分享到:
评论
7 楼 thinkingmysky 2013-12-06  
楼主分析的很好,透彻,只是分析的不全,而且部分好像还是错误的呢!如下代码:

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

其实,这个有可能就根本没有执行,就是说非阻塞通道,没有指定selector,也没有使用默认的selector!

具体看connect方法代码如下:

public void connect(SocketAddress paramSocketAddress, int paramInt) throws IOException {
/*  69 */     if (paramSocketAddress == null)
/*  70 */       throw new IllegalArgumentException("connect: The address can't be null");
/*  71 */     if (paramInt < 0) {
/*  72 */       throw new IllegalArgumentException("connect: timeout can't be negative");
/*     */     }
/*  74 */     synchronized (this.sc.blockingLock()) {
/*  75 */       if (!(this.sc.isBlocking())) {
/*  76 */         throw new IllegalBlockingModeException();
/*     */       }
/*     */       try
/*     */       {
/*  80 */         if (paramInt == 0) {
/*  81 */           this.sc.connect(paramSocketAddress);
/*  82 */           monitorexit; return;
/*     */         }
/*     */
/*  86 */         SelectionKey localSelectionKey = null;
/*  87 */         Selector localSelector = null;
/*  88 */         this.sc.configureBlocking(false);
/*     */         try {
/* ====================================================================== */
/*  90 */           if (this.sc.connect(paramSocketAddress)) { jsr 158;
/*     */             return; }
/* ====================================================================== */
/*  92 */           localSelector = Util.getTemporarySelector(this.sc);
/*  93 */           localSelectionKey = this.sc.register(localSelector,;
/*  94 */           long l1 = paramInt;
/*     */           while (true) {
/*  96 */             if (!(this.sc.isOpen()))
/*  97 */               throw new ClosedChannelException();
/*  98 */             long l2 = System.currentTimeMillis();
/*  99 */             int i = localSelector.select(l1);
/* 100 */             if ((i > 0) && (localSelectionKey.isConnectable()) && (this.sc.finishConnect())) {
/*     */               break;
/*     */             }
/* 103 */             localSelector.selectedKeys().remove(localSelectionKey);
/* 104 */             l1 -= System.currentTimeMillis() - l2;
/* 105 */             if (l1 <= 0L) {
/*     */               try {
/* 107 */                 this.sc.close(); } catch (IOException localIOException) {
/*     */               }
/* 109 */               throw new SocketTimeoutException();
/*     */             }
/*     */           }
/*     */         } finally {
/* 113 */           jsr 6; } localObject2 = returnAddress; if (localSelectionKey != null)
/* 114 */           localSelectionKey.cancel();
/* 115 */         if (this.sc.isOpen())
/* 116 */           this.sc.configureBlocking(true);
/* 117 */         if (localSelector != null)
/* 118 */           Util.releaseTemporarySelector(localSelector); ret;
/*     */       }
/*     */       catch (Exception localException)
/*     */       {
/* 122 */         Net.translateException(localException, true);
/*     */       }
/*     */     }
/*     */   }

仔细看的会发现,如果它使用原始的socket连接成功,就根本不会使用临时默认的selector了!

楼主是否确认?
6 楼 bukebuhao 2012-08-23  
javatar 写道
bukebuhao 写道
楼主,问个问题,memchached设计为session服务,是否要设置失效时间,1.如果不设置失效时间,就需要一个进程定时去清理失效的用户信息;2.如果设置失效时间,例如利用filter,每次请求就replace,更新一下失效时间。不过,第2种,服务器网络开销大些,导致很多缓存失效,不到失效时间就失效啦。不知道楼主对利用memchached做为session服务,有什么好的建议和措施?


可以考虑用第二种方式,用redis存储,直接用expire指令。

谢谢,我尝试一下。
5 楼 javatar 2012-08-23  
bukebuhao 写道
楼主,问个问题,memchached设计为session服务,是否要设置失效时间,1.如果不设置失效时间,就需要一个进程定时去清理失效的用户信息;2.如果设置失效时间,例如利用filter,每次请求就replace,更新一下失效时间。不过,第2种,服务器网络开销大些,导致很多缓存失效,不到失效时间就失效啦。不知道楼主对利用memchached做为session服务,有什么好的建议和措施?


可以考虑用第二种方式,用redis存储,直接用expire指令。
4 楼 bukebuhao 2012-08-22  
楼主,问个问题,memchached设计为session服务,是否要设置失效时间,1.如果不设置失效时间,就需要一个进程定时去清理失效的用户信息;2.如果设置失效时间,例如利用filter,每次请求就replace,更新一下失效时间。不过,第2种,服务器网络开销大些,导致很多缓存失效,不到失效时间就失效啦。不知道楼主对利用memchached做为session服务,有什么好的建议和措施?
3 楼 wenson 2010-04-27  
最近也遇到了這個問題,不過現在danga.memcached2.5.1版本出來了,修正了這個問題。
2 楼 dennis_zane 2010-01-21  
不过这个是sun的jdk这样实现,其他jdk可能未必如此。
1 楼 dennis_zane 2010-01-21  
非阻塞connect的时候确实会在ThreadLocal创建selector,因此如果连接池没有限制的话很容易超过文件句柄限制。

相关推荐

    danga memcached使用

    **标题:“danga memcached使用”** memcached是一款高性能、分布式内存对象缓存系统,它广泛应用于Web应用中,用于减轻数据库的负载,通过在内存中缓存数据和对象来减少读取数据库的次数。Danga是memcached的原始...

    memcached_net.spy_com.danga JAR

    Memcache java jar 包 java_memcached-release_2.5.1.jar import com.danga.MemCached.MemCachedClient; import net.spy.memcached.MemcachedClient;

    java_memcached-release_2.5.1.jar Memcache java jar 包

    2. **缓存操作**:通过简单的API,开发者可以设置、获取、删除或检查缓存在Memcached中的键值对。这些操作通常都是原子性的,确保了数据的一致性。 3. **序列化与反序列化**:Java Memcached客户端库通常会处理对象...

    memcached在JAVA中调用的DEMO

    memcached在JAVA中调用的DEMO,直接导入项目,无需新建工程。前提是必须按安装memcached,并启动memcached server服务

    memcached-2.4.2.jar memcached-2.4.2.jar

    使用memcached 方法 package com.war.common.util; import com.danga.MemCached.*; import java.util.Date; public class MemCached { protected static MemCachedClient mcc; protected static Logger log; ...

    memcached使用说明.doc

    【Memcached 使用说明】 Memcached 是一套由 danga.com(LiveJournal 技术团队)开发的分布式内存对象缓存系统,旨在减少数据库负载并提升应用性能。它通过将数据存储在内存中,使得高并发环境下的数据访问更为快速...

    java-memcached-2.6.6.jar

    java-memcached-2.6.6.jar

    计算机后端-PHP视频教程. Memcachedmemcached(day1).pdf

    Memcached由Danga Interactive公司开发,最初服务于LiveJournal,现在已经成为一种开源、高性能、分布式的内存对象缓存系统。它的工作原理是将数据存储在内存中,避免了频繁读取硬盘带来的I/O延迟,从而提高了数据...

    java使用memcached的实例

    在IT领域,尤其是在JavaWeb开发中,Memcached作为一种高性能、分布式的内存对象缓存系统,被广泛用于加速动态网络应用的加载速度。本文将详细解析如何在Java项目中使用Memcached,包括其配置方法以及一个具体的示例...

    memcached安装和使用详细说明

    当检测到特定的查询操作时,可以先尝试从Memcached中获取数据,如果不存在则从数据库查询并存入缓存。这种方式可以显著提高查询速度,尤其是在高并发场景下。 **总结** Memcached作为一个高效的分布式缓存系统,...

    memcached.jar

    com.danga package 下的 memcached.jar

    memcached客户端(三种客户端)

    在Java开发中,有三种主流的Memcached客户端库供开发者选择:官方的基于传统阻塞IO的客户端、Dustin Sallings实现的基于Java NIO的Spymemcached以及XMemcached。 1. **官方客户端** 官方提供的客户端是由Greg ...

    memcached1

    最近一直在做一个项目的前期设计工作,考虑到后期系统的扩展和性能问题也找了很多解决方法,有一个就是用到了数据库的缓存工具memcached(当然该工具并不仅仅局限于数据库的缓存)。先简单的介绍下什么是memcached。...

    memcached-library, 一个与Memcached缓存系统接口的CodeIgniter库.zip

    library, 一个与Memcached缓存系统接口的CodeIgniter库 用于CodeIgniter的#Memcached 库##Description这个库是为了帮助一个基于CodeIgniter的项目与 1或者更多的Memcached服务器( http://www.danga.com/memcached

    memcached代码分析详解.docx

    memcached是由Danga Interactive开发的一个开源项目,旨在提高动态Web应用的性能。它是一个基于内存的键值存储系统,用于临时存储(非持久化)中小规模的数据,如网页内容、数据库查询结果等。memcached通过在服务器...

Global site tag (gtag.js) - Google Analytics