异步
异步请求
同步请求:同一个请求由一个线程从头到尾进行处理 一步到位 异步请求:同一个请求中由多个线程进行处理 处理的线程先释放资源 之后通过回调机制处理
场景 红烧猪蹄 : 高压锅压猪脚那么费时间、这个时候我们可以去做其他事情 等到猪蹄压好了再继续烹饪
如果是直接拿锅煮 不仅费时还要经常搅动 查看是否熟透 浪费时间又耗时 这个时候没有反馈机制 只能自己照看 就很麻烦
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());
}
网友评论