恒久地平线

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

异步处理  

  应用服务器中的 web容器通常对各个客户端情求分别使用一个服务器线程。在工作负载很繁重的情况下,容器常要大量线程来为所有客户端请求服务。可扩展性限制包括内存用尽,或容器线程池耗尽。为了创建可扩展的 web应用,必须确保与请求关联的线程不会空闲, 使容器可以用它们处理新的请求。有时与请求关联的线程可能会空闲, 这有两种常见的情况:

  • 线程需要等待一个资源可用,或者在建立响应之前处理数据。例如,应用在生成响应之前可能需要査询一个数据库, 或者要从一个远程 web服务访问数据。
  • 线程生成响应之前需要等待一个事件。例如, 一个应用在生成响应之前可能必须等待一个 JMS消息、 来自另一个客户端的新信息, 或者队列中可用的一个新数据。

  这些情況表示了限制 web应用可扩展性的一些阻塞操作。异步处理是指把这些阻塞操作分配给一个新线程, 而与请求美联的线程立即返回给容器 。

servlet中的异步处理

  Java EE为 servlet和过滤器提供了异步处理支持。如果一个 servlet或过渡器在处理请求时达到一个可能的阻塞操作,它可以把这个操作分配到一个异步执行上下文,并将与请求联的线程立即返回到容器, 而不生成响应, 阻塞操作在异步执行上下文中的一个不同的线程中完成,这可能会生成一个响应,或者将请求分派给另一个servlet。
  要在一个 servlet上启用异步处理.可以将@Webservlet注解的参数 asyncsupported设置为true,如下所示:

@WebServ1et(urlPatterns="/asynserv1et" , asyncSupported=true)
public class AsyncServlet extends HttpServlet{ 
.. 
}

  javax. servlet.Asynccontext类提供了在服务方法中完成异步处理所需的功能为了得到 Asynccontext的一个实例,可以在服务方法的请求对象上调用startAsync()方法,例如:AsynoContext acontext = req. startAsync();
这个调用将请求置于异步模式. 并确保退出服务方法后不提交响应。 必须在异步上下文中完成阻塞操作之后或者将请求分派到另一个 servlel之后再生成响应。

@WebServ1et(urlPatterns={"/asyncservlet"}), asyncSupported=true) 
public classe AsyncServlet extends HttpServlet {
    public void doGetlHttpServletRequest request,
            HttpServletResponse response)  {
        final AsyncContext acontext= request.startAsync();
        acontext.start(new Runnable() {
            public void run() {    
                HttpServletResponse response= acontext.getResponse();
                response.getWriter().write("complete")
                acontext.cmplete()
            }
            ...

  Asyncservlet在@Webservlet注解中增加了 asyncsupported=true。 另外只有服务方法有区别 。

  • request.startAsync()使请求得到异步处理,响应不会在服务方法完成时发送到客户端。
  • acontext.start(new Runnable() 1…}) 从容器得到一个新线程。

  内部类 run()方法中的代码在这个新线程中执行,这个内部类可以访问异步上。
从请求读取参数.并写至响应,调用异步上下文的 complete()方法会提交响应并把它发送到客户端,Asyncservlet的服务方法立即返回,请求在异j步上下文中处理。

非阻塞I/O  

  应用服务器中的 Web容器通常对每个客户端请求分别使用一个线程。要开发可扩展的 web应用. 必须确保与客户端请求关联的线程不会会保持空闲吗来等持一个阻塞操作完成。异步处理提供了-种机制,在一个新线程中执行应用特定的阻塞操作,将与请求关联的线程立即返回到容器. 即使对服务方法中所有应用特定的阻塞操作都使用异步处理,但由于输入/输出同题,与客户端请求关联的线程偶尔也可能会空闲。
  例如.如果客户端在一个很慢的网络连接上提交一个很大的 HTTP POST青求.服务器读取请求的速度可能比客户端提供请求的速度更快。 如果使用传统的 I/O。 与这个请求关联的容器线程有时就会空闲, 来等待请求的其余部分 。
  Java EE采用异步模式处理清求时, 対 servlet和过滤器提供非阻塞I/O支持。下面的步骤总结了如何使用非阻塞 I/0在服务方法中处理清求和写入响应。

  1. 将请求置为异步模式.
  2. 在服务方法中从请求和响应对象得到一个输入流或一个输出流。
  3. 为输入流指定一个读监听器成为输出流提供一个写监听器。
  4. 在监听器的回调方法中处理请求和响应 。
@WebServlet(urlPatterns={"/asyncioservlet"},asyncSupported= true)
public class AsyncIOServlet extends HttpServlet{
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        final AsyncContext context = request.startAsync();
        final ServletInputStream input = request.getInputStream();
        input.setReadListener(new ReadListener() {
            byte buffer [] = new byte[4 * 1024];
            StringBuilder sbuilder = new StringBuilder();
            @Override
            public void onError(Throwable arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onDataAvailable() throws IOException {
                try {
                    do {
                        int length = input.read(buffer);
                        sbuilder.append(new String(buffer,0,length));
                    } while (input.isReady());
                } catch (Exception e) {
                    // TODO: handle exception
                }
                
            }
            
            @Override
            public void onAllDataRead() throws IOException {
                try {
                    context.getResponse().getWriter().write("the message");
                } catch (Exception e) {
                    // TODO: handle exception
                }
                context.complete();
                
            }
        });
    }
}

  input.isReady()是非阻塞的方法,是否可读,立即返回true或false。

Servlet文件上传

  对于很多 web应用来说. 支持文件上传是一个很基本也很常见的需求。 在Servlet规范中,实现文件上传需要使用外部库或者复杂的输入处理。现在 Java Servlet规范可以采用一种通用而且可移植的方式为这个问题提供一个可靠的解决方案。如今 Java Servlet技术可以直接支持文件上传, 实现 Java Servlet规范的所有 web容器都可以解析多部分请求,通过过HttpServletRequest对象提供 MIME附件。
  另外有一个新注解 javax.servlet.annotation.Multipartconfig,用来指示声明该注解的servlet希望使用multipart/form-data MIME类型建立清求。有 @Multipartconfig 注解的servlet可以调用 request.getPart(String name)或 request.getParts()方法获取一个给定 multipart/form-data请求的 Part组件。

@Multipartconfig注解

  @Multipartconfig注解支持以下可选的属性:

  • location:这是一个绝对路径,指示文件系统上的一个目录。location属性不支持相对于应用上下文的路径。处理多部分成文件大小超出指定 filesizeThreshold设置时,会用这个位置临时地存储文件。默认位置为””
  • filesizeThreshold:超过这个文件大小(字节数)的其余部分将临时存储在磁盘上。默认大小为 0字节。
  • MaxFilesize:上传文件允许的最大大小(字节数)。如果任何一个上传文件的大小超过了这个大小, Web容器就会批出一个异常(IllegalstateException)。默认大小为无限制 。
  • maxRequestSize:対于一个multipart/form-clata请求,所允许的最大大小(字节数)。如果所有上传文件的总大小超过这个固值, Web容器会抛出一个异常。默认大小为无限制 。

getParts和 getPart 方法

Servlet规范还支持另外两个 HttpservletRequest方法:

  • Collection getParts()
  • Part getPart(String namee)

  request.getParts()方法返回所有 Part对象的集合。如果有多个输入文件类型,就会返回多个 Part对象,由于 Part对象是命名对象,可以用 getPart(string name)方法访问一个特定的 Part,或者.可以用返回Iterable的 getparts()方法得
到所有 Part对象上的一个Iterator。
  javax.servlet.http.Part接口是一个简单的接口, 提供了一些允许各个 part自省的方法, 这些方法完成以下工作:

  1. 获取Part的名字、大小和内容类型,,
  2. 查询带 part提交的头部,,
  3. 删除一个Part,,
  4. 将一个 part写到磁盘上

  例如, Part接口提供了write(string filename)方法来写指定名的文件,这个文件保存在@MultipartConfig注解location属性指定的目录中,或者对于fileupload示例,它保存在表单Destination域指定的位置上。

参考资料

  • 《Java EE 7权威指南》
posted on 2017-07-19 20:00  恒久地平线  阅读(1695)  评论(0编辑  收藏  举报

腾讯微博:http://t.qq.com/zhangxh20