3 回答

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

TA貢獻(xiàn)1785條經(jīng)驗(yàn) 獲得超8個(gè)贊
您可以創(chuàng)建自定義線程執(zhí)行器
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);
}
這是創(chuàng)建自定義線程池執(zhí)行器的最佳實(shí)踐

TA貢獻(xiàn)1872條經(jīng)驗(yàn) 獲得超4個(gè)贊
調(diào)整連接池大小的正確方法通常是將其保留為默認(rèn)值。
如果您有 10,000 個(gè)前端用戶,那么擁有 10,000 個(gè)連接池將是瘋狂的。1000還是太恐怖了?即使是 100 個(gè)連接,也太過分了。您需要一個(gè)最多包含幾十個(gè)連接的小型池,并且希望其余應(yīng)用程序線程阻塞在池中等待連接。如果池經(jīng)過適當(dāng)調(diào)整,它將被設(shè)置為數(shù)據(jù)庫能夠同時(shí)處理的查詢數(shù)量的限制——如上所述,該限制很少超過(CPU 核心 * 2)。
如果您知道每個(gè)請求將消耗 10 個(gè)線程,那么您想打破這個(gè)建議并采用更多線程 - 將其保持在小于 100 的數(shù)字可能會提供足夠的容量。
我會像這樣實(shí)現(xiàn)控制器:
使用 s讓您的控制器/服務(wù)類中的查詢異步CompletableFuture
,并讓連接池?fù)?dān)心保持其線程繁忙。
所以控制器可能看起來像這樣(我從其他一些代碼中改編了它,這些代碼不像這個(gè)例子那樣工作,所以對這段代碼持懷疑態(tài)度):
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
使用的盡可能多的線程,它們將同時(shí)錘擊所有數(shù)據(jù)庫,并且連接池可以擔(dān)心保持連接處于活動(dòng)狀態(tài)。
但我認(rèn)為您在小負(fù)載下看到響應(yīng)時(shí)間井噴的原因是,根據(jù) JDBC 的設(shè)計(jì),它會在等待數(shù)據(jù)從數(shù)據(jù)庫返回時(shí)阻塞線程。
要阻止阻塞對響應(yīng)時(shí)間產(chǎn)生如此大的影響,您可以嘗試Spring Boot 反應(yīng)式。這使用異步 io 和背壓來匹配 IO 生產(chǎn)和消耗,基本上這意味著應(yīng)用程序線程盡可能繁忙。這應(yīng)該會阻止響應(yīng)時(shí)間以線性方式增加的負(fù)載下的行為。
請注意,如果您確實(shí)采用反應(yīng)式路徑,jdbc 驅(qū)動(dòng)程序仍然會阻塞,因此 spring 大力推動(dòng)創(chuàng)建反應(yīng)式數(shù)據(jù)庫驅(qū)動(dòng)程序。
添加回答
舉報(bào)