C 10 K問題による技術変革


C 10 K問題


サーバ応用分野では古くから有名な問題ですが、1台のサーバで同時に10 K級の接続をサポートする必要があります.これらの接続は生存状態を維持している可能性があります.
この問題を解決するには、主な考え方は2つあります.1つは、接続処理ごとに独立したプロセス/スレッドを割り当てることです.もう1つの考え方は、同じプロセス/スレッドで複数の接続を同時に処理することです.

プロセス/スレッドごとに接続を処理


この考え方が最も直接的だ.しかし、申請プロセス/スレッドはかなりのシステムリソースを占有するとともに、マルチプロセス/スレッドの管理はシステムに圧力を与えるため、このスキームは良好な拡張性を備えていない.
そのため、この考え方は、サーバリソースが十分に豊かではない場合、実行できません.資源が十分に豊かであっても、効率は高くない.
問題:リソースの占有量が多すぎて、拡張性が悪い.

プロセス/スレッドごとに複数の接続を同時に処理


伝統的な考え方.


最も簡単な方法は、各接続を1つずつ処理することであり、各接続は1つのsocketに対応し、すべてのsocketにデータがある場合、この方法は実行可能である.
しかし、アプリケーションがsocketのファイルデータを読み込んでreadyしないと、アプリケーション全体がブロックされてファイルハンドルが待機し、他のファイルハンドルreadyでも下に処理できません.
構想:複数の接続を直接ループ処理する.
問題:いずれかのファイルハンドルが失敗すると、アプリケーション全体がブロックされます.

select


上のブロックの問題を解決するには、考え方が簡単です.もし私がファイルのハンドルを読む前に、まずその状態を調べて、readyになったら処理し、readyにならなければ処理しないなら、この問題を解決したのではないでしょうか.
そこでselect案がありました.fd_を1つ使うset構造体は、カーネルに複数のファイルハンドルを同時に監視するように伝え、ファイルハンドルの状態が指定された変化(例えば、ハンドルが使用不可から使用可能に変更された)またはタイムアウトした場合に呼び出されます.その後、アプリケーションはFD_を使用できます.ISSETは,どのファイルハンドルの状態が変化したかを逐次調べる.
これにより、小規模な接続の問題は大きくありませんが、接続数が多い(ファイルハンドルの数が多い)場合は、個々にステータスをチェックするのが遅くなります.従って、selectには管理ハンドル上限(FD_SETSIZE)が存在することが多い.また、使用上、注目イベントと発生イベントを記録するフィールドは1つしかないため、呼び出すたびにfd_を再初期化します.set構造体.
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

構想:接続要求が到着したら再検査処理する.
問題:ハンドル上限+繰り返し初期化+すべてのファイルハンドルのステータスを1つずつ調べるのは効率的ではありません.

poll


pollは主にselectの最初の2つの問題を解決します:1つのpollfd配列を通じてカーネルに注目するイベントを伝達してファイルハンドルの上限を除去し、同時に異なるフィールドを使用してそれぞれ注目イベントと発生イベントをマークして、繰り返し初期化を回避します.
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

構想:新しいデータ構造を設計して使用効率を提供する.
質問:すべてのファイルハンドルのステータスを1つずつ調べるのは効率的ではありません.

epoll


すべてのファイルハンドルのステータスを1つずつチェックする効率が悪い以上、呼び出しが返されたときにステータスが変化した(データreadyの可能性が高い)ファイルハンドルだけをアプリケーションに提供すれば、チェックを行う効率はずっと高いのではないでしょうか.
epollはこの設計を採用し,大規模な応用シーンに適している.
実験により、ファイルハンドルの数が10を超えると、epoll性能はselectとpollより優れていることが明らかになった.ファイルハンドルの数が10 Kに達すると、epollはselectとpollの2桁を超えた.
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

考え方:ステータスが変化するファイルハンドルのみを返します.
質問:特定のプラットフォーム(Linux)に依存します.

libevent


プラットフォームにまたがって、下位プラットフォームの呼び出しをカプセル化し、統一的なAPIを提供するが、下位プラットフォームは異なるプラットフォーム上で自動的に適切な呼び出しを選択する.

C 10 KからC 10 M


技術の進化に伴い,epollはC 10 K問題をよりよく処理できるようになったが,10 M規模の同時接続をサポートするなど,さらなる拡張を行うには,従来の技術ではどうしようもない.
では、新しいボトルネックはどこにあるのでしょうか.
前の進化の過程から、根本的な考え方は効率的にブロックし、CPUがコアの任務を遂行できるようにすることであることがわかります.
接続が多い場合は、まず大量のプロセス/スレッドが必要です.同時に、システム内のアプリケーションプロセス/スレッドは大量にready状態にある可能性があります.システムが絶えず迅速に切り替える必要がありますが、システムコンテキストの切り替えには代価があることを知っています.現在Linuxシステムのスケジューリングアルゴリズムは効率的に設計されているが,10 Mのような大規模なシーンにはまだ力が足りない.
だから私たちが直面しているボトルネックは2つあります.1つはプロセス/スレッドが処理ユニットとして厚すぎるかということです.もう1つは、システムスケジューリングのコストが高すぎることです.
自然に、処理ユニットとしてより軽量レベルのプロセス/スレッドがあり、それらのスケジューリングが迅速に(ロックを必要としないほうがいい)できれば、完璧だと思います.
このような技術は、coroutine(コラボレーション)、またはコラボレーションルーチンであるいくつかの言語で実装されています.具体的にはPython,Lua言語におけるcoroutine(コモン)モデル,Go言語におけるgoroutine(Go程)モデルは,いずれも類似の概念である.実際には,複数の言語(さらにはC言語)で類似のモデルを実現することができる.
実装では、複数のタスクを少量のスレッドのセットで実装しようとしています.タスクがブロックされると、同じスレッドで他のタスクを実行し続け、多くのコンテキストの切り替えを避けることができます.各コヒーレントが独占するシステムリソースは、スタック部分のみであることが多い.また,各コヒーレンス間の切り替えは,ユーザがコードによって明示的に指定することが多く(各種callbackと同様),カーネル参加を必要とせず,非同期を容易に実現できる.

参考文献


http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/
転載は以下のことを明記してください.http://blog.csdn.net/yeasy/article/details/43152115