Spring MVC 对于异步请求处理的两种方式

场景: Tomcat对于主线程性能瓶颈,当Tomcat请求并发数过多时,当线程数满时,就会出现请求等待Tomcat处理,这个时候可以使用子线程处理业务逻辑,主线程只是处理返回请求,这样可以大大提高Tomcat的吞吐量。

1. Callable

1. 使用Callable返回异步信息

- 对于前台用户来说,只是一个同步的请求,根本感觉不到后台的异步处理。

- 后台直接返回Callable,是由Tomcat返回给前台, 而Callable数据会等待返回结果给前台。

- 代码

    //简单的Mapping映射请求
@GetMapping("order")
public Callable<String> order1 () {
_LOGGER.info("主线程开始");
Callable<String> callable = () -> {
_LOGGER.info("副线程启动");
//模拟业务逻辑-需要一秒来处理
TimeUnit.SECONDS.sleep(1);
_LOGGER.info("副线程返回");
return "success";
};
_LOGGER.info("主线程返回");
return callable;
}
//日志输出,可以看到主线程直接返回, 而子线程等待一秒后,这时候前台返回数据
//2018-04-11 11:27:54.584 INFO 1912 --- [nio-8010-exec-8] org.ko.web.async.AsyncController : 主线程开始
//2018-04-11 11:27:54.584 INFO 1912 --- [nio-8010-exec-8] org.ko.web.async.AsyncController : 主线程返回
//2018-04-11 11:27:54.592 INFO 1912 --- [ MvcAsync1] org.ko.web.async.AsyncController : 副线程启动
//2018-04-11 11:27:55.592 INFO 1912 --- [ MvcAsync1] org.ko.web.async.AsyncController : 副线程返回
- 使用Callable 对于前台来说是和正常请求一样的,对于后台来说却可以大大增加Tomcat吞吐量。

当然有些场景, Callable并不能解决,比如说:我们访问A接口,A接口调用三方的服务,服务回调B接口,这种情况就没办法使用Callable了,这个时候可以使用DeferredResult

2. DeferredResult

2. 使用DeferredResult异步处理复杂场景,线程间数据传递

- DeferredResult: 对于用户来说,只是一个同步请求,而后台是分开两个接口。

- 同Callable一样,直接返回给前台,后续再像DeferredResult中放入值,前台直接获取数据。

- 代码

    @GetMapping("order")
public DeferredResult<String> order () throws InterruptedException {
_LOGGER.info("主线程开始");
String orderNumber = RandomStringUtils.randomNumeric(8);
mockQueue.setPlaceOrder(orderNumber);
DeferredResult<String> result = new DeferredResult<>();
deferredResultHolder.getMap().put(orderNumber, result);
return result;
}
- PlaceOrder中创建线程模拟双接口
public void setPlaceOrder(String placeOrder) throws InterruptedException {
_LOGGER.info("接到下单请求");
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.completeOrder = placeOrder;
_LOGGER.info("下单请求处理完毕");
}).start();
}
- 实现ApplicationListener模拟回调,最后返回给前台
    @Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{ private static final Logger _LOGGER = LoggerFactory.getLogger(QueueListener.class); @Autowired
private MockQueue mockQueue; @Autowired DeferredResultHolder deferredResultHolder; @Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
new Thread(() -> {
while (true) {
if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
String orderNumber = mockQueue.getCompleteOrder();
_LOGGER.info("返回订单处理结果: {}", orderNumber);
deferredResultHolder.getMap().get(orderNumber).setResult("place order success;");
mockQueue.setCompleteOrder(null);
} else {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
- Holder实现
    @Component
public class DeferredResultHolder {
/**
* 订单处理结果
*/
private Map<String, DeferredResult<String>> map = new HashMap<>();
public Map<String, DeferredResult<String>> getMap() {
return map;
} public void setMap(Map<String, DeferredResult<String>> map) {
this.map = map;
}
}
- DeferredResult比较适合一些比较复杂的业务场景,提升性能。
- 有个问题,当使用分布式部署,调用链走的不是同一个实例时,DeferredResult的处理有可能会出现问题。

3. 异步调优

  • 对异步处理调优的一些参数配置,Spring默认异步线程是不使用线程池的,可以自己设定一些可以重用的线程

  • 继承WebMvcConfigurerAdapter重写configureAsyncSupport()方法

    //@Configuration
public class WebConfig extends WebMvcConfigurerAdapter { @Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
//注册callable拦截器
configurer.registerCallableInterceptors();
//注册deferredResult拦截器
configurer.registerDeferredResultInterceptors()
//异步请求超时时间
configurer.setDefaultTimeout()
//设定异步请求线程池callable等, spring默认线程不可重用
configurer.setTaskExecutor()
}
}

4. 代码

Spring Web Async异步处理#Callable #DeferredResult的更多相关文章

  1. Spring Boot @Async 异步任务执行

    1.任务执行和调度 Spring用TaskExecutor和TaskScheduler接口提供了异步执行和调度任务的抽象. Spring的TaskExecutor和java.util.concurre ...

  2. 利用Spring的@Async异步处理改善web应用中耗时操作的用户体验

    Web应用中,有时会遇到一些耗时很长的操作(比如:在后台生成100张报表再呈现,或 从ftp下载若干文件,综合处理后再返回给页面下载),用户在网页上点完按钮后,通常会遇到二个问题:页面超时.看不到处理 ...

  3. spring boot @Async异步注解上下文透传

    上一篇文章说到,之前使用了@Async注解,子线程无法获取到上下文信息,导致流量无法打到灰度,然后改成 线程池的方式,每次调用异步调用的时候都手动透传 上下文(硬编码)解决了问题. 后面查阅了资料,找 ...

  4. 使用spring的@Async异步执行方法

    应用场景: 1.某些耗时较长的而用户不需要等待该方法的处理结果 2.某些耗时较长的方法,后面的程序不需要用到这个方法的处理结果时 在spring的配置文件中加入对异步执行的支持 <beans x ...

  5. Spring Boot Async异步执行

    异步调用就是不用等待结果的返回就执行后面的逻辑,同步调用则需要等带结果再执行后面的逻辑. 通常我们使用异步操作都会去创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如下: Execut ...

  6. spring的@Async异步使用

    pring的@Async功能,用的时候一定要注意: 1.异步方法和调用类不要在同一个类中. 2.xml里需要加入这一行 <task:annotation-driven/> 下面的可以直接粘 ...

  7. SpringMVC异步调用,Callable和DeferredResult的使用

    Callable和DeferredResult都是springMVC里面的异步调用,Callable主要用来处理一些简单的逻辑,DeferredResult主要用于处理一些复杂逻辑 1.Callabl ...

  8. spring boot(17)-@Async异步

    验证码的异步机制 上一篇讲过可以用邮件发验证码,通常我们在某网站发验证码时,首先会提示验证码已发送,请检查邮箱或者短信,这就是图中的1和3.然而此时查看邮箱或短信可能并没有收到验证码,往往要过几秒种才 ...

  9. Spring Boot (18) @Async异步

    通常我们在某网站发送邮件验证码时,首先会提示验证码已发送,然而此时可能没有收到验证码,过几秒种才真正的收到.如果是同步会先验证发送是否成功然后再通知,如果是异步可以先通知用户已发送,并释放请求,然后再 ...

随机推荐

  1. maven-windows使用

    目录 前言 安装 配置镜像 idea配置Maven 私服 安装到centos 访问 客户端配置私服 idea修改指定pom 项目发布到私服 jar包会自动从私服下载 从客户端导入第三方jar包 前言 ...

  2. SQL Server Mobile/Compact Edition 简单介绍

    除了SQL Server Express,SQL Server还有个更轻量级的版本:SQL Server Compact Edition,容易让人想起Windows Compact Edition ( ...

  3. 四、C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 .NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作,下面就 ...

  4. olivehc--百度开源的cdn cache

    github 地址:http://git.baidu.com/olivehc/olivehc 主要是为了方便管理,百度cdn承载了全百度40%的流量,但是cdn团队只有几个人(一次培训中提到只有4个) ...

  5. IOS 登录信息类(使用单例)

    WCUserInfo.h #import <Foundation/Foundation.h> #import "Singleton.h" @interface WCUs ...

  6. 【[IOI2005]Riv 河流】

    趁魏佬去英语演讲了,赶快%%%%%%%%%%%%%%魏佬 基本上是照着魏佬的代码写的 这其实还是一个树上背包 我们用\(dp[i][j][k]\)表示在以\(i\)为根的子树里,我们修建\(k\)个伐 ...

  7. luogu P2015 二叉苹果树

    嘟嘟嘟 这应该算一道树形背包吧,虽然我还是分不太清树形背包和树形dp的区别…… 首先dp[i][u][j] 表示在u的前 i 棵子树中,留了 j 条树枝时最大的苹果数量,而且根据题目描述,这些留下的树 ...

  8. luogu P1272 重建道路

    嘟嘟嘟 这好像是一种树上背包. 我们令dp[i][j] 表示在 i 所在的子树中(包括节点 i)分离出一个大小为 j 的子树最少需割多少条边. 那么转移方程就是 dp[u][j] = min(dp[u ...

  9. 3、Web Service-Jaxws(Eclipse版本)

    1.概述 开发手段: 使用jdk开发(1.6及以上的版本) 使用CXF框架开发 组成: -服务器端 -客户端 2.使用JDK开发 1).服务器端 -@WebService(SEI和SEI的实现类) - ...

  10. (转)进程process和线程thread的关系

    写的很好很明白cpu每次只能执行一个进程,所以其他进程会挂起 在一个进程中,允许存在n个线程,n个线程共享这个进程中的资源 多个线程在共享的时候存在资源互斥,一次只能一个线程,会需要加锁 一次存在固定 ...