正确理解servlet3.0规范之异步特性
2017-02-26 16:59
337 查看
如果想了解有关servlet3.0特性,推荐下面这篇ibm的文章:
https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/index.html#icomments
servlet3.0增加了对异步的支持,在servlet3.0之前,客户端请求到达servlet后,servlet通常会执行一些比较耗时的外部操作,比如数据库操作、I/O操作、跨网络调用等,往往会阻塞当前servlet线程,当前的线程是由servlet容器(tomcat)管理并分配的,容器线程池为请求分配的线程会持有一系列的servlet资源,因为该线程会调用一系列方法(大部分为tomcat内部方法),而方法内部可能会持有很多线程私有的对象,比如在一个有过滤器的web应用中,每个线程将持有私有的filterChain对象。如果阻塞时间过长,那么在这段时间内此线程无法被回收,为资源分配的内存一直被占用,无法被GC回收或者返回Object pool,且该线程也无法分配给其他客户端请求,在一定程度上会造成并发量的降低。
因此,在servlet3.0中,在servlet中执行完一些必要的操作后(可以调用response.getWriter().flush()首先返回客户端一部分数据,因为flush()方法会将缓冲区的数据刷新到输出流中,但是注意,不能调用response.getWriter().close()方法(因为调用完close()方法后,此response的输出流就关闭了),并且response的contentType必须要被显式设置(否则浏览器在不知道接收到的数据类型的情况下不会部分显示数据,而选择等待所有数据都到来)),可以将耗时较大的操作放到另外一个线程中执行,并把一个AsyncContext上下文传递过去,而当前线程直接返回以便servlet容器线程池能够将其回收。而另一个线程在执行完成后,再调用response.getWriter().flush()、response.getWriter().close()操作,将结果返回给客户端。
servlet3.0规范新增的异步处理支持,很多人都会理解错误,比如,这篇文章以及评论,错误地认为servlet3.0的异步就是servlet能够将部分结果返回给客户端,然后一段时间后再将剩余结果返回给客户端。就像这个问题中的提问者一样。
其实这种返回部分结果的特性,servlet2就可以实现,就是多次调用输出流的flush()方法即可,把缓冲区的数据刷新到输出流中,输出流就会把数据返回给浏览器。如果此时response的contentType被显式设置了,浏览器就会部分显示数据(chrome浏览器)。也就是说实现方式为多次调用flush()方法,并且只最后调用一次close()方法。
将部分结果返回给客户端并不算是异步,而异步真正的关注点在于后台处理的方式。返回部分结果,后台可以是同步的(提前flush,提高客户端体验),也可以是异步的。而servlet3.0规范中的异步,目的不是关注客户端体验,因为返回部分结果同样可以由同步实现:
其真正的目的是使后台操作异步,提早回收由容器管理的servlet线程。
注:一直在想,在servlet2中另起一个线程,将response传递过去,并且在servlet中response不close,这不也能实现后台异步处理嘛? 为了印证这个想法,我就试了一下,结果返回的结果不稳定,很可能会丢失数据,多次刷新浏览器快速访问此servlet还会报出
测试代码如下:
https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/index.html#icomments
servlet3.0增加了对异步的支持,在servlet3.0之前,客户端请求到达servlet后,servlet通常会执行一些比较耗时的外部操作,比如数据库操作、I/O操作、跨网络调用等,往往会阻塞当前servlet线程,当前的线程是由servlet容器(tomcat)管理并分配的,容器线程池为请求分配的线程会持有一系列的servlet资源,因为该线程会调用一系列方法(大部分为tomcat内部方法),而方法内部可能会持有很多线程私有的对象,比如在一个有过滤器的web应用中,每个线程将持有私有的filterChain对象。如果阻塞时间过长,那么在这段时间内此线程无法被回收,为资源分配的内存一直被占用,无法被GC回收或者返回Object pool,且该线程也无法分配给其他客户端请求,在一定程度上会造成并发量的降低。
因此,在servlet3.0中,在servlet中执行完一些必要的操作后(可以调用response.getWriter().flush()首先返回客户端一部分数据,因为flush()方法会将缓冲区的数据刷新到输出流中,但是注意,不能调用response.getWriter().close()方法(因为调用完close()方法后,此response的输出流就关闭了),并且response的contentType必须要被显式设置(否则浏览器在不知道接收到的数据类型的情况下不会部分显示数据,而选择等待所有数据都到来)),可以将耗时较大的操作放到另外一个线程中执行,并把一个AsyncContext上下文传递过去,而当前线程直接返回以便servlet容器线程池能够将其回收。而另一个线程在执行完成后,再调用response.getWriter().flush()、response.getWriter().close()操作,将结果返回给客户端。
servlet3.0规范新增的异步处理支持,很多人都会理解错误,比如,这篇文章以及评论,错误地认为servlet3.0的异步就是servlet能够将部分结果返回给客户端,然后一段时间后再将剩余结果返回给客户端。就像这个问题中的提问者一样。
其实这种返回部分结果的特性,servlet2就可以实现,就是多次调用输出流的flush()方法即可,把缓冲区的数据刷新到输出流中,输出流就会把数据返回给浏览器。如果此时response的contentType被显式设置了,浏览器就会部分显示数据(chrome浏览器)。也就是说实现方式为多次调用flush()方法,并且只最后调用一次close()方法。
将部分结果返回给客户端并不算是异步,而异步真正的关注点在于后台处理的方式。返回部分结果,后台可以是同步的(提前flush,提高客户端体验),也可以是异步的。而servlet3.0规范中的异步,目的不是关注客户端体验,因为返回部分结果同样可以由同步实现:
writer.print("首先返回这句话,使客户端得到及时的响应"); writer.flush();//刷新到输出流 result = db.query();//进行数据库查询 writer.print(result);//返回剩余结果
其真正的目的是使后台操作异步,提早回收由容器管理的servlet线程。
注:一直在想,在servlet2中另起一个线程,将response传递过去,并且在servlet中response不close,这不也能实现后台异步处理嘛? 为了印证这个想法,我就试了一下,结果返回的结果不稳定,很可能会丢失数据,多次刷新浏览器快速访问此servlet还会报出
Exception in thread "Thread-13" org.apache.tomcat.jni.Error: 20005: An invalid socket was returned at org.apache.tomcat.jni.Socket.sendbb(Native Method)的异常,原因应该是servlet线程执行完方法后,tomcat会自动调用response.close()方法,导致在另外一个线程中使用输出流时,客户端和服务器的连接已经关掉了。
测试代码如下:
@WebServlet(name = "noAsyncServlet",urlPatterns = "/noasync",asyncSupported = false) public class NoAsyncServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final PrintWriter writer = resp.getWriter(); writer.write("first"); writer.write("second\n"); writer.flush(); new Thread(new Task(writer)).start(); writer.write("already give to another\n"); writer.flush(); } private class Task implements Runnable{ PrintWriter writer = null; public Task(PrintWriter writer) { this.writer = writer; } public void run() { writer.write("begin handle\n"); writer.flush(); try { Thread.sleep(1000); } catch (Exception e) { } writer.write("end handle\n"); writer.flush(); writer.close(); } } }
相关文章推荐
- 【Servlet3.0新特性】第01节_Servlet注解及异步支持
- servlet3.0新特性Servlet3.0引入的若干重要新特性,包括异步处理、新增的注解支持、可插性支持等等
- 测试:Servlet 3.0实战:异步处理特性应用
- Servlet3.0新特性之异步请求实践
- Servlet3.0新特性---异步支持
- servlet3.0新特性Servlet3.0引入的若干重要新特性,包括异步处理、新增的注解支持、可插性支持等等,为读者顺利向新版本过渡扫清障
- C# 3.0语言新特性(语言规范):6 具有隐式类型的数组
- C# 3.0语言新特性(语言规范):6 具有隐式类型的数组
- C# 3.0语言新特性(语言规范):4 对象和集合初始化器
- 理解C# 3.0新特性之Extension方法浅议
- C# 3.0语言新特性(语言规范):4 对象和集合初始化器
- C# 3.0语言新特性(语言规范):2 扩展方法
- C# 3.0语言新特性(语言规范):7 查询表达式
- 理解C# 3.0新特性之Extension方法浅议
- C# 3.0语言新特性(语言规范):5 匿名类型
- C# 3.0语言新特性(语言规范):8 表达式树
- String虽然很简单,但他的某些特性你真理解正确了吗?
- 理解C# 3.0新特性之Extension方法浅议
- Servlet3.0新功能: 异步处理
- C# 3.0语言新特性(语言规范)