接続プールとレイテンシ
接続プールとレイテンシ
1)なぜプールが必要なのか
接続は高価です(TCP/TLSハンドシェイク、認証、ウォームアップ)。プールは以下を可能にします:- 既製の接続を再利用(keep-alive)→TTFB以下。
- 同時性を制御し、リトリートの雪崩の代わりにバックプレッシャーを与えます。
- 正しいサイズとタイムアウトによるp95/p99テールの削減。
主なリスク:プール内の待ち行列、ヘッド・オブ・ライン・ブロッキング、接続のためのコンテンツ、退却の嵐。
2)数学の基盤: プールのサイズを数える方法
Little's law: 'L=λ × W'を用いる。プールの場合、これは次のことを意味します:- 'λ'は平均リクエストストリーム(RPS)です。
- 'W'はリクエストごとの平均接続ビジー(ネットワーク遅延やリモートサービス運用を含むサービス時間)です。
- 最小プールサイズは'N_min ≈ λ × W'です。
- バリエーションのマージンを追加し、p99:ヘッドルーム20-50%。
- 例:300 RPS、平均ホールドタイム40 ms→'N_min=300 × 0。04 = 12`.50%のマージンで、18の接続が→です。
尾が大きい場合:クリティカルパスの場合は'W_p95'または'W_p99'を考慮してください-プールが成長します。
3)一般的な設計原則
1.短いデータパス:再利用(keep-alive、 HTTP/2/3 multiplexing)。
2.並列性の制限:バックエンドを揚げるよりも、すばやく(429/503)拒否することをお勧めします。
3.タイムアウト>リトリート:小さなタイムアウトとまれなジッタのリトリートを設定します。
4.クライアントキューはサーバキューより短い(高速フェイルファスト)。
5.Backpressure:プールがいっぱいのとき-すぐにNACK/error/collbeck 「later」。
6.ターゲットによるプールの分離:DB、キャッシュ、外部PSP-その限界。
4) HTTP/1。1対HTTP/2/3、 keep-alive
。 。1:一度に1つの接続要求(実質的に);ホストごとに複数の接続を持つプールが必要です。
HTTP/2: 1つのTCPでのストリーム多重化;接続が少なくなりますが、パケットが失われるとTCPでのHOLブロックが可能になります。
HTTP/3 (QUIC): UDPに対するストリーミングの独立性-HOLの問題が少なく、最初のバイト数が速くなります。
- keep-aliveタイムアウト30-90(プロフィール別)、接続要求の制限(優雅なリサイクル)。
- 作業者の開始時に予熱(事前接続)する。
- 1 HTTP/2あたりの最大フローを制限します(例:100-200).
nginx upstream backend {
server app-1:8080;
server app-2:8080;
keepalive 512;
keepalive_requests 1000;
keepalive_timeout 60s;
}
proxy_http_version 1. 1;
proxy_set_header Connection "";
使節(HTTP/2プール):
yaml http2_protocol_options:
max_concurrent_streams: 200 common_http_protocol_options:
idle_timeout: 60s max_connection_duration: 3600s
5) DBプール: PgBouncer、 HikariCPの運転者
目標は、競争力のある取引を制限し、短い接続を保持することです。
5.1 PgBouncer (PostgreSQL)
モード:'session'/'transaction'/'statement'。APIの場合-より頻繁にトランザクション。
重要なパラメータは'pool_size'、 'min_pool_size'、 'reserve_pool_size'、 'server_idle_timeout'、 'query_wait_timeout'です。
ini
[databases]
appdb = host=pg-primary port=5432 dbname=appdb
[pgbouncer]
pool_mode = transaction max_client_conn = 5000 default_pool_size = 100 min_pool_size = 20 reserve_pool_size = 20 query_wait_timeout = 500ms server_idle_timeout = 60 server_reset_query = DISCARD ALL
5.2 HikariCP (Java)
小さくて速い接続、ハードタイムアウト。
properties dataSourceClassName=org. postgresql. ds. PGSimpleDataSource maximumPoolSize=30 minimumIdle=5 connectionTimeout=250 validationTimeout=200 idleTimeout=30000 maxLifetime=1800000 leakDetectionThreshold=5000
ルール:
- 'maximumPoolSize ≈ RPS × W × headroom'。
- 'connectionTimeout'は秒単位ではなくミリ秒単位です。
- リーク検出を有効にします。
5.3 Go/Node/Python-例
HTTPに移動します。クライアント(再利用+タイムアウト):go tr:= &http. Transport{
MaxIdleConns: 512,
MaxIdleConnsPerHost: 128,
IdleConnTimeout: 60 time. Second,
TLSHandshakeTimeout: 2 time. Second,
}
c:= &http. Client{
Transport: tr,
Timeout: 2 time. Second ,//general
}
ノード。js keep-aliveエージェント:
js const http = require('http');
const agent = new http. Agent({ keepAlive: true, maxSockets: 200, maxFreeSockets: 64, timeout: 60000 });
psycopg/SQLAlchemy (Python):
python engine = create_engine(
url, pool_size=30, max_overflow=10, pool_recycle=1800, pool_pre_ping=True, pool_timeout=0. 25
)
6)待ち行列とテールレイテンシー
尾は次のときに発生します:- プールは'λ × W'より小さい→接続キューが成長している。
- バッファとリミットなしでムラ(バースト)をロードします。
- 長いリクエストは接続を取り、HOLを作成します。
- リクエストタイプ(高速/遅い)でプールを分離します。
- クライアント側のタイムアウトを実装します。期限切れの場合-高速NACK。
- ルート(Envoy、 HAProxy)の外れの検出および回路破壊。
- 「重い」ルートのクォータ、レポート/エクスポート用の個別のプール。
yaml circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 200 max_pending_requests: 100 max_requests: 1000 max_retries: 2
7)タイムアウトとリトリート(正しい順序)
1.タイムアウトを接続(DC内部で50〜250ミリ秒)
2.TLSハンドシェイクタイムアウト(500-1000ミリ秒DC)。
3.要求/読み取りタイムアウト(ルートSLOに近い)。
4.再試行:最大1時間、idempotentメソッドのみ;ジッタ+バックオフ。
5.リトレイ予算:RPSの割合としてのグローバル制限(例:≤ 10%)。
8)キープアライブ、ナーグル、プロトコル
小さなメッセージRPCのNagle (TCP_NODELAY)を無効にします。
可能な限りHTTP keep-aliveを有効にします。
TIME_WAITを見る-結果を理解している場合にのみ'再利用'/'リサイクル'を調整します。より良い-カーネルチューニングではなく、接続を再利用します。
TLS-セッション再開とALPNを使用します。
9) OS/カーネルチューニング(注意)
'ネット。コアです。somaxconn'、'ネット。ipv4。ip_local_port_range'、ネット。ipv4。 。 。
記述子:'nofile' ≥プロキシプロセスあたり64kです。
IRQバランス、GRO/LRO-トラフィックプロファイル。
優先順位-プロファイル;メトリックなしでチューニングすることはしばしば有害です。
10) Observability: 何を測定するか
プール使用率:ビジー/トータル、p50/p95接続保留中。
機内リクエストとそのホールドタイム(ルートスライス)。
リトレイエラー予算:リピートの割合。
接続チャーン毎秒作成/閉じる。
TCP/TLS: SYN RTT、握手、セッションの再利用。
Rock:アクティブな接続、待機、長いトランザクション、ロック。
Графики: 「RPS vs pool wait」、 「hold-time distribution」、 「reuse ratio」、 「circuit trips」。
11)ケースレシピ
11.1 APIゲートウェイ→バックエンド
'max_concurrent_streams=200'にHTTP/2します。
ゲートウェイノードあたりのサービスあたりの接続数は20〜40です。
タイムアウト:100ms、試行あたり300-500ms、共有1-2s、ジッタで1再試行。
11.2 PostgreSQL→PgBouncerによるサービス
'pool_mode=transaction'、 'default_pool_size' (RPS × W × 1。3).
'connectionTimeout ≤ 250ms'では、短いトランザクション(<100ms)。
重いレポート要求-個別のプール/レプリカ。
11.3 gRPC内部
スレッド制限が100〜200のターゲットホストごとに1チャネル(HTTP/2)。
SLOルートのRPCの締め切りは、idempotentのみを再試行します。
長いRPCトレースのサンプリングとホールドタイムのメトリック。
12)実装チェックリスト(0-30日)
0-7日
キールート/クライアントの「W」(ホールドタイム)を測定します。
'N_min=λ × W'を計算し、30〜50%のヘッドルームを追加します。
keep-aliveと短い接続タイムアウトを有効にします。
8-20日
個別のプール(高速/遅い/外部)。
タイプサーキットブレーカとリトレイ予算。
ダッシュボードを追加:プール待機P95、再利用比率、機内。
21-30日
負荷はバースト、カオステスト「バックエンドの落下」で実行されます。
尾の最適化:重いルートの分離、ローカルキャッシュ。
runbook 'axの文書式と制限。
13)アンチパターン
プールサイズはランダムで、ヘッドルームはありません。
高速障害の代わりに、大規模な接続待ちタイムアウト→ロングテール。
多くの人はジッタとidempotency→嵐なしで後退します。
すべてのリクエストタイプに1つの共有プール。
長いトランザクションは、接続(DB)→残りの飢餓を維持します。
無効なキープアライブまたは小さすぎるアイドル→チャーン制限とTTFBの成長。
14)成熟度の指標
プールはprodでp95を待ちます<合計p95ルートの10%。
再利用率(内部HTTPでは>90%;>外的な)のための80%。
DB txn時間p95 <100-200ミリ秒;長いトランザクションの割合<1%。
再試行レート<5%(および≤予算)、タイムアウトによるエラーは安定して予測可能です。
すべての重要な顧客のための文書化されたプール決済。
15)結論
効果的な接続プールはキューエンジニアリング+タイムアウト規律です。'W'を測定し、プール'λ × W'をマージンで計算し、keep-alive/HTTP2+をオンにし、スローパスを分離し、短いタイムアウトを保ち、ジッタで最小限のレトラを行います。「プール待ちvsレイテンシー」の観測性とサーキットブレーカを追加すると、バックエンドを過熱することなく、低いTTFB、制御されたp99テール、サージ抵抗を得ることができます。