Overview
Node.js runs your code on a single thread, yet it can handle thousands of requests at once. This video untangles that apparent contradiction with a restaurant analogy: the restaurant is the server, the waiter is a thread, and each group of customers is a request.
One waiter can look after several tables because customers do not need the waiter the whole time.
Key Points
- A request splits into CPU work (time spent thinking) and I/O work (time spent waiting for files, external services or timers). Traditional servers such as Apache create a thread per request, which gets heavy at scale because of per-thread memory and context switches.
- With non-blocking I/O, the thread moves on to another request while one is waiting. The event loop hands off slow work and picks up whatever has finished, and an event demultiplexer built on epoll, kqueue or IOCP watches many I/O operations at once.
- libuv, a library written in C, does the actual I/O: the OS handles network I/O asynchronously, while file access and cryptography run in a thread pool of four threads by default.
- Node.js suits I/O-heavy work such as web servers, chat and APIs, not CPU-heavy tasks such as video conversion or machine learning. Heavy computation can be moved to worker threads.
- The video also clears up three common questions:
awaitis not blocking, the real cost of blocking is context switching, and multithreading means concurrency but does not guarantee parallelism.
Chapters
- 0:00 Introduction
- 0:55 Chapter 1: Servers and threads
- 1:27 Chapter 2: A server is a restaurant
- 2:39 Chapter 3: CPU work and I/O work
- 3:54 Chapter 4: Processes and threads
- 4:37 Chapter 5: The call stack
- 5:14 Chapter 6: One heavy task
- 6:14 Chapter 7: Adding more threads
- 7:04 Chapter 8: Non-blocking I/O
- 8:55 Chapter 9: The event loop
- 9:26 Chapter 10: The demultiplexer
- 10:35 Chapter 11: libuv
- 12:17 Chapter 12: Four kinds of I/O
- 13:23 Chapter 13: Checking it in code
- 14:43 Chapter 14: What it is bad at
- 15:59 Chapter 15: Common questions
- 18:09 Summary
概要
Node.jsは1本のスレッドでコードを動かしているのに、何千ものリクエストを同時にさばけます。この動画では、その一見矛盾した仕組みをレストランの例えで分解します。お店がサーバー、ウェイターがスレッド、お客さんの組がリクエストです。
ウェイター1人で複数のテーブルを見られるのは、お客さんがずっと店員を必要としているわけではないからです。
ポイント
- リクエストは、CPUの仕事(考えている時間)とI/Oの仕事(ファイルや外部サービス、時間の経過を待っている時間)に分けられます。Apacheのような伝統的なサーバーはリクエストごとにスレッドを作りますが、スレッドごとのメモリとコンテキストスイッチのため、アクセスが増えると重くなります。
- ノンブロッキングI/Oでは、あるリクエストが待っている間にスレッドが別のリクエストへ移ります。イベントループは時間のかかる処理を預けて、終わったものから拾い上げます。epoll、kqueue、IOCPを使うイベントデマルチプレクサが、たくさんのI/Oをまとめて見張ります。
- 実際にI/Oを実行しているのは、C言語で書かれたライブラリーのlibuvです。ネットワークのI/OはOSが非同期で面倒を見て、ファイルの読み書きや暗号の計算は初期設定で4本のスレッドプールで処理されます。
- Node.jsはWebサーバーやチャット、APIのようにI/Oの多い仕事に向いていて、動画の変換や機械学習のような計算の重い仕事には向きません。重い計算はワーカースレッドに逃がせます。
- よくある3つの疑問にも答えます。awaitはブロッキングではないこと、ブロッキングが遅いのは切り替えの費用のせいであること、マルチスレッドは並行であって並列を保証しないことです。
チャプター
- 0:00 イントロ
- 0:55 第1章 サーバーとスレッド
- 1:27 第2章 サーバーはレストラン
- 2:39 第3章 CPUの仕事とI/Oの仕事
- 3:54 第4章 プロセスとスレッド
- 4:37 第5章 コールスタック
- 5:14 第6章 1つの重い処理
- 6:14 第7章 スレッドを増やす
- 7:04 第8章 ノンブロッキングI/O
- 8:55 第9章 イベントループ
- 9:26 第10章 デマルチプレクサ
- 10:35 第11章 libuv
- 12:17 第12章 I/Oの4つの種類
- 13:23 第13章 コードで確かめる
- 14:43 第14章 苦手なこと
- 15:59 第15章 よくある疑問
- 18:09 まとめ