Servlet Async 异步调用处理方式

异步调用

异步调用前,我们说说它对应的同步调用。通常开发过程中,一般上我们都是同步调用,即:程序按定义的顺序依次执行的过程,每一行代码执行过程必须等待上一行代码执行完毕后才执行。而异步调用指:程序在执行时,无需等待执行的返回值可继续执行后面的代码。显而易见,同步有依赖相关性,而异步没有,所以异步可并发执行,可提高执行效率,在相同的时间做更多的事情。

回调:处理异步同步外,还有一个叫回调。其主要是解决异步方法执行结果的处理方法,比如在希望异步调用结束时返回执行结果,这个时候就可以考虑使用回调机制。

原生异步请求API说明

在编写实际代码之前,我们来了解下一些关于异步请求的api的调用说明。

  • 获取AsyncContext:根据HttpServletRequest对象获取。
1
AsyncContext asyncContext = request.startAsync();
  • 设置监听器:可设置其开始、完成、异常、超时等事件的回调处理

其监听器的接口代码:

1
2
3
4
5
6
public interface AsyncListener extends EventListener {
     void onComplete(AsyncEvent event) throws IOException;
     void onTimeout(AsyncEvent event) throws IOException;
     void onError(AsyncEvent event) throws IOException;
     void onStartAsync(AsyncEvent event) throws IOException;
}

说明:

  1. onStartAsync:异步线程开始时调用
  2. onError:异步线程出错时调用
  3. onTimeout:异步线程执行超时调用
  4. onComplete:异步执行完毕时调用

一般上,我们在超时或者异常时,会返回给前端相应的提示,比如说超时了,请再次请求等等,根据各业务进行自定义返回。同时,在异步调用完成时,一般需要执行一些清理工作或者其他相关操作。

需要注意的是只有在调用request.startAsync前将监听器添加到AsyncContext,监听器的onStartAsync方法才会起作用,而调用startAsyncAsyncContext还不存在,所以第一次调用startAsync是不会被监听器中的onStartAsync方法捕获的,只有在超时后又重新开始的情况下onStartAsync方法才会起作用。

  • 设置超时:通过setTimeout方法设置,单位:毫秒。

一定要设置超时时间,不能无限等待下去,不然和正常的请求就一样了。。

Servlet方式实现异步请求

package com.example.demo.Servlet;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 使用Servlet方式进行异步请求
 */
@Slf4j
@RestController
public class ServletController {

    @RequestMapping(value = "/servlet/orig")
    public void todo(HttpServletRequest request, HttpServletResponse response) throws InterruptedException, IOException {
        response.setContentType("ext/html;charset=UTF-8");
        Thread.sleep(1000);
        response.getWriter().print("这是【正常】的请求返回");
    }

    @RequestMapping(value = "/servlet/async")
    public void todoAsync(HttpServletRequest request, HttpServletResponse response) {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                log.info("执行完成");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                log.info("超时了");
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                log.info("发生错误");
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                log.info("线程开始");
            }
        });
        asyncContext.setTimeout(20000);
        asyncContext.start(() -> {
            try {
                Thread.sleep(10000);
                System.out.println("ddddd");
                log.info("内部线程:"+Thread.currentThread().getName());
                asyncContext.getResponse().setCharacterEncoding("utf-8");
                asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                asyncContext.getResponse().getWriter().print("这是【异步】的请求返回");

            }catch (Exception e){
                log.error("异常",e);
            }
            //异步请求完成通知
            //此时整个请求才完成
            //其实可以利用此特性 进行多条消息的推送 把连接挂起。。
            asyncContext.complete();
        });
        System.out.println("main方法线程");
        //此时之类 request的线程连接已经释放了
        log.info("线程:" + Thread.currentThread().getName());
    }
}

注意:异步请求时,可以利用ThreadPoolExecutor自定义个线程池。

1.启动下应用,查看控制台输出就可以获悉是否在同一个线程里面了。同时,可设置下等待时间,之后就会调用超时回调方法了。大家可自己试试。

猜你喜欢

转载自www.cnblogs.com/h-java/p/10275853.html