Java接口異步調用優(yōu)化技巧詳解
在日常項目中,我們經常采用多線程異步調用的方式來提高接口的響應時間。
在實際情況下,我們如何通過異步方式優(yōu)化我們的接口呢,有以下幾種常見思路
1,自己new線程或者線程池
如下我們把三個耗時操作交給新的線程或者線程池執(zhí)行。
當請求過來的時候tomcat線程會等待子線程全部執(zhí)行完成,然后匯總結果進行返回。
/** * 這里會阻塞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,修改單個任務為批量任務
在項目中我們有很多數據庫的查詢,批量查詢要快于單個查詢,中間省了很多io操作。
思考能不能吧單個調用轉換成批量呢,針對并發(fā)比較高的接口。合并多個用戶的調用,轉換成一批進行查詢。
把一個時間段內的請求放進隊列,然后通過定時任務進行批量查詢,然后進行響應分發(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(() -> { //每次運行的時候 去拿MQ中的數據量 int size = queue.size(); if (size == 0) { return; } log.info("批量獲取任務:{}-{}", Thread.currentThread().getName(), size); //多次請求收集到一起一塊去批量請求下面的需要的數據 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):當阻塞隊列滿時,再往隊列里add插入元素會拋IllegalStateException:Queue full * 2.offer(e):插入方法,成功true失敗返回false * 3.put(e):當阻塞隊列滿時,生產者線程繼續(xù)往隊列里添加元素,隊列會一直阻塞生產者線程。直到put數據or響應中斷退出 * 4.offer(e,time,unit):當阻塞隊列滿的時候,隊列會阻塞生產者線程一定時間,超過限時后生產者線程會退出。 * <p> * ● 移除 * 1.remove():當隊列為空的時候,再往隊列里remove移除元素會拋NoSuchElementException * 2.poll():移除方法,成功返回出隊列的元素,隊列里沒有就返回null。 * 3.take():當隊列為空消費者線程試圖從隊列里take元素,隊列會一直阻塞消費者線程知道隊列可用 * 4.poll(time,unit):當隊列為空的時候,會阻塞一段時間超時后消費者線程退出。 * <p> * ● 檢查 * 1.element():當隊列為空時直接拋出異常 * 2.peek():當隊列為空時阻塞 * <p> * 這里不會阻塞tomcat的線程 */ @GetMapping("getAllEgFour") public UserInfo getAllEgFour(Long userId) throws ExecutionException, InterruptedException { if (userId == null) { userId = worker.nextId(); } log.info("開始獲取數據: {}: {}", 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("完成獲取數據: {}: {}", Thread.currentThread().getName(), userInfo); return userInfo; } }
到此這篇關于Java接口異步調用優(yōu)化技巧詳解的文章就介紹到這了,更多相關Java接口異步調用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
multi-catch和try-catch異常處理知識點詳解
在本篇文章里我們給大家分享了一篇關于multi-catch和try-catch異常處理知識點內容,有需要的朋友們可以參考學習下。2019-11-11Spring?Lifecycle?和?SmartLifecycle區(qū)別面試精講
這篇文章主要為大家介紹了Spring?Lifecycle和SmartLifecycle的區(qū)別面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10深入探究SpringBoot中的Elasticsearch自動配置原理及用法
SpringBoot中的Elasticsearch自動配置為我們提供了一種快速集成Elasticsearch的方式,使我們可以在SpringBoot應用程序中輕松地使用Elasticsearch,本文將介紹Spring Boot中的Elasticsearch自動配置的作用、原理和使用方法2023-07-07