紀錄一下因壓力測試做過各種調整
就用預設的 tomcat ,我懶得換,其他的我也不熟
反正 tomcat 資料好找很多
server: tomcat: max-threads: 256 min-spare-threads: 64
有用,但是很難抓數值要配多少
這兩個值預設都是0,但在實際壓測下來可以發現會越測越快,感覺是有在擴充 pool 的大小
在現代很多擁有動態擴充能力的架構下,固定值實在不是很好用,就讓他吃到 cpu 80% 後自動水平擴展會是比較好的做法
A寫法
@ResponseStatus(HttpStatus.ACCEPTED) @PostMapping(value = "v1/activity/{activityid}/signup", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ActivitySignupRs signup( @PathVariable("activityid") String activityid, @Valid @RequestBody ActivitySignupRq activitySignupRq) { Long sequenceNumber = twitterSnowFlake.nextId(); return new ActivitySignupRs(sequenceNumber); }
B寫法
@ResponseStatus(HttpStatus.ACCEPTED) @PostMapping(value = "v1/activity/{activityid}/signup", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public CompletableFuture<ActivitySignupRs> signup( @PathVariable("activityid") String activityid, @Valid @RequestBody ActivitySignupRq activitySignupRq) { CompletableFuture<ActivitySignupRs> cf = CompletableFuture.supplyAsync(() -> { Long sequenceNumber = twitterSnowFlake.nextId(); return new ActivitySignupRs(sequenceNumber); }); return cf; }
測試下來 A寫法的吞吐量比較高也比較快,沒必要的 Future 會增加額外的 thread 切換
應該是要把 IO 耗時的部分(讀檔或呼叫API)包起來就好了
Future 用在同時呼叫多個耗時作業效果會很好
異步處裡也還是需要 thread 來作業,但是我們並沒有關心是誰的 thread 來作業,
其實是由 tomcat 的 threadpool 來支援,這樣對內對外都是共用一個pool,thread的總量是一樣的
你可以在 springboot @EnableAsync
並配置一個額外的 ThreadPool
@Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { ThreadPoolExecutor threadpool = new ThreadPoolExecutor( 64, 256, 5, TimeUnit.SECONDS, new ArrayBlockingQueue(2000), new ThreadPoolExecutor.CallerRunsPolicy()); return threadpool; }
然後在運行時指定 threadpool 來提供 thread 資源
@Async(value = "threadPoolTaskExecutor") public void signup(String activityid, ActivitySignupRq activitySignupRq, Long sequenceNumber) { //..... }
這樣你的 tomcat thread 就可以釋放回去
也可以用在 spring data 上
@Repository public interface UserRepAsync extends PagingAndSortingRepository<User, Long> { @Async("threadPoolTaskExecutor") CompletableFuture<List<User>> findByCardNo(String cardNo); }
需要回傳值的時候
@Async public CompletableFuture<User> findUser(String user) throws InterruptedException { String url = String.format("https://api.github.com/users/%s", user); User results = restTemplate.getForObject(url, User.class); // Artificial delay of 1s for demonstration purposes Thread.sleep(1000L); return CompletableFuture.completedFuture(results); }
多任務並行
// Kick of multiple, asynchronous lookups CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware"); CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry"); CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects"); // Wait until they are all done CompletableFuture.allOf(page1,page2,page3).join(); // Print results, including elapsed time logger.info("Elapsed time: " + (System.currentTimeMillis() - start)); logger.info("--> " + page1.get()); logger.info("--> " + page2.get()); logger.info("--> " + page3.get());
參考資料
【并发】基于 @Async和 CompletableFuture 实现并发异步操作 - ssslinppp - 博客园← springboot create ThreadPool