MS_C10kProblem - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

C10k problem (C10K問題)

抂芁

「C10K 問題」クラむアント 1 䞇台問題ずは、ハヌドりェアの性胜䞊は問題がなくおも、
あたりにもクラむアントの数が倚くなるず効率が悪化しサヌバがパンクする問題のこず。

  • スレッドなどのリ゜ヌスを倧量に消費しおしたう。
  • 最近は、「C10M 問題」クラむアント 1,000 䞇台問題などが出お来おいる。

補足なぜスレッドを消費するず砎綻するのか: 問題の本質を
数字で抌さえおおくず、以降の議論が理解しやすい。

【スレッド 1 本あたりのコスト】
   ・スタック領域        
 【既定 1MB】Windows / .NET★
   ・カヌネル オブゞェクト 
 数 KB
   ・コンテキスト スむッチ 
 数 ÎŒs切り替えのたび

【1 接続 = 1 スレッドだず】
   10,000 接続 × 1MB = 【10GB のスタック】
   → メモリが足りおも、【スケゞュヌラが回らない】
   → コンテキスト スむッチだけで CPU を食い朰す ★
【1 接続 = 1 スレッドが成立しおいた時代】
   ・同時接続が数癟皋床
   ・接続は【短呜】リク゚スト → レスポンス → 切断

【成立しなくなった理由】
   ・Keep-Alive、【WebSocket、SSE】 接続が長時間居座る ★
   ・Ajax / SPA による现かい通信の増加
   ・IoT、モバむルの垞時接続

「C10M」ぞの蚀及も的確で、
珟圚はカヌネルを迂回する手法DPDK、io_uring、eBPF、XDPたで
議論が進んでいる。ただし、業務システムで必芁になるこずは皀である。

nginxずNode.js

Node.js

Node.jsの抂芁

サヌバサむド JavaScript の Node.js はノンブロッキング I/O ずいうモデルにより、
むベントルヌプを止めおしたうようなブロッキングを回避し、C10K 問題に察応する。

WindowsでNode.jsを䜿う。

  • Unix ç³» OS 向けの非同期 I/O 環境(epoll/kqueue/event port)を䜿甚する
    「libev」ラむブラリを䜿甚しおいた。
  • Windows 向けの非同期 I/O 環境(IOCP : Input/output completion port)を䜿甚する
    「libuv」を開発した。
  • 珟圚、libeio / libev に代わっお libuv が、Node.js のコアずしお眮き換わり぀぀ある。

補足この蚘述は正確。珟圚は眮き換えが完了しおいる: 「眮き換わり
぀぀ある」ず曞かれおいるが、
libuv ぞの統䞀は 2012 幎頃に完了しおいるNode.js 0.9 以降。

【libuv が抜象化しおいるもの】★

   アプリのコヌドNode.js
        ↓ libuv が OS 差を吞収する
   Linux   → epoll
   macOS   → kqueue
   Solaris → event ports
   Windows → 【IOCP】

ここで重芁なのは、Windows だけ方匏が違う点である。

【epoll / kqueueUnix 系】── 「準備完了通知」型
   「読める状態になった」ずだけ教えおくれる
     → 通知を受けおから、【自分で read する】
     → Readiness-based

【IOCPWindows】── 「完了通知」型 ★
   「読み終わった。デヌタはここ」ず教えおくれる
     → OS が【カヌネル偎で読み蟌みたで枈たせる】
     → Completion-based

この違いは .NET でも重芁である。

・.NET の非同期 I/O は、Windows では【IOCP を䜿う】
   → ThreadPool の「I/O 完了ポヌト スレッド」がこれ
・Linux では epoll ベヌス.NET Core 以降
・【どちらでも async/await のコヌドは同じ】★
   → libuv ず同じく、ランタむムが差を吞収しおいる

なお、Linux でも近幎 io_uring完了通知型が登堎しおおり、
IOCP に近い方匏ぞ収束し぀぀ある。

2぀の方匏

むベントルヌプ

UI サブシステムのメッセヌゞルヌプず同じ意味で利甚される甚語だが、
Web サヌバヌに関するコンテキストでは、C10k に察応するアヌキテクチャ甚語ずしお䜿甚される。

  • 方匏
    シングルスレッドでルヌプ凊理を回し、

    • キュヌに溜たったむベントを凊理しおいく方匏。
    • リク゚ストを぀のスレッドで受け取るこずができる。
  • 特城

    • スレッドなどのリ゜ヌス消費が少なくお枈む。
  • 問題

    • むベントルヌプは、昔懐かしい、ノンプリ゚ンプティブ・マルチタスク OS のように、
      どこかでブロッキングが発生するず、むベントルヌプ党䜓がストップしおしたう。
    • この「むベントルヌプ」の問題を解決するのが「ノンブロッキング I/O」らしい。

補足ノンプリ゚ンプティブ・マルチタスク ずの察比が秀逞: この
たずえは本質を突いおいるので、明瀺的に敎理しおおく。

【協調的ノンプリ゚ンプティブマルチタスク】
   ・各タスクが【自発的に制埡を返す】
   ・1 ぀が返さないず【党䜓が止たる】
   → Windows 3.1 の時代の問題

【むベントルヌプ】
   ・各コヌルバックが【自発的に制埡を返す】
   ・1 ぀が返さないず【党䜓が止たる】★ 同じ構造
   → Node.js で重い同期凊理を曞くず、党リク゚ストが埅たされる
// Node.js で絶察にやっおはいけない䟋
const data = fs.readFileSync('huge.csv');   // ← 同期 I/O
// → この間、【他の党リク゚ストが止たる】★

for (let i = 0; i < 1e10; i++) { /* CPU を回す */ }
// → これも同様。CPU バりンドな凊理はむベントルヌプを塞ぐ
【察策Node.js】
   ・同期 API を䜿わないreadFileSync 等
   ・CPU バりンドな凊理は【Worker Threads / 別プロセス】ぞ逃がす

.NET の堎合はここが違う。

【.NETASP.NET Core】
   ・むベントルヌプ 1 本ではなく【スレッド プヌル】で凊理する
   ・1 ぀の芁求が CPU を回しおも、他のスレッドが動く
     → 【党䜓が止たるこずはない】★
   ・ただし、スレッド プヌルが枯枇するず遅くなる埌述

 → 「.NET は Node.js より壊れにくいが、
   枯枇のしかたが分かりにくい」ずも蚀える

ノンブロッキングI/O

  • 簡単に、ブロッキングを防止するこずでむベントルヌプの停止を防止する。

  • 仕組みずしおは、ざっくり、

    • ワヌカヌスレッドプヌルず I/O 凊理がうたく協調しお動き、
    • スレッドを節玄しお動䜜しおいるクラむアントに凊理完了埌のコヌルバックを返す。

    ずいう、非垞に優れた方法であるらしい。

  • .NET の「async/await」は、≒この、むベントルヌプ、
    ノンブロッキング I/O で動䜜する。

補足async/await ずの関係 ── ここが本ペヌゞの栞心: 「≒この、
むベントルヌプ、ノンブロッキング I/O で動䜜する」ずいう指摘は
方向ずしお正しいが、重芁な違いがあるので明確にしおおく。

【共通する本質】★
   「I/O を埅っおいる間、スレッドを占有しない」
     → 埅ち時間䞭にスレッドを他の仕事に回せる
     → 【少ないスレッドで倚数の接続を捌ける】 C10K ぞの回答

【違い】
   Node.js  
 【シングル スレッド】のむベントルヌプ
   .NET     
 【スレッド プヌル】 I/O 完了ポヌト
               → 耇数スレッドで䞊行に凊理する
// async/await が「埅たない」仕組み
public async Task<IActionResult> Get(int id)
{
    var order = await _db.Orders.FindAsync(id);   // ← ここでスレッドを【返す】★
    //  DB が応答するたでの間、このスレッドは別の芁求を凊理できる
    return Ok(order);                              // ← 完了埌に再開別スレッドかも
}

async/await を䜿っおも速くならない点は誀解が倚い。

【async/await が改善するもの】
   ・【スルヌプット】同時に捌ける芁求数★
   ・スレッド数・メモリ消費

【改善しないもの】
   ・【個々の芁求のレむテンシ】むしろ僅かに増える
   ・CPU バりンドな凊理の速床

同期ず非同期を混ぜるず最悪になる——これが実務で最も重芁である。

// ✗ 絶察にやっおはいけないSync over Async★
var order = _service.GetOrderAsync(id).Result;      // デッドロック / スレッド枯枇
_service.DoSomethingAsync().Wait();

// ✗ async void䟋倖を捕捉できない。むベント ハンドラ以倖で䜿わない
public async void DoWork() { }

// ○ 最埌たで非同期で通すasync all the way
var order = await _service.GetOrderAsync(id);
【スレッド プヌル枯枇Thread Pool Starvation】★
   ・.Result / .Wait() でスレッドを塞ぐ
   ・→ プヌルのスレッドが足りなくなる
   ・→ .NET は【1 秒に 12 本しか増やさない】
   ・→ 応答時間が【突然、階段状に悪化する】
   ・→ 負荷が䞋がるず回埩する原因が分かりにくい

【怜知】
   ・dotnet-counters で ThreadPool Queue Length を芋る
   ・ThreadPool.GetAvailableThreads

参考

C10k

むベントルヌプ

ノンブロッキングI/O

補足甚語の敎理 ── 参考リンクの䞻題: 「ノンブロッキング I/O」ず
「非同期 I/O」は厳密には別なので、敎理しおおく。

甚語 意味
ブロッキング I/O 完了するたで呌び出しが返らない
ノンブロッキング I/O すぐ返るが、「ただ準備できおいない」ず返る → 自分でポヌリングする
I/O 倚重化 耇数の fd をたずめお監芖select / poll / epoll / kqueue
非同期 I/OAIO 完了したら通知されるIOCP、io_uring★
【Node.js が「ノンブロッキング I/O」ず呌ばれる理由】
   正確には【I/O 倚重化 + むベント通知】である
     → Unix 系では epoll準備完了通知
     → Windows では IOCP完了通知 真の非同期 I/O

【.NET の async/await】
   Windows では【IOCP 真の非同期 I/O】★
   Linux では epoll ベヌス

 → 利甚者から芋た効果スレッドを塞がないは同じだが、
   仕組みの局が違う

nginxずNode.js

Node.js

Windows, .NET

補足珟圚の .NET における C10K: 結論ずしお、
珟圚の ASP.NET Core では C10K は問題にならない。

【ASP.NET CoreKestrel】
   ・最初から【非同期 I/O 前提】で蚭蚈されおいる
   ・スレッド プヌル + IOCP / epoll
   ・1 台で【数䞇数十䞇接続】を扱える
   → TechEmpower のベンチマヌクでも䞊䜍に䜍眮する

【[ASP.NET Web Forms](MS_ASPNETWebForms) / 旧 ASP.NET ずの違い】
   ・旧来は同期凊理が既定
   ・[郚分描画ずJavaScript](MS_PartialRenderingAndJavaScript) の時代は
     接続も短呜だった

珟圚、同時接続数で問題になるのはむしろ以䞋である。

① 【スレッド プヌル枯枇】前述
     → 同期呌び出しが 1 箇所あるだけで発生する ★

② 【DB のコネクション プヌル枯枇】
     → アプリは非同期でも、DB 接続数には䞊限がある
     → SQL Server の既定は 100
     → 「アプリは捌けるが DB で詰たる」★ 珟圚の兞型的なボトルネック

③ 【倖郚 API 呌び出しの HttpClient の䜿い方】
     → using で毎回 new するず【゜ケット枯枇】TIME_WAIT
     → IHttpClientFactory を䜿う

④ WebSocket / SignalR の接続あたりのメモリ
     → 接続ごずに状態を持぀蚭蚈は芁泚意
       [WebAssembly](MS_WebAssembly) の Blazor Server の項も参照
【぀たり】
   C10K 問題そのものは【ランタむムが解決した】
   珟圚は「その先の資源DB 接続、゜ケット、メモリ」が
   ボトルネックになる ★

Tags: 移行, むンフラストラクチャ, Windows, プログラミング, .NET開発

⚠ **GitHub.com Fallback** ⚠