HTTP的请求一般来说都是同步的请求。但是在某些情况下,同步的请求处理无法很好地解决一些问题,这时候需要异步的请求处理(Asynchronous Request Processing,ARP)。GlassFish在其HTTP引擎Grizzly的内部就实现了异步请求处理,使得异步请求处理的速度与同步请求处理一样迅速快捷。在ARP的基础之上,Grizzly还实现了一个服务器推送技术的引擎Comet。这个引擎满足了日益流行的AJAX应用对服务器连接资源的消耗,使得GlassFish成为部署AJAX应用最理想的平台之一。
本章重点:
l 异步请求处理的概念
l Grizzly的异步请求处理的实现方式
l 编写和部署异步请求处理的应用
l 服务器推送技术Comet的概念
l Grizzly中Comet的实现方式
l 编写和部署Comet的应用
18.1 异步请求处理
Web请求一般来说都是同步的。如图18-1所示,浏览器发出HTTP的请求,然后等待Web服务器的响应,并将返回结果显示给用户。

图18-1 同步的HTTP请求
当然,在Java EE的标准范围中已经包括了许多异步服务的技术。例如JMS或是Java Mail。但是这不是本章要讨论的范围。因为这些异步服务都集中在服务器端执行,并不涉及到与客户端浏览器的交互。在图18-2中,浏览器发送一个请求给服务器,服务器在执行请求的过程中向JMS服务器发送一个异步的消息,然后返回给客户端一个“消息已经发送”的页面。虽然在这个请求当中有异步服务的处理(JMS),但是对于客户端来说还是同步的,它一直会等到返回结果才能显示给用户。

图18-2 异步的服务器后端请求
那么,什么才能算是Web端的异步请求服务?为什么需要Web端的异步请求服务呢?事实上HTTP协议的同步请求模型在一些特殊的情况下并不十分适合。在以下的场景中,同步请求服务会遇到困难:
l 在执行Web请求服务的过程中,需要调用外部的服务,并且这个服务的完成可能需要很长的时间,因此浏览器可能会等待很长的时间才能显示。
l 当前Web请求被卷入一个工作流的过程,这个工作流可能需要外部的介入才能完成,例如需要经理的批准。
事实上这些情况在实际需求中经常出现,而通常的解决方案是用同步请求的模式来改造这些异步请求的流程:将原有的处理流程拆分成两个,一个是业务处理,一个是状态查询。业务处理可以在服务器端背景运行,不需要立刻返回。业务处理在运行的时候会更新当前的状态。而状态查询的请求可用同步的方式获得当前状态的一个快照。
用一个简单的例子(这个例子也是下文中详细介绍的内容)来说,如果设计一个Web邮件阅读器(例如Google Mail),就会有上面的问题。当邮件服务器有新的邮件的时候,一般来说浏览器是无法知道的,只有重新刷新页面才能知道新的状态。有些界面友好的Web邮件阅读器会利用AJAX或iFrame技术自动刷新,让用户感觉到好像浏览器能够实时地获得服务器的状态。实际上每次刷新都是一个新的HTTP请求,每次请求都会执行服务器端的代码去检查有没有新的邮件。如果邮件不是很频繁,在大多数情况下,这些刷新的请求都在执行无用的操作:检查有没有邮件,发现没有新邮件就会返回。如果并发用户很多,就会给服务器带来额外的压力和消耗。另外,显示新邮件的逻辑被拆分成两个过程,一是检查信箱的状态(是否有新邮件),二是显示新邮件(如果有的话)。这使得编程的逻辑变得更加复杂。
Web异步请求处理就是为了解决这个问题。在异步请求处理的模式下,浏览器只需要发出“显示新邮件”的HTTP请求。但是这些请求在服务器端并不是马上就去执行,而是当满足一些条件或有特定的事件触发(有新邮件到达)才去执行。这样就会大大节省了服务器的资源消耗,并且能够将最新的信息快速地传递给客户端。
浏览器在发送异步请求之后需要将当前的HTTP连接保持住,当服务器端执行完异步任务之后还需要通过这个连接将结果返回给浏览器。这个特点给传统的Web服务器带来了一定的困难,因为每个HTTP连接通常会占用一个处理线程。大量空闲的HTTP连接会消耗大量的Java线程,使得系统的扩展性不好。而在GlassFish中,Web引擎Grizzly使用了NIO技术,能够将HTTP连接与实际处理线程分离,很好地解决了异步请求的扩展性问题。
18.1.1 Grizzly异步请求处理的实现
Grizzly实现异步请求处理的目标是借助NIO的优势,最大限度的利用原有同步执行的模型,实现高性能的异步请求处理的功能,做到HTTP连接和处理线程的分离,以获得很好的扩展性。
在Grizzly异步请求处理的模块中,有三个重要的概念需要理解。
l AsyncHandler:当系统配置成为异步请求处理的时候,Grizzly会将请求交给这个接口来处理。这是异步请求处理的主要入口。AsyncHandler在处理请求的时候会委托给AsyncExecutor来执行具体的操作。由于异步请求处理并不是正规支持的特点,也不是标准所规定的功能,因此默认情况下,异步请求处理是关闭的。很重要的一点会在下文源码分析中看到:在异步请求处理功能打开的情况下,同步请求处理也能正常地进行。
l AsyncFilter:这个接口需要应用程序去具体实现,因为AsyncFilter的接口定义了条件的判断规则,来决定当前的请求是中断还是继续执行。
l AsyncExecutor:异步请求的执行接口。当AsyncHandler在处理请求的时候会委托给AsyncExecutor,AsyncExecutor会执行当前所有注册的AsyncFilter,来决定是继续执行请求的处理,还是中断当前请求,释放当前线程。
异步请求处理的原理如图18-3所示。

图18-3 Grizzly中ARP的实现原理
在图18-3的上半部分,是同步请求处理的过程:由SelectorThread将请求引进来,首先是由一个ReadTask来进行请求数据的读取,请求数据准备好以后,会产生一个ProcessTask来处理这个请求。无论是ReadTask还是ProcessTask都会在特定的Pipeline中执行。一旦异步请求处理的功能被打开了,那么ProcessTask就不会简单地在Pipeline中执行,而是交给一个AsyncHandler去执行。AsyncHandler将执行任务委托给AsyncExecutor,AsyncExecutor将所有注册的AsyncFilter都执行一遍,根据执行的结果来决定是继续执行请求的处理,还是中断当前请求。如果当前请求被中断就会放到一个中断的队列中,等待下次被调度执行。
异步请求处理的具体实现分为以下几个部分。
1. 异步请求处理功能的打开
异步处理功能的打开是在主线程中由SelectorThread类和SelectorThreadConfig类(在com.sun.enterprise.web.connector.grizzly包中)处理的。例如在SelectorThreadConfig类中的configureProperties方法,就有对异步请求参数的读取和配置。
【例18.1】异步处理的初始化过程(SelectorThreadConfig):
/**
* Read systems properties and configure the SelectorThread
*/
...
protected static void configureProperties(SelectorThread selectorThread){
if (System.getProperty(ASYNCH_HANDLER_PORT) != null){
String ports = System.getProperty(ASYNCH_HANDLER_PORT);
StringTokenizer st = new StringTokenizer(ports,",");
while(st.hasMoreTokens()){
if(st.nextToken().equals(String.valueOf(
selectorThread.getPort())) && System.getProperty(ASYNCH_HANDLER_CLASS)!= null){
selectorThread.asyncHandler =
(AsyncHandler)loadClassAndInstanciate(
System.getProperty(ASYNCH_HANDLER_CLASS));
selectorThread.asyncExecution = true;
}
}
}
...
}
从上面的代码可以清楚地看到,系统在初始化的时候根据系统的配置参数来决定是否要使用异步处理的模式。如果检测到配置了异步处理,就会设置asyncExecution的变量。这个变量在以后的处理中会经常使用,用于区分同步和异步处理的不同算法。一般来说要检查两个参数,一个是由ASYNCH_HANDLER_PORT变量定义的参数(在类中有定义:com.sun. enterprise.web.connector.grizzly.asyncHandler.ports)。另一个是ASYNCH_HANDLER_CLASS变量定义的参数(在类中有定义:com.sun.enterprise.web.connector.grizzly.asyncHandlerClass)。在通常情况下,异步处理的端口号和同步处理相同,异步处理所实现的类使用系统自带的一个类:com.sun.enterprise.web.connector.grizzly.async.DefaultAsyncHandler。因此,如果要打开异步处理的模式,需要在<glasfish_install>/domains/domain1/config/domain.xml中的<java-config>元素中设置相应的参数。
【例18.2】domain.xml中的异步处理配置:
<java-config>
...
<jvm-options>
-Dcom.sun.enterprise.taglibs=appserv-jstl.jar,jsf-impl.jar
</jvm-options>
<jvm-options>
-Dcom.sun.enterprise.taglisteners=jsf-impl.jar
</jvm-options>
<jvm-options>
-XX:NewRatio=2
</jvm-options>
<jvm-options>
-Dcom.sun.enterprise.web.connector.grizzly.asyncHandlerClass=com.sun
.enterprise.web.connector.grizzly.async.DefaultAsyncHandler
</jvm-options>
<jvm-options>
-Dcom.sun.enterprise.web.connector.grizzly.asyncHandler.ports=8080
</jvm-options>
</java-config>
2. 异步处理的读(Read)任务
当异步请求处理的功能被打开后,所有的读(Read)的任务不再使用DefaultReadTask类了,取而代之的是AsyncReadTask类。AsyncReadTask类和DefaultReadTask类的大部分功能都一样,只有在请求处理的时候(方法executeProcessorTask())有一些区别。
【例18.3】AsyncReadTask类中的executeProcessorTask()方法:
Public boolean executeProcessorTask() throws IOException{
...
// Call the listener and execute the task on it's own pipeline.
taskEvent.setStatus(TaskEvent.START);
taskContext.setInputStream(inputStream);
taskEvent.attach(taskContext);
fireTaskEvent(taskEvent);
return false;
}
从上面的代码来看,在AsyncReadTask类中并没有象DefaultReadTask一样直接去执行任务的处理(使用processorTask.process(inputStream, null)),而是触发了一个事件,让事件的监听者去执行相应的操作。这主要是因为同步和异步的区别以及在处理性能上的考虑。在同步处理的模式下,直接调用任务处理模块,使得任务的处理在当前的线程中执行,这样能够避免线程的切换,获得最大的性能。而在异步模式下,一般来说任务的执行一般不会立刻发生(由于一些条件不满足),因此将任务的执行和当前的线程剥离开来,让任务的执行(或调度)运行在自己的线程中,这样能更快地释放当前的线程来进行其他的请求服务。
3. 异步请求的具体处理过程
当AsyncReadTask类中触发了事件以后,这个事件的监听者就是DefautProcessorTask的对象。在它的taskEvent()方法中有对这个事件的处理过程。
【例18.4】DefautProcessorTask类中的taskEvent()方法:
public void taskEvent(TaskEvent event){
if (event.getStatus() == TaskEvent.START) {
taskContext = (TaskContext)event.attachement();
if (taskEvent == null) {
taskEvent = new TaskEvent<TaskContext>();
}
taskEvent.attach(taskContext);
if (!asyncExecution) {
execute();
} else {
asyncHandler.handle(this);
}
}
}
从上面的代码中可以清楚地看到:在异步处理的模式下,请求的处理被交给asyncHander对象的handle方法来处理了。asyncHander对象的变量,实际上就是我们通过配置文件传入的com.sun.enterprise.web.connector.grizzly.async.DefaultAsyncHandler。下面来了解整个异步处理的全过程。
【例18.5】DefaultAsyncHandler类中的handle()方法:
public void handle(Task task){
AsyncTask apt = null;
if (task.getType() == Task.PROCESSOR_TASK) {
apt = getAsyncProcessorTask();
apt.setProcessorTask((ProcessorTask)task);
}
boolean wasInterrupted = interrruptedQueue.remove(task);
if (!wasInterrupted && apt == null) {
String errorMsg = "";
if (task.getSelectionKey() != null) {
errorMsg = "Connection " + task.getSelectionKey().channel()
+ " wasn't interrupted";
}
throw new IllegalStateException(errorMsg);
} else if (apt == null){
apt = (AsyncTask)task;
}
apt.execute();
}
从上面的代码中可以看出,DefaultAsyncHandler类中的handle()方法主要是构造了一个AsyncProcessorTask类的对象,并且执行它的execute的方法。这个handle()方法除了可以在DefautProcessorTask类的taskEvent()方法中被调用以外,在异步处理执行的过程中,当有任务从中断队列中移出的时候,也会调用这个方法的。因此在这个方法中有对不同情况的调用进行的判断,进行不同的处理。AsyncProcessorTask这个类位于com.sun.enterprise.web. connector.grizzly.async包中,它实际上是对原有的ProcessorTask进行的一个包装,最后任务的实际处理都是调用它所包含的ProcessorTask中的方法来进行的,这就使得对请求的处理保持了一致性,无论是同步还是异步请求。
对AsyncProcessorTask的执行(通过execute方法),最后都会落在它的doTask()方法上。
【例18.6】AsyncProcessorTask类中的doTask()方法:
public void doTask() throws java.io.IOException {
boolean contineExecution = true;
while (contineExecution) {
try{
switch(stage){
case AsyncTask.PRE_EXECUTE:
stage = AsyncTask.INTERRUPTED;
contineExecution = asyncExecutor.preExecute();
break;
case AsyncTask.INTERRUPTED:
stage = AsyncTask.POST_EXECUTE;
contineExecution = asyncExecutor.interrupt();
break;
case AsyncTask.EXECUTE:
contineExecution = asyncExecutor.execute();
stage = AsyncTask.POST_EXECUTE;
break;
case AsyncTask.POST_EXECUTE:
contineExecution = asyncExecutor.postExecute();
stage = AsyncTask.COMPLETED;
break;
}
} catch (Throwable t){
SelectorThread.logger().log(Level.SEVERE,t.getMessage(),t);
if (stage <= AsyncTask.INTERRUPTED) {
// We must close the connection
stage = AsyncTask.POST_EXECUTE;
} else {
stage = AsyncTask.COMPLETED;
throw new RuntimeException(t);
}
} finally {
// If the execution is completed, return this task to the pool
if (stage == AsyncTask.COMPLETED){
stage = AsyncTask.PRE_EXECUTE;
asyncExecutor.getAsyncHandler().returnTask(this);
}
}
}
}
从上面的代码(例18.6)中我们可以看到,AsyncProcessorTask的执行在最后都委托给了asyncExecutor对象(一般情况下,这个对象属于DefaultAsyncExecutor类)。这个对象会先执行preExecute()方法。这个方法会先去解析客户请求的数据。随后asyncExecutor对象会执行interrupt()方法,这个方法是异步处理的核心方法,它会负责检查当前的条件是否满足请求任务的处理。如果不满足,就放到中断队列中,等待下次调度被执行;如果满足执行的条件,就会执行该请求,这个执行相当于Servlet和JSP的请求被执行一样。最后asyncExecutor对象会执行postExecute()方法将结果返回给客户端。
【例18.7】DefaultAsyncExecutor类中的interrupt()和invokeFilters()方法:
public boolean interrupt() throws Exception{
if (asyncFilters == null || asyncFilters.size() == 0) {
execute();
return false;
} else {
asyncHandler.addToInterruptedQueue(asyncProcessorTask);
return invokeFilters();
}
}
...
private boolean invokeFilters(){
boolean continueExec = true;
for (AsyncFilter asf: asyncFilters){
continueExec = asf.doFilter(this);
if (!continueExec){
break;
}
}
return continueExec;
}
在上面的代码(例18.7)中可以看到,DefaultAsyncExecutor的interrupt()方法其实比较简单,它仅仅是将任务放到了中断的队列中,再调用invokeFilters()方法。而在invokeFilters()方法中,遍历了所有注册的AsyncFilter,依次执行它们的doFilter()方法。只要有一个AsyncFilter的doFilter()方法返回是false,那么整个interrupt()方法就会返回true。意味着当前的请求没有满足所有的条件,不能继续执行,只能放到中断队列中等待下次被调度执行。
【例18.8】AsyncFilter接口:
public interface AsyncFilter {
/**
* Execute and return true if the next AsyncFilter can be invoked
* Return false to stop calling the AsyncFilter
*/
public boolean doFilter(AsyncExecutor asyncExecutor);
}
在上面的代码(例18.8)中可以看到,AsyncFilter只是一个接口,具体的异步处理的模块需要实现这个接口。最重要的一点是,Grizzly中整个异步处理的框架都要依靠这个接口来扩展。下面就以“新邮件提醒”为例来进行异步处理的讲解。
18.1.2 “新邮件提醒”异步请求处理
随着Web技术的流行,越来越多的应用从C/S的结构移植到B/S的结构上。邮件阅读器就是其中的一个。在早些时候,邮件阅读器几乎被微软的Outlook统治着。但是随着一些门户网站推出免费的和大容量的个人邮箱之后,邮件不再是企业员工才能拥有的工具了。大量的Web邮箱(使用HTTP协议)不但使得大量的个人用户通过浏览器就能访问自己的个人Email,而且使得访问邮箱变得非常便利:例如不需要安装客户端、可以通过防火墙等。据国际权威组织统计,目前Web邮箱的使用人数已经超过使用传统邮件阅读器的人数。近年来,随着Web 2.0技术的发展,几乎所有Web邮件的提供商(Google、Sina、Yahoo、Sohu等)都更新了它们的Web邮件阅读器,使得这些Web邮件的阅读与原来的专用阅读工具具有同样的可操作性、界面同样友好、交互与C/S结构一样流畅。
但是,无论界面如何友好,有一点Web邮件系统很难做到的是:如何及时地获得最新的信息。这是由于HTTP协议的无状态性造成的。当客户端的请求被处理以后,刚才建立起来的HTTP连接就被关闭了。而且每个请求都必须是浏览器客户端发起的,Web服务器只是被动地响应客户端的请求。当服务器端探测到新的邮件到达时,无法通知客户端。因为服务器不能主动新建立一个HTTP连接到浏览器(因为安全性、防火墙等原因),因此,只能依靠浏览器自己去请求新的信息。前面提到过,一般来说浏览器通过一个叫轮询(Polling)的技术不断刷新(自动或手动)页面,才能完成特定的功能。这样的方案不具有良好的扩展性,同时浪费了大量的服务器资源和网络资源。
用Grizzly的异步请求处理(ARP)的模式就可以解决这个问题。下面来看看这个“新邮件提醒”的ARP实现。这个实现是Grizzly作者编写的,与Grizzly的源码放在一起,位于org.glassfish.grizzly.async.javamail包中。这个包中有三个类,它们是JavaMailAsyncFilter、JavaMailAsyncFilterHandler、JavaMailAsyncFilterEvent。但是在JavaMailAsyncFilter类中还包含另外一个类——MailFetcher。这几个类的关系如图18-4所示。

图18-4 “新邮件提醒”ARP扩展类图
上一节提到过,要扩展ARP(异步请求处理)的模型,只需要实现AsyncFilter这个接口,并将其注册上去就行了。JavaMailAsyncFilter类正是对AsyncFilter这个接口的一个实现,因此它是这个“新邮件提醒”功能的主要入口。而另外几个类的存在是将“新邮件提醒”的应用分成了两个部分。一个是ARP扩展部分,指的就是这几个类。这些扩展需要放到GlassFish系统的类路径下,会成为GlassFish的一部分。每次对这个部分的修改和部署,都需要重新启动GlassFish,才能使改动生效。需要主意的是:JavaMailAsyncFilterHandler只是一个接口,需要具体的应用来实现它。因此“新邮件提醒”功能的另外一个部分应该是实现了JavaMailAsyncFilterHandler接口的Web应用。这一个部分可以在GlassFish运行的时候随时部署和修改,不需要重新启动服务器。换句话说,基于“新邮件提醒”的ARP扩展部分,用户可以部署不同的“新邮件提醒”的应用,这些应用根据对JavaMailAsync- FilterHandler接口的不同实现,提供了不同的邮件提醒功能。
下面我们通过跟踪源代码,来分析这个“新邮件提醒”的ARP扩展的具体实现方法。
【例18.9】JavaMailAsyncFilter的doFilter()方法:
public boolean doFilter(AsyncExecutor asyncExecutor) {
AsyncTask asyncProcessorTask = asyncExecutor.getAsyncTask();
ProcessorTask processorTask = asyncProcessorTask.getProcessorTask();
AsyncHandler asyncHandler =
((DefaultProcessorTask)processorTask).getAsyncHandler();
Request request = ((DefaultProcessorTask)processorTask).getRequest();
String contextPath = request.requestURI().toString();
contextPath = contextPath.substring(contextPath.lastIndexOf("/"));
JavaMailAsyncFilterHandler handler = handlers.get(contextPath); //[1]
if ( handler == null){
processorTask.invokeAdapter(); // [2]
return true;
}
MailFetcher mf = mailFetcherCache.poll();
if (mf == null){
mf = new MailFetcher();
}
mf.handler = handler;
mf.processorTask = processorTask;
mf.asyncProcessorTask = asyncProcessorTask;
mf.asyncHandler = asyncHandler;
scheduler.schedule(mf,1L,TimeUnit.SECONDS); //[3]
return false;
}
从上面的源码(例18.9)来看,在主要的入口类JavaMailAsyncFilter的doFilter()方法中,首先根据系统注册的JavaMailAsyncFilterHandler接口的实现类,核对当前的contextPath是否是注册过的路径[1]。ARP系统会只对注册过的路径进行异步处理,而对没有注册过的路径,就算打开了异步请求处理的功能,系统也会通过processorTask.invokeAdapter()的调用,直接去执行当前请求[2]。如果当前路径是注册过的,接着获得一个MailFetcher的对象。这个对象是Runnable的类,可以在单独的线程中运行。最后代码[3]将这个MailFetcher的对象放到一个线程调度器中等待调度。
【例18.10】MailFetcher的run()方法:
public void run(){
Message[] messages = checkMail(); //[1]
event.setMessages(messages);
boolean continueCheckMail = handler.handleEvent(event); //[2]
if (!continueCheckMail) {
processorTask.invokeAdapter(); //[3]
asyncHandler.handle(asyncProcessorTask);
mailFetcherCache.offer(this);
} else {
scheduler.schedule(this,10L,TimeUnit.SECONDS); //[4]
}
}
MailFetcher的对象被放到一个线程调度器中,根据调度的规则被调度运行之后,会直接运行run()的方法。从上面的源码(例18.10)来看,run()方法会先调用checkMail()方法[1]。这个方法事实上是唯一去和邮箱打交道的方法,它会根据配置的参数,通过JavaMail的API获得邮件的信息,放到变量中。接着run()方法会调用注册过的JavaMailAsyncFilterHandler接口实现类的handleEvent()方法[2],并且根据返回的结果判断当前是否满足执行的条件。如果已经满足条件了,就去执行当前的请求[3],也就是说当前的Servlet就会被执行。如果不满足条件,这个MailFetcher的对象会被重新放到线程调度器中去调度[4]。最后再来看看具体的应用如何实现JavaMailAsyncFilterHandler的接口。
【例18.11】EmailNotifierServlet:
public class EmailNotifierServlet extends HttpServlet
implements JavaMailAsyncFilterHandler{
...
public void init(ServletConfig config) throws ServletException {
username = config.getInitParameter("username");
password = config.getInitParameter("password");
mailServer = config.getInitParameter("mailServer");
mailServerPort = config.getInitParameter("mailServerPort");
JavaMailAsyncFilter.register(
config.getInitParameter("contextPath"), this);
}
...
public boolean handleEvent(JavaMailAsyncFilterEvent event) {
messages = event.getMessages();
if (messages == null || messages.length == 0){
return true;
} else {
return false;
}
}
...
}
从上面的源码(例18.11)来看,要实现“新邮件提醒”功能的Web应用,需要实现一个JavaMailAsyncFilterHandler接口。在本例中,当前的Servlet自身就实现了这个接口。并且在init方法中进行了注册。对于JavaMailAsyncFilterHandler接口的实现也非常简单,本例中的判断条件是如果有新的邮件就返回“true”。当然,读者可以自己改造成别的判断条件,例如每5个邮件提醒一次等。有关这个“新邮件提醒”功能例子的部署和运行,在本书所附光盘中有详细的代码和步骤。
18.1.3 Grizzly异步请求处理的特点
在Grizzly中,对异步请求处理完全是依靠NIO的特点,将处理线程与HTTP连接进行了分离,来确保系统的高性能和高可扩展性。但是这个实现在很多的方面还有待提高和完善:
l 在同一个GlassFish实例中,同步和异步模式不能同时打开。系统只能运行在一个模式下。虽然在异步模式下也能运行同步的请求,但是性能和IO吞吐量会有损失。如果能够根据不同应用的需要,系统既可以在同步模式下运行同步请求的应用以获得最大的性能,又能在别的应用上打开异步的模式来完成ARP的请求,这样系统就更加完善。
l 异步请求的扩展部分比较复杂,编写一个ARP扩展需要了解太多的内部细节,例如需要自己提供线程调度器等。如果能够提供一个简单的ARP扩展接口,并且提供更多的系统服务(例如调度器),那么编写ARP扩展就会变得容易多了
在GlassFish中,异步请求处理(ARP)还不是一个官方支持的功能,在相关文档中都没有太多的描述和说明。异步请求处理还处于探索阶段,它的实现和接口还有很大的提高余地。但是从它的强大功能中,不难看出其光辉的未来。







