3 回答

TA貢獻1820條經驗 獲得超2個贊
您可能需要考慮更多參數:
1. 數據庫的最大并發請求參數。云提供商對不同層的并發請求有不同的限制,您可能需要檢查您的。
2.當你說50-200ms時,雖然很難說,但是平均有8個50ms的請求和2個200ms的請求還是都差不多?為什么?您的 doQuery 可能會受到查詢占用最大時間(即 200 毫秒)的限制,但花費 50 毫秒的線程將在其任務完成后被釋放,使其可用于下一組請求。
3. 您期望獲得的 QPS 是多少?
一些計算:如果單個請求需要 10 個線程,并且您配置了 100 個連接和 100 個并發查詢限制,假設每個查詢 200 毫秒,那么您一次只能處理 10 個請求。如果大多數查詢需要 50 毫秒左右的話,可能會比 10 好一點(但我并不樂觀)。
當然,如果您的任何查詢花費> 200毫秒(網絡延遲或其他任何東西),那么其中一些計算就會被折騰,在這種情況下,我建議您在連接端有一個斷路器(如果允許您中止)超時后的查詢)或在 API 端。
注意:最大連接限制與最大并發查詢限制不同。
建議:由于您需要 500 毫秒以下的響應,因此您還可以在池上設置約 100-150 毫秒的連接超時。最壞情況:150 毫秒連接超時 + 200 毫秒查詢執行 + 100 毫秒應用程序處理 < 500 毫秒響應。作品。

TA貢獻1785條經驗 獲得超8個贊
您可以創建自定義線程執行器
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
private CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
/**
* Returns a fixed thread pool where task threads take Diagnostic Context from the submitting thread.
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new CustomThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
}
在配置中,您可以如下配置ExecutorService bean
@Bean
public ExecutorService executeService() {
return CustomThreadPoolExecutor.newFixedThreadPool(10);
}
這是創建自定義線程池執行器的最佳實踐

TA貢獻1872條經驗 獲得超4個贊
調整連接池大小的正確方法通常是將其保留為默認值。
如果您有 10,000 個前端用戶,那么擁有 10,000 個連接池將是瘋狂的。1000還是太恐怖了?即使是 100 個連接,也太過分了。您需要一個最多包含幾十個連接的小型池,并且希望其余應用程序線程阻塞在池中等待連接。如果池經過適當調整,它將被設置為數據庫能夠同時處理的查詢數量的限制——如上所述,該限制很少超過(CPU 核心 * 2)。
如果您知道每個請求將消耗 10 個線程,那么您想打破這個建議并采用更多線程 - 將其保持在小于 100 的數字可能會提供足夠的容量。
我會像這樣實現控制器:
使用 s讓您的控制器/服務類中的查詢異步CompletableFuture
,并讓連接池擔心保持其線程繁忙。
所以控制器可能看起來像這樣(我從其他一些代碼中改編了它,這些代碼不像這個例子那樣工作,所以對這段代碼持懷疑態度):
public class AppController {?
? ? @Autowired private DatabaseService databaseService;?
? ? public ResponseEntity<Thing> getThing() {?
? ? ? ? CompletableFuture<Foo> foo = CompletableFuture.runAsync(databaseService.getFoo());
? ? ? ? CompletableFuture<Bar> bar = CompletableFuture.runAsync(databaseService.getBar());
? ? ? ? CompletableFuture<Baz> baz = CompletableFuture.runAsync(databaseService.getBaz());
? ? ? ? // muck around with the completable future to return your data in the right way
? ? ? ? // this will be in there somewhere, followed by a .thenApply and .join
? ? ? ? CompletableFuture<Void> allFutures = CompletableFuture.allOf(foo, bar, baz);
? ? ? ? return new ResponseEntity<Thing>(mashUpDbData(cf.get()));
? ? }? ??
}
控制器將生成您允許ForkJoinPool
使用的盡可能多的線程,它們將同時錘擊所有數據庫,并且連接池可以擔心保持連接處于活動狀態。
但我認為您在小負載下看到響應時間井噴的原因是,根據 JDBC 的設計,它會在等待數據從數據庫返回時阻塞線程。
要阻止阻塞對響應時間產生如此大的影響,您可以嘗試Spring Boot 反應式。這使用異步 io 和背壓來匹配 IO 生產和消耗,基本上這意味著應用程序線程盡可能繁忙。這應該會阻止響應時間以線性方式增加的負載下的行為。
請注意,如果您確實采用反應式路徑,jdbc 驅動程序仍然會阻塞,因此 spring 大力推動創建反應式數據庫驅動程序。
添加回答
舉報