Java接口異步調(diào)用優(yōu)化技巧詳解
在日常項(xiàng)目中,我們經(jīng)常采用多線程異步調(diào)用的方式來提高接口的響應(yīng)時間。
在實(shí)際情況下,我們?nèi)绾瓮ㄟ^異步方式優(yōu)化我們的接口呢,有以下幾種常見思路
1,自己new線程或者線程池
如下我們把三個耗時操作交給新的線程或者線程池執(zhí)行。
當(dāng)請求過來的時候tomcat線程會等待子線程全部執(zhí)行完成,然后匯總結(jié)果進(jìn)行返回。
/**
* 這里會阻塞tomcat的線程
*/
@GetMapping("getAllEgOne")
public Map<String, Object> getAllEgOne() throws ExecutionException, InterruptedException {
FutureTask<String> stringFutureTaskOne = new FutureTask<>(asyncService::getOne);
FutureTask<String> stringFutureTaskTwo = new FutureTask<>(asyncService::getTwo);
FutureTask<String> stringFutureTaskThree = new FutureTask<>(asyncService::getThree);
new Thread(stringFutureTaskOne).start();
new Thread(stringFutureTaskTwo).start();
new Thread(stringFutureTaskThree).start();
Map<String, Object> result = new HashMap<>();
result.put("one", stringFutureTaskOne.get());
result.put("two", stringFutureTaskTwo.get());
result.put("three", stringFutureTaskThree.get());
return result;
}2,Sping Mvc
我們返回一個Callable 這時候會開啟一個新的線程不會阻塞tomcat的線程
/**
* 這里不會阻塞tomcat的線程
*/
@GetMapping("getAllEgTwo")
public Callable<Map<String, Object>> getAllEgTwo() {
return () -> {
FutureTask<String> stringFutureTaskOne = new FutureTask<>(asyncService::getOne);
FutureTask<String> stringFutureTaskTwo = new FutureTask<>(asyncService::getTwo);
FutureTask<String> stringFutureTaskThree = new FutureTask<>(asyncService::getThree);
new Thread(stringFutureTaskOne).start();
new Thread(stringFutureTaskTwo).start();
new Thread(stringFutureTaskThree).start();
Map<String, Object> result = new HashMap<>(3);
result.put("one", stringFutureTaskOne.get());
result.put("two", stringFutureTaskTwo.get());
result.put("three", stringFutureTaskThree.get());
return result;
};
}3,修改單個任務(wù)為批量任務(wù)
在項(xiàng)目中我們有很多數(shù)據(jù)庫的查詢,批量查詢要快于單個查詢,中間省了很多io操作。
思考能不能吧單個調(diào)用轉(zhuǎn)換成批量呢,針對并發(fā)比較高的接口。合并多個用戶的調(diào)用,轉(zhuǎn)換成一批進(jìn)行查詢。
把一個時間段內(nèi)的請求放進(jìn)隊(duì)列,然后通過定時任務(wù)進(jìn)行批量查詢,然后進(jìn)行響應(yīng)分發(fā)。
import com.example.demo.conf.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* @author liwenchao
*/
@RestController
@RequestMapping("async")
@Slf4j
public class AsyncController {
@Autowired
private AsyncService asyncService;
private final SnowFlake worker = new SnowFlake(1, 1, 1);
private final LinkedBlockingQueue<RequestBody<Long, UserInfo>> queue = new LinkedBlockingQueue<>();
@PostConstruct
public void doWork() {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(() -> {
//每次運(yùn)行的時候 去拿MQ中的數(shù)據(jù)量
int size = queue.size();
if (size == 0) {
return;
}
log.info("批量獲取任務(wù):{}-{}", Thread.currentThread().getName(), size);
//多次請求收集到一起一塊去批量請求下面的需要的數(shù)據(jù)
List<Long> requestBodyList = new ArrayList<>();
List<RequestBody<Long, UserInfo>> requestBodies = new ArrayList<>();
for (int i = 0; i < size; i++) {
RequestBody<Long, UserInfo> requestBody = queue.poll();
requestBodies.add(requestBody);
Long requestParam = requestBody.getRequestParam();
requestBodyList.add(requestParam);
}
List<UserInfo> fourBatch;
try {
fourBatch = asyncService.getFourBatch(requestBodyList);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (CollectionUtils.isEmpty(fourBatch)) {
return;
}
for (UserInfo x : fourBatch) {
for (RequestBody<Long, UserInfo> y : requestBodies) {
if (x.getId().equals(y.getRequestParam())) {
y.getResult().complete(x);
break;
}
}
}
}, 1000L, 50L, TimeUnit.MILLISECONDS);
}
/**
* ● 插入
* 1.add(e):當(dāng)阻塞隊(duì)列滿時,再往隊(duì)列里add插入元素會拋IllegalStateException:Queue full
* 2.offer(e):插入方法,成功true失敗返回false
* 3.put(e):當(dāng)阻塞隊(duì)列滿時,生產(chǎn)者線程繼續(xù)往隊(duì)列里添加元素,隊(duì)列會一直阻塞生產(chǎn)者線程。直到put數(shù)據(jù)or響應(yīng)中斷退出
* 4.offer(e,time,unit):當(dāng)阻塞隊(duì)列滿的時候,隊(duì)列會阻塞生產(chǎn)者線程一定時間,超過限時后生產(chǎn)者線程會退出。
* <p>
* ● 移除
* 1.remove():當(dāng)隊(duì)列為空的時候,再往隊(duì)列里remove移除元素會拋NoSuchElementException
* 2.poll():移除方法,成功返回出隊(duì)列的元素,隊(duì)列里沒有就返回null。
* 3.take():當(dāng)隊(duì)列為空消費(fèi)者線程試圖從隊(duì)列里take元素,隊(duì)列會一直阻塞消費(fèi)者線程知道隊(duì)列可用
* 4.poll(time,unit):當(dāng)隊(duì)列為空的時候,會阻塞一段時間超時后消費(fèi)者線程退出。
* <p>
* ● 檢查
* 1.element():當(dāng)隊(duì)列為空時直接拋出異常
* 2.peek():當(dāng)隊(duì)列為空時阻塞
* <p>
* 這里不會阻塞tomcat的線程
*/
@GetMapping("getAllEgFour")
public UserInfo getAllEgFour(Long userId) throws ExecutionException, InterruptedException {
if (userId == null) {
userId = worker.nextId();
}
log.info("開始獲取數(shù)據(jù): {}: {}", Thread.currentThread().getName(), userId);
RequestBody<Long, UserInfo> objectObjectRequestBody = new RequestBody<>();
CompletableFuture<UserInfo> completableFuture = new CompletableFuture<>();
objectObjectRequestBody.setRequestParam(userId);
objectObjectRequestBody.setResult(completableFuture);
queue.add(objectObjectRequestBody);
UserInfo userInfo = completableFuture.get();
log.info("完成獲取數(shù)據(jù): {}: {}", Thread.currentThread().getName(), userInfo);
return userInfo;
}
}到此這篇關(guān)于Java接口異步調(diào)用優(yōu)化技巧詳解的文章就介紹到這了,更多相關(guān)Java接口異步調(diào)用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目如何使用多線程執(zhí)行定時任務(wù)
在SpringBoot項(xiàng)目中使用多線程執(zhí)行定時任務(wù),可以避免一個耗時任務(wù)影響其他任務(wù),通過配置線程池任務(wù)調(diào)度器,Spring會自動使用多線程執(zhí)行定時任務(wù)2025-01-01
springboot整合mybatis-plus逆向工程的實(shí)現(xiàn)
這篇文章主要介紹了springboot整合mybatis-plus逆向工程的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Spring Boot console log 格式自定義方式
這篇文章主要介紹了Spring Boot console log 格式自定義方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
一文搞懂Java?ScheduledExecutorService的使用
JUC包(java.util.concurrent)中提供了對定時任務(wù)的支持,即ScheduledExecutorService接口。本文主要對ScheduledExecutorService的使用進(jìn)行簡單的介紹,需要的可以參考一下2022-11-11
解決SpringBoot集成Eureka導(dǎo)致返回結(jié)果由json變?yōu)閤ml的問題
這篇文章主要介紹了解決SpringBoot集成Eureka導(dǎo)致返回結(jié)果由json變?yōu)閤ml的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07

