异步

作者: 一个好汉 | 来源:发表于2021-07-17 19:49 被阅读0次

异步

异步请求

同步请求:同一个请求由一个线程从头到尾进行处理 一步到位 异步请求:同一个请求中由多个线程进行处理 处理的线程先释放资源 之后通过回调机制处理

场景 红烧猪蹄 : 高压锅压猪脚那么费时间、这个时候我们可以去做其他事情 等到猪蹄压好了再继续烹饪

如果是直接拿锅煮 不仅费时还要经常搅动 查看是否熟透 浪费时间又耗时 这个时候没有反馈机制 只能自己照看 就很麻烦

sequenceDiagram
    红烧猪蹄->>准备猪脚香料: 开始
    准备猪脚香料->>下高压锅蒸: 不下高压锅中午怕是吃不上了
    下高压锅蒸-->>下锅烹饪: 高压锅这段时间我们是没有等待 可以去做其他事情
    下锅烹饪->>红烧猪蹄: 上菜

怎么搞

package cn.gd.cz.hong.springbootlearn.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

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;

/**
 * 异步测试
 * 直接使用request.getAsyncContext()是不行的
 * java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
 * <p>
 * 不配置@WebServlet(urlPatterns = "/async", asyncSupported = true)也是可以的
 */
@Controller
@RequestMapping("/async")
public class AsyncController {

    private static final Logger LOGGER =
            LoggerFactory.getLogger(AsyncController.class);

    @RequestMapping("/req")
    public void req(HttpServletRequest request,
                    HttpServletResponse response) {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                LOGGER.info("complete");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                LOGGER.info("timeout");
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                LOGGER.info("error");
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                LOGGER.info("start");
            }
        });
        //设置超时时间
        asyncContext.setTimeout(200);
        try {
            Thread.sleep(100);
            LOGGER.info("内部线程:" + Thread.currentThread().getName());
            asyncContext.getResponse().setCharacterEncoding("utf-8");
            asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
            asyncContext.getResponse().getWriter().println("这是【异步】的请求返回");
        } catch (Exception e) {
            LOGGER.error("异常:", e);
        }
        //异步请求完成通知
        //此时整个请求才完成
        //其实可以利用此特性 进行多条消息的推送 把连接挂起。。
        asyncContext.complete();
        //此时之类 request的线程连接已经释放了
        LOGGER.info("线程:" + Thread.currentThread().getName());
    }
}

主要是加一个Listener的回调 还有就是记得调用 asyncContext.complete()
不然异步方法不会结束

结果


还有spring自带的异步请求方式 但需要spring5+ 这里不做赘述 有兴趣可参见
异步请求

这篇文章中提到 有一个@WebServlet 可参见Servlet中@WebServlet的作用

异步调用

前言 同步调用: 代码一步一步顺序执行 异步调用: 代码可在不返回的情况执行下一步

奇思妙想: 异步调用=在《让子弹飞》中,张麻子打枪 那不得让子弹飞一会儿呀

配置让异步生效

package cn.gd.cz.hong.springbootlearn.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步调用配置
 * EnableAsync 允许在调用方法的时候使用异步
 * 这里先试一下 没有配置直接调用是个什么情况
 * 没有配置前 使用的是同一个进程
 * 21:19:13.694 [http-nio-9000-exec-1] [INFO ] cn.gd.cz.hong.springbootlearn.controller.AsyncController:85 --- here is controller :http-nio-9000-exec-1
 * 21:19:13.694 [http-nio-9000-exec-1] [INFO ] cn.gd.cz.hong.springbootlearn.service.impl.AsyncServiceImpl:19 --- here is async: http-nio-9000-exec-1
 *
 * 配置了之后 便是用了两个进程进行处理
 * 21:20:47.055 [http-nio-9000-exec-1] [INFO ] cn.gd.cz.hong.springbootlearn.controller.AsyncController:85 --- here is controller :http-nio-9000-exec-1
 * 21:20:47.057 [task-1] [INFO ] cn.gd.cz.hong.springbootlearn.service.impl.AsyncServiceImpl:19 --- here is async: task-1
 *
 * 第二次就是
 * 21:23:03.791 [http-nio-9000-exec-4] [INFO ] cn.gd.cz.hong.springbootlearn.controller.AsyncController:85 --- here is controller :http-nio-9000-exec-4
 * 21:23:03.792 [task-2] [INFO ] cn.gd.cz.hong.springbootlearn.service.impl.AsyncServiceImpl:19 --- here is async: task-2
 *
 * 看到这里进程已经变为2 也就是没有复用 每一次都是新建一个进程给我用
 *
 * 这里配置一个线程池给进程使用 看到线程已经得到了复用 额~ 满意
 * 21:29:47.620 [http-nio-9000-exec-10] [INFO ] cn.gd.cz.hong.springbootlearn.controller.AsyncController:85 --- here is controller :http-nio-9000-exec-10
 * 21:29:47.620 [hong-20] [INFO ] cn.gd.cz.hong.springbootlearn.service.impl.AsyncServiceImpl:19 --- here is async: hong-20
 * 21:29:48.254 [http-nio-9000-exec-1] [INFO ] cn.gd.cz.hong.springbootlearn.controller.AsyncController:85 --- here is controller :http-nio-9000-exec-1
 * 21:29:48.254 [hong-1] [INFO ] cn.gd.cz.hong.springbootlearn.service.impl.AsyncServiceImpl:19 --- here is async: hong-1
 *
 * 回调
 * 可以使用 Future
 */
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "asyncPoolTaskExecutor")
    public ThreadPoolTaskExecutor getAsyncThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(20);
        taskExecutor.setMaxPoolSize(200);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.setKeepAliveSeconds(200);
        taskExecutor.setThreadNamePrefix("hong-");
        // 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调度器shutdown被调用时等待当前被调度的任务完成
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        //等待时长
        taskExecutor.setAwaitTerminationSeconds(60);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

直接调用带有注解@Async的方法即可

@Async("asyncPoolTaskExecutor")
@Override
public void invoke(){
        LOGGER.info("here is async: {}",Thread.currentThread().getName());
        }

相关文章

网友评论

      本文标题:异步

      本文链接:https://www.haomeiwen.com/subject/lcmmultx.html