金沙城中心赌场,Servlet3.0的异步,Servlet3.0异步

servlet之前的操作同时同步的,就是按照这样的一个流程来走的:

1.请求根据一个路径路由到一个servlet中,

2.servlet获取一系列的参数

3.执行一系列的逻辑(花费时间所占的比重也更大)

4.返回结果

上面的问题出现在这一系列的操作都是同步的,所以这个请求必定是堵塞到所以任务都完成之后才返回的,

这样将会很浪费资源,因为线程堵塞在那里,仅仅是等待任务的完成。但是在servlet3.0之后,我们基本上可以

是这样做的

1.请求根据一个路径路由到一个servlet中,

2.将逻辑放入到异步队列中去

3.返回结果

4.异步队列处理任务,得出结果,返回给页面

而servet3.0对于异步的处理主要涉及的有两个特性,一个是新增的类AsyncContext,另外的一个就是asyncSupported属性

①如果我们想要让我们的servlet支持异步的话,那么asyncSupported这个属性是一定需要设置的,对于注解的类型来说,我们直接设置属性

@WebServlet(asyncSupported=true,urlPatterns={"/async"})

就可以了,对于老版本的配置问价来说,只需要在配置web.xml
的servlet那里增加一个

<async-supported>true</async-supported> 

还有一个就是对于动态的servlet,设置

dynamic.setAsyncSupported(true);

就可以了
②而对于AsyncContext 需要记住的东西还是蛮多的,但是它主要的是保留了请求和相应的引用,在前面提到的返回结果之后的操作就是通过在异步环境下,对这两个引用进行操作。

要获取这个就需要使用request在3.0之后增加的方法,startAsync(..) ,这个方法就是返回一个AsyncContext实体对象,这里包含了request和response的引用,至于我们异步的处理方式,就有很多种了,我们可以直接定义一个工作队列,异步的方式一个个的进行处理,又或者是直接使用AsyncContext.start(Runnable)方法启动一个新的线程去进行处理逻辑

AsyncContext主要的方法:
getRequest() 获得请求即request,我们可以在异步的环境像在service中使用一样

getReponse() 和上面差不多一个意思

hasOriginalRequestAndResponse()这个方法表示的是我们使用的AsyncContext是使用原始的请求获取的,还是通过封装过的请求和相应创建的
简单的讲就是 原始的类型表示的是调用startAsync()。但是封装的就是startAsync(ServletRequest, ServletResponse)或者其他类型啦,

dispatch()方法,这个方法有有好几个重载,表示的是转发,和req.getRequestDispatcher()有点类似,但是比较丰富
如果使用的是startAsync(ServletRequest, ServletResponse)初始化AsyncContext,且传入的请求是HttpServletRequest的一个实例,则使用HttpServletRequest.getRequestURI()返回的URI进行分派。否则分派的是容器最后分派的请求URI。
下面的代码是网上的:

// 请求到 /url/A  
AsyncContext ac = request.startAsync();  
...  
ac.dispatch(); // 异步分派到 /url/A  

// 请求到 /url/A  
// 转发到 /url/B  
request.getRequestDispatcher(“/url/B”).forward(request, response);  
// 从FORWARD的目标内启动异步操作  
AsyncContext ac = request.startAsync();  
ac.dispatch(); // 异步分派到 /url/A  

// 请求到 /url/A  
// 转发到 /url/B  
request.getRequestDispatcher(“/url/B”).forward(request, response);  
// 从FORWARD的目标内启动异步操作  
AsyncContext ac = request.startAsync(request, response);  
ac.dispatch(); //异步分派到 /url/B  

dispatch(String path) 这个方法就是转发到指定的url上去

 

complete():在我们使用了request.startAsync(..)获得AsyncContext之后,在完成异步操作以后,需要调用这个方法结束异步的操作。如果请求分派到一个不支持异步操作的Servlet,或者由AsyncContext.dispatch调用的目标servlet之后没有调用complete,则complete方法会由容器调用。但是对于比合法操作来说,比如没有调用startAsync放方法,却代用complete()
,那么就会抛出IllegalStateException的异常,同时在调用complete()之前,调用dispath()方法是不起作用的,当然了,因为这个时候异步还没结束嘛,当然不会又什么作用了。

setTimeOut(..) 设置超时的时间
表示的是异步处理的最大时间,如果是一个负数的话,那么表示永远不会超时

start(Runnable run) Runnable表示的就是异步处理的任务。我们在做的时候
会AsyncContext  带进去 因为所以的操作 都需要依靠他呢

addListener(AsyncListener listener);增加监听器
 就是监听AsyncContext各种状态发现变化的,主要有

金沙城中心赌场 1

前面三个都比较好理解,最后异步监听器将以它们添加到请求时的顺序得到通知。

下面是AsyncContext的一般使用方式

 

package com.hotusm.servlet.async;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns={"/url"},asyncSupported=true)
public class AsynDemoServlet extends HttpServlet{


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //resp.setHeader("Connection", "Keep-Alive");
        resp.setContentType("text/html;charset=utf-8");

        System.out.println(req.isAsyncSupported()+"  "+req.isAsyncStarted());
        /*req.getAsyncContext(); 表示的是最近的那个被request创建或者是
         * 重转发的AsyncContext
        */

        final AsyncContext ac = req.startAsync();

            //设置超时的时间
            ac.setTimeout(5*1000L);

            //这种方式 
            ac.start(new Runnable() {

                public void run() {

                    try {
                        TimeUnit.SECONDS.sleep(3L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    try {
                        PrintWriter writer = ac.getResponse().getWriter();
                        writer.write("1");
                        writer.flush();

                        //这是测试  同一个AsyncContext在没有调用complete 之前能不能多次的
                        //调用request 和response
                        PrintWriter writer1 = ac.getResponse().getWriter();
                        writer1.write("2");
                        writer1.flush();

                        ServletRequest request = ac.getRequest();

                        request.setAttribute("isAsyn", true);

                        /*
                         * 2.在调用完complete之后 表示这个异步已经结束了 如果在调用
                         * getRequest 或者是getResponse的话 都会抛出IllegalStateException
                         * 
                         * */
                        ac.complete();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        //设置监听
        ac.addListener(new AsyncListenerImpl());

        // 在同一个request中不能同时调用多次
        //req.startAsync();
        PrintWriter out = resp.getWriter();
        out.write("hello async");
        out.write("<br/>");
        //调用flush 不然还是不会输出  因为没有将内容刷出去
        out.flush();
    }

    static class AsyncListenerImpl implements AsyncListener{

        public void onComplete(AsyncEvent event) throws IOException {

            System.out.println("onComplete");
        }

        public void onTimeout(AsyncEvent event) throws IOException {
            System.out.println("onTimeout");
            event.getAsyncContext().complete();
        }

        public void onError(AsyncEvent event) throws IOException {
            System.out.println("onError");
        }

        public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("onStartAsync");
        }
    }
}

当我们上面的url的时候  会马上返回hello
async,然后在大概三秒钟之后,输出12

 

上面的方式只是使用了start(Runnable
run);的方式.我们也可以将AsyncContext放到一个工作队列中去,然后另外的一个线程池去做处理。

 示例代码:

package com.hotusm.servlet.async;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns={"/async1"},asyncSupported=true)
public class AsyncDispatchServlet1 extends HttpServlet{

    private LinkedBlockingQueue<AsyncContext> works=new LinkedBlockingQueue<AsyncContext>(100);

    @Override
    public void init() throws ServletException {
     //因为这里是测试 所以就开了5个线程来进行处理  但是真实的情况下  肯定是设计一个伸缩性的方案
        new Thread(new HelperWork()).start();
        new Thread(new HelperWork()).start();
        new Thread(new HelperWork()).start();
        new Thread(new HelperWork()).start();
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Connection", "Keep-Alive");
        resp.addHeader("Cache-Control", "private");
        resp.addHeader("Pragma", "no-cache");
        resp.setContentType("text/html;charset=utf-8");
        try {
            works.put(req.startAsync());
        } catch (Exception e) {

        }
        PrintWriter writer = resp.getWriter();
        writer.write("等待异步完成");
        writer.flush();
    }

    private class HelperWork implements Runnable{

        public void run() {
            try {
                AsyncContext ac = works.take();

          //模拟业务消耗
          TimeUnit.SECONDS.sleep(2L)

                 HttpServletRequest request =
(HttpServletRequest)ac.getRequest();

                Map<String, String[]> maps = request.getParameterMap();
                System.out.println(maps);

                HttpServletResponse response = (HttpServletResponse)ac.getResponse();
                PrintWriter writer = response.getWriter();
                writer.write(maps.toString());
                writer.flush();
                ac.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

 

上面只是一种思路,我们还可以放入到线程池中进行处理等等。

然后再讲一下怎么通过ajax怎么异步的通信,我们只需要在第一次访问servlet的时候,保留AsyncContext的引用,之后通过这个的输出和页面做交互就可以了。

 

 

 

servlet之前的操作同时同步的,就是按照这样的一个流程来走的:
1.请求根据一个路径路由到一个servlet中,…

什么是Servlet,Servlet

写Web应用时,直接用的spring框架,没有接触过Servlet,于是遇到问题不知道根本原因,所以还是学一下吧!
一、 什么是Servlet
看见这个词我就晕,到底是什么?我试着说清楚,从大的方面来说,它是一个在Web中生成动态内容的标准,即是用于开发Web应用程序的基本技术;从小的方面来说它是Java提供的一个接口Servlet,我们也把实现这个接口的类称为Servlet。它和CGI相对,但是它不向CGI一样每次接受一个Http请求都会创建一个进程(想想Chrome),它每次执行完它的第一个请求之后都会驻留在内存中,等待后续的请求。
一个Servlet应用程序经常包括一个或者多个Servlet,Servlet是运行在Servlet容器中的,它不能自动运行。Servlet容器又是什么东西?我的理解就是实现了Servlet规范,然后暴露出来一些编程的接口供我们实现自己的逻辑,所以每个自定义的Servlet类都要实现Servlet接口,它能够生成动态的内容,不向Web服务器一样,只是提供静态资源。更细节的内容看如下的相关文章。Servlet容器将用户的请求传给Servlet应用程序,并将Servlet应用程序的响应回传给用户。比较流行的Servlet容器有Tomcat和Jetty。这两个本质上是Servlet容器
,而不是Web服务器,但是它们把Web服务器要做的工作也集成到容器中了,所以不需要额外的Web服务器。
二、 Servlet API
我使用的是Servlet的3.1.0版本,有4个Java包:
javax.servlet:包含Servlet与Servlet容器进行通信需要的类和接口,比如请求、响应、过滤和监听等;
javax.servlet.http:包含Servlet的实现类httpServlet与Servlet容器进行通信需要的类和接口;
javax.servlet.annotation:使用Servlet、Filter和Listener时需要的注解;
javax.servlet.descriptor:提供对配置信息进行编程式访问的类型;
在Servlet技术的核心当然是Servlet接口,每个自定义的类都是直接或者间接实现了这个接口。在这个接口中说明了Servlet怎么被Servlet容器调用,首先容器会把Servlet类加载到内存中,并在Servlet类调用特定的方法,在每个应用程序中,每个Servlet只有一个实例(单实例)。下面试一下Servlet中的方法:

  1. public class TestServlet implements Servlet {  
  2.     private transient ServletConfig servletConfig;  
  3.       
  4.     public void init(ServletConfig config) throws ServletException {  
  5.         this.servletConfig = config;  
  6.         System.out.println(“init”);  
  7.     }  
  8.   
  9.     public ServletConfig getServletConfig() {  
  10.         return servletConfig;  
  11.     }  
  12.   
  13.     public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {  
  14.         System.out.println(“service”);  
  15.     }  
  16.   
  17.     public String getServletInfo() {  
  18.         return this.servletConfig.getServletName();  
  19.     }  
  20.   
  21.     public void destroy() {  
  22.         System.out.println(“destroy”);  
  23.     }  
  24. }  

除了要自定义一个Servlet类,我们还要想一个办法来确定怎么访问到我们自定义的Servlet?显然要指定url,有两种方法:
通过注解,比较简单,直接在类上加上注解:

  1. @WebServlet(name=”testServlet”, urlPatterns=”/testServlet”,loadOnStartup=-1)  
  2. public class TestServlet implements Servlet {}  

通过web.xml配置:

  1. <servlet>  
  2.     <servlet-name>TestServlet</servlet-name>  
  3.     <servlet-class>com.javaservlet.servlet.TestServlet</servlet-class>  
  4. </servlet>  
  5. <servlet-mapping>  
  6.     <servlet-name>TestServlet</servlet-name>  
  7.     <url-pattern>/TestServlet</url-pattern>  
  8. </servlet-mapping>  

注意:
配置url-pattern时,可以对一个Servlet配置多个url,也就是可以通过多个url来访问同一个servlet;
配置url-pattern时,也可以使用通配符进行配置,但是只有两种固定方法:一是“*.扩展名”,类似“*.html”;另外一个则是以“/”开头,以“/*”结尾,类似“/test/*”;
启动Servlet容器,这里我们使用Tomcat,经过多次使用浏览器发送请求,输出如下:

金沙城中心赌场 2

Servlet接口中有三个生命周期方法:
init():刚启动Servlet容器时不会调用这个方法,当第一次请求来的时候才会调用该方法,其实什么时候调用由loadOnStartup属性决定,如果是非0值则该方法会在容器启动时被调用,但是该值默认是-1,在后续的请求时不会再调用该方法,所以说在该方法中进行程序初始化相关的代码,另外值得注意的是该方法总是在servlet的构造器之后运行;
service():每次请求Servlet时候,Servlet容器会调用这个方法,是具体编写业务逻辑的地方;
destroy():销毁Servlet的时候,Servlet容器会调用这个方法,发生在卸载应用程序或者关闭Servlet容器时,另外从上面的输出来看,每次修改代码后保存都会reload,这个时候其实销毁该Servlet类,关闭Servlet容器并重新开启,可以在这个方法中进行资源清理的工作;
该接口中还有其他两个非生命周期的方法:getServletInfo()感觉不是很常用,只是返回对Servlet的描述;getServletConfig()返回init()传入的ServletConfig类实例,是关于Servlet的配置信息。
由于Servlet实例是单实例,在每一个应用程序中,多次请求都是针对一个对象,很容易造成并发问题,所以可以把它当做只读的,或者使用java并发机制中成员。

写Web应用时,直接用的spring框架,没有接触过Servlet,于是遇到问题不知道根本原因,所以还是学一下吧!
一、 什么是…

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图