Spring boot 实现高吞吐量异步处理(适用于高并发场景)
技术要点
org.springframework.web.context.request.async.DeferredResult<T>
示例如下:
1. 新建Maven项目 async
2. pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.java</groupId>
<artifactId>async</artifactId>
<version>1.0.0</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent> <dependencies> <!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- 热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.8.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
</dependency> </dependencies> <build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin> <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3. AsyncStarter.java
package com.java; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class AsyncStarter { public static void main(String[] args) {
SpringApplication.run(AsyncStarter.class, args);
} }
4. AsyncVo.java
package com.java.vo; import org.springframework.web.context.request.async.DeferredResult; /**
* 存储异步处理信息
*
* @author Logen
*
* @param <I> 接口输入参数
* @param <O> 接口返回参数
*/
public class AsyncVo<I, O> { /**
* 请求参数
*/
private I params; /**
* 响应结果
*/
private DeferredResult<O> result; public I getParams() {
return params;
} public void setParams(I params) {
this.params = params;
} public DeferredResult<O> getResult() {
return result;
} public void setResult(DeferredResult<O> result) {
this.result = result;
} }
5. RequestQueue.java
package com.java.queue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import org.springframework.stereotype.Component; import com.java.vo.AsyncVo; /**
* 存放所有异步处理接口请求队列的对象,一个接口对应一个队列
*
* @author Logen
*
*/
@Component
public class RequestQueue { /**
* 处理下订单接口的队列,设置缓冲容量为50
*/
private BlockingQueue<AsyncVo<String, Object>> orderQueue = new LinkedBlockingQueue<>(50); public BlockingQueue<AsyncVo<String, Object>> getOrderQueue() {
return orderQueue;
} }
6. OrderTask.java
package com.java.task; import java.util.HashMap;
import java.util.Map; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.java.queue.RequestQueue;
import com.java.vo.AsyncVo; /**
* 处理订单接口的任务,每个任务类处理一种接口
*
* @author Logen
*
*/
@Component
public class OrderTask extends Thread { @Autowired
private RequestQueue queue; private boolean running = true; @Override
public void run() {
while (running) {
try {
AsyncVo<String, Object> vo = queue.getOrderQueue().take();
System.out.println("[ OrderTask ]开始处理订单"); String params = vo.getParams();
Thread.sleep(3000);
Map<String, Object> map = new HashMap<>();
map.put("params", params);
map.put("time", System.currentTimeMillis()); vo.getResult().setResult(map); System.out.println("[ OrderTask ]订单处理完成");
} catch (InterruptedException e) {
e.printStackTrace();
running = false;
} }
} public void setRunning(boolean running) {
this.running = running;
} }
7. QueueListener.java
package com.java.listener; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import com.java.task.OrderTask; /**
* 队列监听器,初始化启动所有监听任务
*
* @author Logen
*
*/
@Component
public class QueueListener { @Autowired
private OrderTask orderTask; /**
* 初始化时启动监听请求队列
*/
@PostConstruct
public void init() {
orderTask.start();
} /**
* 销毁容器时停止监听任务
*/
@PreDestroy
public void destory() {
orderTask.setRunning(false);
} }
8. OrderController.java
package com.java.controller; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult; import com.java.queue.RequestQueue;
import com.java.vo.AsyncVo; /**
* <blockquote>
*
* <pre>
*
* 模拟下单处理,实现高吞吐量异步处理请求
*
* 1、 Controller层接口只接收请求,不进行处理,而是把请求信息放入到对应该接口的请求队列中
* 2、 该接口对应的任务类监听对应接口的请求队列,从队列中顺序取出请求信息并进行处理
*
* 优点:接口几乎在收到请求的同时就已经返回,处理程序在后台异步进行处理,大大提高吞吐量
*
*
* </pre>
*
* </blockquote>
*
* @author Logen
*
*/
@RestController
public class OrderController { @Autowired
private RequestQueue queue; @GetMapping("/order")
public DeferredResult<Object> order(String number) throws InterruptedException {
System.out.println("[ OrderController ] 接到下单请求");
System.out.println("当前待处理订单数: " + queue.getOrderQueue().size()); AsyncVo<String, Object> vo = new AsyncVo<>();
DeferredResult<Object> result = new DeferredResult<>(); vo.setParams(number);
vo.setResult(result); queue.getOrderQueue().put(vo);
System.out.println("[ OrderController ] 返回下单结果");
return result;
} }
9. 运行 AsyncStarter.java ,启动测试
浏览器输入 http://localhost:8080/order?number=10001
正常情况处理3秒返回,返回结果如下
{"time":1548241500718,"params":"10001"}
观察控制台打印日志,如下所示:
[ OrderController ] 接到下单请求
当前待处理订单数: 0
[ OrderController ] 返回下单结果
[ OrderTask ]开始处理订单
[ OrderTask ]订单处理完成
结论:Controller层几乎在接收到请求的同时就已经返回,处理程序在后台异步处理任务。
快速多次刷新浏览器,目的为了高并发测试,观察控制台打印信息
现象:Controller层快速返回,待处理请求在队列中开始增加,异步处理程序在按顺序处理请求。
优点:对客户端响应时间不变,但提高了服务端的吞吐量。大大提升高并发处理性能!
.
Spring boot 实现高吞吐量异步处理(适用于高并发场景)的更多相关文章
- spring boot:使用log4j2做异步日志打印(spring boot 2.3.1)
一,为什么要使用log4j2? log4j2是log4j的升级版, 升级后更有优势: 性能更强/吞吐量大/支持异步 功能扩展/支持插件/支持自定义级别等 这些优 ...
- Spring boot 使用WebAsyncTask处理异步任务
上文介绍了基于 @Async 注解的 异步调用编程,本文将继续引入 Spring Boot 的 WebAsyncTask 进行更灵活异步任务处理,包括 异步回调,超时处理 和 异常处理. 正文 1. ...
- Spring Boot使用@Async实现异步调用
原文:http://blog.csdn.net/a286352250/article/details/53157822 项目GitHub地址 : https://github.com/FrameRes ...
- Spring Boot任务(定时,异步,邮件)
一.定时任务 开启定时任务(在Spring Boot项目主程序上添加如下注解) @EnableScheduling //开启定时任务的注解 创建定时任务(创建一个Service如下) @Service ...
- Spring Boot使用@Async实现异步调用:自定义线程池
前面的章节中,我们介绍了使用@Async注解来实现异步调用,但是,对于这些异步执行的控制是我们保障自身应用健康的基本技能.本文我们就来学习一下,如果通过自定义线程池的方式来控制异步调用的并发. 定义线 ...
- Spring boot参考指南
介绍 转载自:https://www.gitbook.com/book/qbgbook/spring-boot-reference-guide-zh/details 带目录浏览地址:http://ww ...
- 蚂蚁开源的 SOFABoot,和 Spring Boot 有啥关系?
一.SOFABoot 是什么鬼? 说到 SOFABoot,不得不先说下 SOFARPC 框架,SOFARPC 也是大名远扬,最早起源于阿里淘宝 HSF 框架,现在是蚂蚁金服开源的一款高性能.高可扩展性 ...
- 是时候为Spring Boot 3.0做准备了
2018年2月28日Spring Boot进入2.0时代,距今已经超过4年了. 2022 年 11 月 Spring Boot 3.0 将正式发布,它将基于 Spring Framework 6.0, ...
- Spring boot 1.3.5 RELEASE 官方文档中文翻译--Part2:新手入门
Part II. 新手入门 如果你刚刚开始学习Spring boot或"普通"的Spring,这部分非常适合你!在这里,我们回答了最基础的"什么是?".&quo ...
随机推荐
- python 基础内置函数表及简单介绍
内建函数名 (表达形式) 主要作用 备注 abs(x) 返回一个X值得绝对值(x=int/float/复数) all(iterable) 如果 iterable 的所有元素均为 True(或 iter ...
- Selenium2(WebDriver)中执行JavaScript代码 (转)
在用selenium编写web页面的自动化测试代码时,可能需要执行一些JavaScript代码,selenium本身就支持执行js,我们在代码中可以使用executeScript.executeAsy ...
- android Binder机制(一)架构设计
Binder 架构设计 Binder 被设计出来是解决 Android IPC(进程间通信) 问题的.Binder 将两个进程间交互的理解为 Client 向 Server 进行通信. 如下:bind ...
- Indexing the World Wide Web: the Journey So Far阅读笔记
文献文档用google搜索标题即可. term预处理:用空格切分,去除标点,去除撇号,归一化小写,去除变音符号,词干还原(?),去除停用词,挖掘词组. 索引选型工程最佳实践:term粒度.按doc分块 ...
- Android中的时间格式的校验
public class MainActivity extends Activity implements OnClickListener{ private Button btn1; private ...
- CAD安装失败怎样卸载CAD 2012?错误提示某些产品无法安装
AUTODESK系列软件着实令人头疼,安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...
- [转]FireFox与IE 下js兼容触发click事件的代码
本文转自:http://www.jb51.net/article/16549.htm FireFox与IE 下js兼容触发click事件 ,对于需要兼容这两者的朋友,就需要参考下下面的代码了<a ...
- ubuntu 16.04下搜狗输入法不能输入中文解决
之前一段时间正常使用的搜狗输入法突然无法输出中文(具体现象是,可以呼出搜狗输入法界面,但是候选词列表无显示),解决之后记录下来,希望能为同样遇到这个问题的人提供参考.同时附linux下常见软件崩溃问题 ...
- Oracle之RMAN备份及还原
RMAN可以进行增量备份:数据库,表空间,数据文件 只有使用过的block可以被备份成backup set 表空间与数据文件对应关系:dba_data_files / v$datafile_heade ...
- BNU 26349——Cards——————【区间dp】
题目大意:给你n张牌,排成一排放在桌子上,可以从左端拿也可以从右端拿.现在有A,B两人轮流取牌,A先取,两人足够聪明,即都想取最大的牌总和,问A能取到的最大值. 解题思路:定义dp[i][j][k]. ...