Jetty6 Continuation

针对大量并发连接挑战,Jetty 6 引入 Continuation 特性。该特性允许中断 AJAX 请求并释放线程,直到有新事件或超时再恢复处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[i]译注:项目中用到Jetty6的Continuation机制,翻译一篇专业文章用于学习此机制[/i]
[url]http://docs.codehaus.org/display/JETTY/Continuations[/url]

[size=medium][b]背景资料[/b]
对于当前的很多网络应用,并行访问的用户数会远远超过服务器上的连接数。这是由于在用户与服务器会话中,当用户在做阅读内容或是填写表格等动作时,连接可能会因为这些暂停被关闭。所以成千上万的用户可以由数百个连接来提供服务。

但是基于AJAX的应用与传统网络应用相比有不同的业务模型(Traffic profiles)。当一个用户在填写表格的时,AJAX会请求服务器获取输入数据验证和提示信息;当一个用户在读网页内容时,AJAX请求可能会去异步获取新闻或是更新网页内容。如此一个AJAX应用,它需要与服务器不间断地连接,这样前面所说的“并行访问用户数可能会远远超过服务器并发TCP/IP连接数”的情况就不会存在了。

这样,如果有几千位用户,你就需要几千个连接;如果有数以万计的用户,你也需要数以万计的并发连接。这对Java WEB容器来说是个巨大挑战,它必须可以处理大量的并发连接。同时,对于你的应用系统,从操作系统到JVM都需要不断地被关注着。

[b]为每个连接分配一个线程(Thread-per-Connect)[/b]
构建一个可扩展的Servlet服务器的最主要问题之一是如果处理线程(Thread)和连接(Connection)。传统的Java IO模型为每个TCP/IP连接关联一个线程。如果你有少量非常活跃的线程,这个模型可以扩展到每秒处理大量请求。

然而在很多WEB应用中有这样的业务模型,当用户在阅读网页或是查找链接的时候,很多HTTP长连接几乎是空闲的。在这样的模型中,“每个连接分配一个线程”的模型在大规模部署时很难让几千线程同时支持几千用户请求。

[b]为每个请求分配一个线程(Thread-per-Request)[/b]
采用异步IO的NIO包可以解决这个问题,它在服务器处理请求时为每个连接分配一个线程。当在两次请求之间连接空闲的时候,线程就会返回到线程池中,并且这个连接会被放入一个NIO选择组(Select set)中去侦听新的请求。“每个请求分配一个线程”的模型对服务器来说,在损失每秒最大请求数的同时可以允许更多的连接。

[b]AJAX轮询问题[/b]
还有一个新的问题。AJAX作为一个WEB应用模型的出现大大改变了服务器端的业务模型。由于AJAX服务器不能将事件异步地通知到AJAX客户端,所以AJAX客户端必须去服务器端轮询等待AJAX服务器端事件。为了避免频繁轮询,AJAX服务器常常持有这次轮询请求,当超时或是有新事件时才释放它。这样AJAX服务器为了当新事件到来的时候发送请求到客户端,就应当持有客户端的一个请求,而这时客户端是空闲的。这种技术思路挺好,但破坏了“每个请求分配一个线程”的模型,因为对于每个客户端在服务器端都有一个未完成的请求。这样每个服务器为每个客户端需要一个或多个线程,在成千上万个用户时就会存在问题。
[img]http://dl.iteye.com/upload/attachment/275146/30c97565-9c83-3ee3-80f0-63a37853e9dc.jpg[/img]

[b]Jetty6 Continuation[/b]
上述问题的解决方法就是Jetty6中引入的新特性--Continuation。当一个Java filter或是Servlet处理一个AJAX请求,可以使用Continuation对象有效地中断(suspend)请求并释放当前的线程。当中断超时或是在Continuation对象上调用resume方法时,这个请求会被唤醒(resume)。Jett6的聊天室Demo中,有下面的这些代码用于处理AJAX对服务器事件的轮询:[/size]

private void doPoll(HttpServletRequest request, AjaxResponse response)
{
HttpSession session = request.getSession(true);

synchronized (mutex)
{
Member member = (Member)chatroom.get(session.getId());

// Is there any chat events ready to send?
if (!member.hasEvents())
{
// No - so prepare a continuation
Continuation continuation = ContinuationSupport.getContinuation(request, mutex);
member.setContinuation(continuation);

// wait for an event or timeout
continuation.suspend(timeoutMS);
}
member.setContinuation(null);

// send any events
member.sendEvents(response);
}
}


[size=medium] 所以正在处理的请求被中断用于等待可以接受的聊天事件。当其它用户在聊天室中说话的时候,这种事件会由另外一个线程通过调用下面方式来发送到每个接收方。[/size]

class Member
{
// ...
public void addEvent(Event event)
{
synchronized (mutex)
{
_events.add(event);
if (getContinuation()!=null)
getContinuation().resume();
}
}
// ...
}



[size=medium][b]Continuation如何工作[/b]
Java中没有中断一个线程并随后唤醒它的机制,所以Jetty在后台同Java与Servlet规范一道完成这样的功能。首先当请求处理器(Request Hander)调用continuation.suspend(timeoutMS)方法时,一个RetryRequest运行时异常被抛出。这个异常会跳过所有的请求处理代码,由Jetty捕获并对它做特殊处理。Jetty不会为这种情况产生错误响应,而是将这个请求(Request)放入一个超时队列(Timeout Queue)并将这个请求的执行线程释放回线程池中。

当超时期到了或是另外一个线程调用continuation.resume()方法时,那个请求就被取回。这时如果再调用continuation.suspend(timeoutMS)方法,这个方法要么返回null要么返回RetryRequest事件,请求处理器会按正常的流程产生对客户端的响应。

这个机制使用HTTP请求处理中的无状态性来模拟一个中断(suspend)和唤醒(resume)动作。那种运行时异常(RetryRequest异常)可以让线程合理地退出请求处理器(Request Handler),任何上游的filter或是servlet都可以添加一些相关的安全上下文(Security Context)。请求在取回时,会重新进入filter/servlet链和一些安全上下文并在continuation的点上重新执行正常处理。

另外,Continuation的API是简明的。如果它运行在一个非Jetty服务器上,它可以在getEvent时使用简单的wait/notify去中断请求。如果Continuation是按我想的这样在工作,我计划在Servlet3.0中支持它。[/size]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值