Springboot實(shí)現(xiàn)高吞吐量異步處理詳解(適用于高并發(fā)場(chǎng)景)
技術(shù)要點(diǎn)
org.springframework.web.context.request.async.DeferredResult<T>
示例如下:
1. 新建Maven項(xiàng)目 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; /** * 存儲(chǔ)異步處理信息 * * @author Logen * * @param <I> 接口輸入?yún)?shù) * @param <O> 接口返回參數(shù) */ public class AsyncVo<I, O> { /** * 請(qǐng)求參數(shù) */ private I params; /** * 響應(yīng)結(jié)果 */ 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; /** * 存放所有異步處理接口請(qǐng)求隊(duì)列的對(duì)象,一個(gè)接口對(duì)應(yīng)一個(gè)隊(duì)列 * * @author Logen * */ @Component public class RequestQueue { /** * 處理下訂單接口的隊(duì)列,設(shè)置緩沖容量為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; /** * 處理訂單接口的任務(wù),每個(gè)任務(wù)類處理一種接口 * * @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 ]開(kāi)始處理訂單"); 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; /** * 隊(duì)列監(jiān)聽(tīng)器,初始化啟動(dòng)所有監(jiān)聽(tīng)任務(wù) * * @author Logen * */ @Component public class QueueListener { @Autowired private OrderTask orderTask; /** * 初始化時(shí)啟動(dòng)監(jiān)聽(tīng)請(qǐng)求隊(duì)列 */ @PostConstruct public void init() { orderTask.start(); } /** * 銷毀容器時(shí)停止監(jiān)聽(tīng)任務(wù) */ @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> * * 模擬下單處理,實(shí)現(xiàn)高吞吐量異步處理請(qǐng)求 * * 1、 Controller層接口只接收請(qǐng)求,不進(jìn)行處理,而是把請(qǐng)求信息放入到對(duì)應(yīng)該接口的請(qǐng)求隊(duì)列中 * 2、 該接口對(duì)應(yīng)的任務(wù)類監(jiān)聽(tīng)對(duì)應(yīng)接口的請(qǐng)求隊(duì)列,從隊(duì)列中順序取出請(qǐng)求信息并進(jìn)行處理 * * 優(yōu)點(diǎn):接口幾乎在收到請(qǐng)求的同時(shí)就已經(jīng)返回,處理程序在后臺(tái)異步進(jìn)行處理,大大提高吞吐量 * * * </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 ] 接到下單請(qǐng)求"); System.out.println("當(dāng)前待處理訂單數(shù): " + 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 ] 返回下單結(jié)果"); return result; } }
9. 運(yùn)行 AsyncStarter.java ,啟動(dòng)測(cè)試
瀏覽器輸入 http://localhost:8080/order?number=10001
正常情況處理3秒返回,返回結(jié)果如下
{"time":1548241500718,"params":"10001"}
觀察控制臺(tái)打印日志,如下所示:
[ OrderController ] 接到下單請(qǐng)求 當(dāng)前待處理訂單數(shù): 0 [ OrderController ] 返回下單結(jié)果 [ OrderTask ]開(kāi)始處理訂單 [ OrderTask ]訂單處理完成
結(jié)論:Controller層幾乎在接收到請(qǐng)求的同時(shí)就已經(jīng)返回,處理程序在后臺(tái)異步處理任務(wù)。
快速多次刷新瀏覽器,目的為了高并發(fā)測(cè)試,觀察控制臺(tái)打印信息
現(xiàn)象:Controller層快速返回,待處理請(qǐng)求在隊(duì)列中開(kāi)始增加,異步處理程序在按順序處理請(qǐng)求。
優(yōu)點(diǎn):對(duì)客戶端響應(yīng)時(shí)間不變,但提高了服務(wù)端的吞吐量。大大提升高并發(fā)處理性能!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
用html css javascript打造自己的RIA圖文教程
用html&css&javascript打造自己的RIA之一,包括了配置等2009-07-07java警告:源發(fā)行版17 需要目標(biāo)發(fā)行版17問(wèn)題及解決
文章介紹了如何解決項(xiàng)目JDK版本不一致的問(wèn)題,包括修改Project Structure、Modules、Dependencies和Settings中的JDK版本,以及在pom.xml中指定JDK源版本2024-11-11springboot項(xiàng)目不同環(huán)境的配置讀取方式
SpringBoot支持application.properties、application.yml、application.yaml三種配置文件類型,可同時(shí)存在并合并配置,配置文件的讀取優(yōu)先級(jí)為:application.properties > application.yml > application.yaml,不同位置的相同類型配置文件2024-11-11Java設(shè)計(jì)模式之代理模式原理及實(shí)現(xiàn)代碼分享
這篇文章主要介紹了Java設(shè)計(jì)模式之代理模式原理及實(shí)現(xiàn)代碼分享,設(shè)計(jì)代理模式的定義,靜態(tài)代理,動(dòng)態(tài)代理,jdk動(dòng)態(tài)代理實(shí)現(xiàn)步驟,原理及源碼等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11java EasyExcel實(shí)現(xiàn)動(dòng)態(tài)列解析和存表
這篇文章主要為大家介紹了java EasyExcel實(shí)現(xiàn)動(dòng)態(tài)列解析和存表示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Spring Boot 利用WebUploader進(jìn)行文件上傳功能
本文的重點(diǎn)是給大家介紹在Spring Boot項(xiàng)目中利用WebUploader如何進(jìn)行文件上傳,本文通過(guò)示例代碼給大家介紹,需要的朋友參考下吧2018-03-03一篇文章帶了解如何用SpringBoot在RequestBody中優(yōu)雅的使用枚舉參數(shù)
這篇文章主要介紹了SpringBoot中RequestBodyAdvice使用枚舉參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08