亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Java中的連接池和線程池設置

Java中的連接池和線程池設置

慕虎7371278 2023-10-13 16:35:44
使用 Hikari 池的 Spring 應用程序。現在,對于來自客戶端的單個請求,我必須查詢 10 個表(業務需要),然后將結果組合在一起。而查詢每個表可能要花費50ms到200ms。為了加快響應時間,我FixedThreadPool在服務中創建一個來查詢不同線程中的每個表(偽代碼):class MyService{    final int THREAD_POOL_SIZE = 20;    final int CONNECTION_POOL_SIZE = 10;    final ExecutorService pool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);    protected DataSource ds;    MyClass(){        Class.forName(getJdbcDriverName());        HikariConfig config = new HikariConfig();        config.setMaximumPoolSize(CONNECTION_POOL_SIZE);        ds = new HikariDataSource(config);    }    public Items doQuery(){        String[] tables=["a","b"......]; //10+ tables        Items result=new Items();        CompletionService<Items> executorService = new ExecutorCompletionService<Items>(pool);        for (String tb : tables) {            Callable<Item> c = () -> {                Items items = ds.getConnection().query(tb); ......                return Items;            };            executorService.submit(c);        }        for (String tb: tables) {            final Future<Items> future = executorService.take();            Items items = future.get();            result.addAll(items);        }    }}現在對于單個請求,平均響應時間可能是 500ms。但對于并發請求,平均響應時間會迅速增加,請求越多,響應時間就越長。我想知道如何設置正確的連接池大小和線程池大小以使應用程序有效工作?順便說一句,數據庫在云中使用 RDS,具有 4 個 cpu 16GB 內存,最大連接數為 2000,最大 IOPS 為 8000。
查看完整描述

3 回答

?
DIEA

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 毫秒響應。作品。


查看完整回答
反對 回復 2023-10-13
?
慕的地10843

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);

    }

這是創建自定義線程池執行器的最佳實踐


查看完整回答
反對 回復 2023-10-13
?
守著一只汪

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 大力推動創建反應式數據庫驅動程序。


查看完整回答
反對 回復 2023-10-13
  • 3 回答
  • 0 關注
  • 166 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號