Java接口異步調(diào)用優(yōu)化技巧詳解
在日常項(xiàng)目中,我們經(jīng)常采用多線程異步調(diào)用的方式來(lái)提高接口的響應(yīng)時(shí)間。
在實(shí)際情況下,我們?nèi)绾瓮ㄟ^(guò)異步方式優(yōu)化我們的接口呢,有以下幾種常見(jiàn)思路
1,自己new線程或者線程池
如下我們把三個(gè)耗時(shí)操作交給新的線程或者線程池執(zhí)行。
當(dāng)請(qǐng)求過(guò)來(lái)的時(shí)候tomcat線程會(huì)等待子線程全部執(zhí)行完成,然后匯總結(jié)果進(jìn)行返回。
/** * 這里會(huì)阻塞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
我們返回一個(gè)Callable 這時(shí)候會(huì)開啟一個(gè)新的線程不會(huì)阻塞tomcat的線程
/** * 這里不會(huì)阻塞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,修改單個(gè)任務(wù)為批量任務(wù)
在項(xiàng)目中我們有很多數(shù)據(jù)庫(kù)的查詢,批量查詢要快于單個(gè)查詢,中間省了很多io操作。
思考能不能吧單個(gè)調(diào)用轉(zhuǎn)換成批量呢,針對(duì)并發(fā)比較高的接口。合并多個(gè)用戶的調(diào)用,轉(zhuǎn)換成一批進(jìn)行查詢。
把一個(gè)時(shí)間段內(nèi)的請(qǐng)求放進(jìn)隊(duì)列,然后通過(guò)定時(shí)任務(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)行的時(shí)候 去拿MQ中的數(shù)據(jù)量 int size = queue.size(); if (size == 0) { return; } log.info("批量獲取任務(wù):{}-{}", Thread.currentThread().getName(), size); //多次請(qǐng)求收集到一起一塊去批量請(qǐng)求下面的需要的數(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ì)列滿時(shí),再往隊(duì)列里add插入元素會(huì)拋IllegalStateException:Queue full * 2.offer(e):插入方法,成功true失敗返回false * 3.put(e):當(dāng)阻塞隊(duì)列滿時(shí),生產(chǎn)者線程繼續(xù)往隊(duì)列里添加元素,隊(duì)列會(huì)一直阻塞生產(chǎn)者線程。直到put數(shù)據(jù)or響應(yīng)中斷退出 * 4.offer(e,time,unit):當(dāng)阻塞隊(duì)列滿的時(shí)候,隊(duì)列會(huì)阻塞生產(chǎn)者線程一定時(shí)間,超過(guò)限時(shí)后生產(chǎn)者線程會(huì)退出。 * <p> * ● 移除 * 1.remove():當(dāng)隊(duì)列為空的時(shí)候,再往隊(duì)列里remove移除元素會(huì)拋NoSuchElementException * 2.poll():移除方法,成功返回出隊(duì)列的元素,隊(duì)列里沒(méi)有就返回null。 * 3.take():當(dāng)隊(duì)列為空消費(fèi)者線程試圖從隊(duì)列里take元素,隊(duì)列會(huì)一直阻塞消費(fèi)者線程知道隊(duì)列可用 * 4.poll(time,unit):當(dāng)隊(duì)列為空的時(shí)候,會(huì)阻塞一段時(shí)間超時(shí)后消費(fèi)者線程退出。 * <p> * ● 檢查 * 1.element():當(dāng)隊(duì)列為空時(shí)直接拋出異常 * 2.peek():當(dāng)隊(duì)列為空時(shí)阻塞 * <p> * 這里不會(huì)阻塞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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Sentinel的熔斷降級(jí)、資源規(guī)則詳解與實(shí)例
這篇文章主要介紹了Sentinel的熔斷降級(jí)、資源規(guī)則詳解與實(shí)例,Sentinel是阿里巴巴開源的一款流量控制和熔斷降級(jí)的框架,它主要用于保護(hù)分布式系統(tǒng)中的服務(wù)穩(wěn)定性,Sentinel通過(guò)對(duì)服務(wù)進(jìn)行流量控制和熔斷降級(jí),可以有效地保護(hù)系統(tǒng)的穩(wěn)定性,需要的朋友可以參考下2023-09-09multi-catch和try-catch異常處理知識(shí)點(diǎn)詳解
在本篇文章里我們給大家分享了一篇關(guān)于multi-catch和try-catch異常處理知識(shí)點(diǎn)內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。2019-11-11Spring?Lifecycle?和?SmartLifecycle區(qū)別面試精講
這篇文章主要為大家介紹了Spring?Lifecycle和SmartLifecycle的區(qū)別面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java簡(jiǎn)單使用redis-zset實(shí)現(xiàn)排行榜
這篇文章主要介紹了Java簡(jiǎn)單使用redis-zset實(shí)現(xiàn)排行榜,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12SpringBoot利用隨機(jī)鹽值實(shí)現(xiàn)密碼的加密與驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何利用隨機(jī)鹽值實(shí)現(xiàn)密碼的加密與驗(yàn)證,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下2024-02-02利用Spring IOC技術(shù)實(shí)現(xiàn)用戶登錄驗(yàn)證機(jī)制
這篇文章主要為大家詳細(xì)介紹了Spring IOC技術(shù)實(shí)現(xiàn)用戶登錄驗(yàn)證機(jī)制的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Java基礎(chǔ)之淺談hashCode()和equals()
今天給大家?guī)?lái)的是關(guān)于Java基礎(chǔ)的相關(guān)知識(shí),文章圍繞著hashCode()和equals()展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06深入探究SpringBoot中的Elasticsearch自動(dòng)配置原理及用法
SpringBoot中的Elasticsearch自動(dòng)配置為我們提供了一種快速集成Elasticsearch的方式,使我們可以在SpringBoot應(yīng)用程序中輕松地使用Elasticsearch,本文將介紹Spring Boot中的Elasticsearch自動(dòng)配置的作用、原理和使用方法2023-07-07