首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

贪嘴的jetty被撑死了

2012-09-16 
贪吃的jetty被撑死了在大量请求并且请求处理时间较长的情况下,jetty的nio模式会导致容器运行缓慢。?测试方

贪吃的jetty被撑死了

在大量请求并且请求处理时间较长的情况下,jetty的nio模式会导致容器运行缓慢。

?

测试方法:

用apache ab对jetty容器发出大规模持续的并发请求,

用命令“jstat -gcutil -h 10 PID 1000"查看GC情况,等到young、old区到100%时停止施压。

?

用“jmap -histo PID | less" 可以看到大量的SelectChannelEndPoint对象。


分析一下原因:

首先介绍一下jetty的nio模式,如下图

贪嘴的jetty被撑死了

?

mainReactor:jetty从线程池中分配一个线程用于接受用户的连接请求(ServerSocketChannel.accpet()),

这个线程就做一件事,接受用户的连接,将channel注册到selector中。

?

_manager.dispatch(new Runnable(){public void run(){final ServerSocketChannel server=_acceptChannel;while (isRunning() && _acceptChannel==server && server.isOpen()){try{SocketChannel channel = server.accept();channel.configureBlocking(false);Socket socket = channel.socket();configure(socket);_manager.register(channel);}catch(IOException e){Log.ignore(e);}}}});
???

?

而jetty的subReactor线程询注册进来的channel,将channel包装成SelectChannelEndPoint对象加入到_endPoints。(可以把endpoint看作是一个连接)

?

private ConcurrentMap<SelectChannelEndPoint,Object> _endPoints = new ConcurrentHashMap<SelectChannelEndPoint, Object>();  public void doSelect() throws IOException {...    else if (change instanceof SocketChannel){// Newly registered channelfinal SocketChannel channel=(SocketChannel)change;SelectionKey key = channel.register(selector,SelectionKey.OP_READ,null);SelectChannelEndPoint endpoint = createEndPoint(channel,key);key.attach(endpoint);endpoint.schedule();}...}
?

?

为什么要加入到_endPoints,为了对所有的endpoint做空闲检查。

?

public void doSelect() throws IOException {。。。// Idle tickif (now-_idleTick>__IDLE_TICK){_idleTick=now;final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))?(now+_maxIdleTime-_lowResourcesMaxIdleTime):now;dispatch(new Runnable(){public void run(){for (SelectChannelEndPoint endp:_endPoints.keySet()){endp.checkIdleTimestamp(idle_now);}}});}。。。}
?

?

这里有个问题,回收动作需要从线程池中分配线程处理,而如果线程池中没有空闲的线程时,那么回收动作将无法正常进行。所以尝试修改__IDLE_TICK到30毫秒(默认是400),希望能提高空闲检查频率,却无法起效。

?

还有这个可恶的_endPoints对象,它将持有大量的endpoint,而这些endpoint又得不到及时处理,内存都被它消耗光。

?

我配置的最大线程池为250,任务队列长度无限制

?

<Set name="ThreadPool">  <!-- Default queued blocking threadpool -->  <New | wc -l"查看),但是很多都block住了;因为线程运行过程中也会有对象创建,也需要一点内存空间,可已经没有内存空间,杯具就这样发生了。

?

如果把mainReactor比作人在吃东西,那么subReator就是他的胃在消化,

大部分情况都是吃个7分饱,此时胃的消化能力很强,

一旦出现暴饮暴食,就会出现胃胀,消化能力反而减弱。


一个解决方案:

分析下来觉得jetty缺了胃反射功能,胃胀信息没有即使反馈给大脑。

可以适当扩展一下mainReactor,看下面的代码:

_manager.dispatch(new Runnable(){public void run(){final ServerSocketChannel server=_acceptChannel;while (isRunning() && _acceptChannel==server && server.isOpen() && !_manager.isLowResourcesConnections()){try{SocketChannel channel = server.accept();channel.configureBlocking(false);Socket socket = channel.socket();configure(socket);_manager.register(channel);}catch(IOException e){Log.ignore(e);}}}});

?添加了_manager.isLowResourcesConnections()方法,嘴巴准备吃的时候要先问一下胃先。

?

subReactor添加一个新方法:

public boolean isLowResourcesConnections() {// 这里的判断阀值是个大概的值。// 拿配置的阀值和第一个selector的keys大小做比较// 任何情况下第一个selector都是存在的,所以这个比较还是靠谱的。return _lowResourcesConnections < _selectSet[0].getSelector().keys().size();}
?

?

热点排行