この記事では、node.js における高同時実行性と分散クラスターについて簡単に説明します。必要な方は参考にしていただければ幸いです。
なぜノードが高い同時実行性を達成できるのかを説明する前に、ノードの他のいくつかの機能を理解しておくとよいでしょう:
まず概念を明確にしましょう。つまり、ノードは シングルスレッド
、これはブラウザのJavaScriptの特性と同じであり、ノードでは、JavaScriptのメインスレッドは他のスレッド(I/Oスレッドなど)と状態を共有できません。 单线程
的,这一点与JavaScript在浏览器中的特性相同,并且在node中JavaScript主线程与其他线程(例如I/O线程)是无法共享状态的。
单线程的好处就是:
无需像多线程那样去关注线程之间的状态同步问题
没有线程切换所带来的开销
没有死锁存在
当然单线程也有许多坏处:
无法充分利用多核CPU
大量计算占用CPU会导致应用阻塞(即不适用CPU密集型)
错误会引起整个应用的退出
不过在今天看来,这些坏处都已经不再是问题或者得到了适当的解决:
(1) 创建进程 or 细分实例
关于第一个问题,最直白解决方案就是使用child_process核心模块或者cluster:child_process 和 net 组合应用。我们可以通过在一台多核服务器上创建多个进程(通常使用fork操作)来充分利用每个核心,不过要处理好进程间通信问题。
另一个方案是,我们可以将物理机器划分为多台单核的虚拟机,并通过pm2等工具,管理多台虚拟机形成一个集群架构,高效运行所需服务,至于每台机器间的通信(状态同步)我这里先按下不表,在下文的Node分布式架构中再做详细说明。
(2) 时间片轮转
关于第二点,我跟小伙伴讨论过后认为可以通过时间片轮转方式,在单线程上模拟多线程,适当减少应用阻塞的感觉(虽然这种方法不会真的像多线程那样节约时间)
(3) 负载均衡、坏点监控/隔离
至于第三点,我跟小伙伴们也讨论过,认为主要的痛点就在于node不同于JAVA,它所实现的逻辑是以异步为主的。
这就导致了node无法像JAVA一样方便地使用 try/catch 来来捕获并绕过错误,因为无法确定异步任务会何时传回异常。而在单线程环境下,绕不过错误就意味着导致应用退出,重启恢复的间隙会导致服务中断,这是我们不愿意看到的。
当然,在服务器资源丰富的当下,我们可以通过 pm2 或 nginx 这些工具,动态的判断服务状态。在服务出错时隔离坏点服务器,将请求转发到正常服务器上,并重启坏点服务器以继续提供服务。这也是Node分布式架构的一部分。
你可能会问,既然node是单线程的,事件全部在一个线程上处理,那不是应该效率很低、与高并发相悖吗?
恰恰相反,node的性能很高。原因之一就是node具有异步I/O
特性,每当有I/O请求发生时,node会提供给该请求一个I/O线程。然后node就不管这个I/O的操作过程了,而是继续执行主线程上的事件,只需要在该请求返回回调时在处理即可。也就是node省去了许多等待请求的时间。
这也是node支持高并发的重要原因之一
实际上不光是I/O操作,node的绝大多数操作都是以这种异步的方式进行的。它就像是一个组织者,无需事必躬亲,只需要告诉成员们如何正确的进行操作并接受反馈、处理关键步骤,就能使得整个团队高效运行。
你可能又要问了,node怎么知道请求返回了回调,又应该何时去处理这些回调呢?
答案就是node的另一特性:事务驱动
マルチスレッドのようなスレッド間の状態同期の問題に注意を払う必要がありません
スレッドの切り替えによるオーバーヘッドがありません
デッドロック非同期 I/O
機能があるため、I/O リクエストが発生すると、そのリクエストに対して I/O スレッドが提供されます。その後、ノードは I/O 操作プロセスを気にせず、メインスレッドでイベントを実行し続けますが、リクエストがコールバックを返したときにのみ処理する必要があります。つまり、ノードはリクエストを待つ時間を大幅に節約します。 🎜🎜🎜これは、ノードが高い同時実行性をサポートする重要な理由の 1 つでもあります🎜🎜🎜実際、I/O 操作だけでなく、ノードのほとんどの操作はこの非同期方法で実行されます。オーガナイザーは、個人的にすべてを行う必要はなく、チーム全体が効率的に運営できるように、メンバーに正しい操作方法、フィードバックの受け入れ方法、主要な手順の処理方法を伝えるだけで済みます。 🎜🎜トランザクション駆動🎜🎜もう一度尋ねたいかもしれませんが、ノードはリクエストがコールバックを返したことをどのようにして知るのでしょうか?また、これらのコールバックをいつ処理する必要があるのでしょうか? 🎜🎜その答えは、ノードのもう 1 つの機能である トランザクション ドライバー
です。つまり、メイン スレッドがイベント ループ トリガーを通じてプログラムを実行します 🎜🎜🎜これが、ノードが高い同時実行性をサポートするもう 1 つの重要な理由です 🎜🎜 🎜ノード環境のイベント ループの図: 🎜┌───────────────────────┐ ┌─>│ timers │🎜🎜ポーリング フェーズ: 🎜🎜🎜ポーリング フェーズに入り、タイマーが呼び出されないと、次の状況が発生します: 🎜🎜 (1) ポーリング キューが空でない場合 : 🎜🎜🎜🎜イベント ループは、キューが空になるか、実行されたコールバックがオンラインになるまで、ポーリング キュー内のコールバック (新しい I/O イベント) を同期的に実行します。 🎜🎜🎜🎜(2) ポーリング キューが空の場合: 🎜🎜🎜🎜 スクリプトが setImmediate() を呼び出すと、イベント ループはポーリング フェーズを終了し、setImmediate() コールバックを実行するチェック フェーズに入ります。 🎜🎜🎜🎜スクリプトが setImmediate() によって呼び出されない場合、イベント ループはコールバック (新しい I/O イベント) がキューに追加されるのを待機し、すぐに実行します。 🎜
ポーリングフェーズに入り、タイマーが呼び出されると、次のことが起こります:
ポーリングキューが空になると、イベントループはタイマーがあるかどうかを確認し、1つ以上のタイマーが到着した場合、イベントループは戻りますタイマー フェーズに移行し、それらのタイマー コールバックを実行します (つまり、次のティックを入力します)。
Priority:
Next Tick Queue > MicroTask Queue
setTimeout, setInterval > setImmediate
タイマーは時間が到来したかどうかを判断するために赤黒ツリーからタイマーを取り出す必要があるため、時間計算量は O (lg(n)) なので、イベントを非同期ですぐに実行したい場合は、setTimeout(func, 0) を使用しないことをお勧めします。代わりに process.nextTick() を使用してこれを実行します。
私が学んだノードクラスターアーキテクチャは主に次のモジュールに分かれています:
Nginx (ロードバランシング、スケジューリング) -> ノードクラスター -> Redis (同期ステータス)
コンパイルしました私の理解に基づく図:
もちろん、これは理想的なアーキテクチャであるはずです。 Redis の読み取り/書き込みは非常に高速ですが、これはメモリ プールにデータを保存し、メモリ上で関連する操作を実行するためです。
これはサーバーのメモリ負荷としては非常に高いので、通常は以下に示すように Mysql をアーキテクチャに追加します:
まずこの図について説明します:
ユーザーデータが到着すると、データは次のように書き込みます。まず Mysql にアクセスし、ノードでデータが必要なときに Redis にアクセスして読み取ります。データが見つからない場合は、Mysql にアクセスして目的のデータをクエリし、次回使用するときに Redis に直接アクセスしてください。クエリ。
Redis での読み取り/書き込みのみと比較した Mysql の追加の利点は次のとおりです:
(1) 短期的に Redis に無駄なデータを書き込むことを避け、メモリを占有し、Redis の負担を軽減します
(2) 修正が必要後の段階のデータ 特定のクエリと分析 (運用アクティビティのユーザーの増加の分析など) を実行する場合、SQL リレーショナル クエリは大きな助けになります
もちろん、短期的な大量の書き込みを処理する場合は、データを Redis に直接書き込むこともできます。データを迅速に保存し、トラフィックに対処するサーバーの能力を高めるという目的を達成するために、データはトラフィックが落ち着いたときに個別に MySQL に書き込まれます。
一般的なアーキテクチャを簡単に紹介した後、各部分の詳細を詳しく見てみましょう:
トラフィック アクセス層の機能は、受け入れられたすべてのトラフィックを処理し、次のサービスを提供することです:
トラフィックバッファリング
迂回と転送
タイムアウト検出
ユーザーとの接続を確立するためのタイムアウト
もちろん、このプラットフォームはこの機能を転送するだけでなく、次のサービスを提供する大規模なプライベート クラウド システムとして理解できます:
(1 )信頼性の高いノードコードを作成し、ニーズに合わせたバックエンドサービスを提供します
(2) 高パフォーマンスのクエリ ステートメントを作成し、Redis および Mysql と対話し、クエリ効率を向上させます
(3) Redis を介してクラスター内の各ノード サービスのステータスを同期します
(4) を介して物理マシンを管理/監視しますハードウェア管理プラットフォームのステータス、管理 IP アドレスなど (実際、作業のこの部分をこの層に配置するのは不適切な気がしますが、どの層に配置すればよいのかわかりません...)
(もちろん、この部分のエントリは簡単に列挙するだけですが、蓄積して深く理解するには時間がかかります)
この層の主な作業は次のとおりです:
(1) MySQL の作成と関連するページとテーブルの設計; クエリの利便性を向上させるために必要なインデックスと外部キーを確立します
(2) Redis をデプロイし、対応するインターフェイスをノード層に提供します
関連する推奨事項:
vue がバックエンドデータをリクエストするためにどのように axios を使用するか
以上がNode.js の高同時実行性と分散クラスタリングに関する簡単な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。