46

長いバージョン:

私は erlang を初めて使用し、スケーラブルなアーキテクチャに使用することを検討しています。私は、プラットフォームの信頼性とフォールト トレランスを売り込んでいる多くの支持者を見つけました。

ただし、メッセージが一時メモリにキューイングされるこのシステムでフォールト トレランスがどのように達成されるかを正確に理解するのに苦労しています。スーパーバイザーの階層を調整して、死亡したプロセスを再起動できることは理解していますが、進行中の作業での再起動の意味についての議論を見つけることができませんでした。進行中のメッセージと、瀕死のノードで失われた部分的に完了した作業のアーティファクトはどうなりますか?

すべてのプロデューサは、コンシューマ プロセスが終了したときに確認応答されなかったメッセージを自動的に再送信しますか? そうでない場合、これはどのように耐障害性があると見なすことができますか? もしそうなら、処理されたが完全に承認されていないメッセージが再送信され、不適切に再処理されるのを防ぐものは何ですか?

(これらの懸念は erlang に固有のものではないことは認識しています。同様の懸念は、どの分散処理システムでも発生します。しかし、erlang 愛好家は、このプラットフォームによってこれがすべて「簡単」になると主張しているようです..?)

メッセージが再送信されると仮定すると、複雑なメッセージング チェーンのダウンストリームへの影響が障害後に非常に混乱するシナリオが容易に想像できます。ある種の重い分散トランザクション システムがなければ、すべてのプロセスで重複に対処せずに一貫性と正確性を維持する方法がわかりません。トランザクションが複数回実行されるのを防ぐために、アプリケーション コードは常に制約を適用する必要がありますか?

短縮版:

分散された erlang プロセスは重複メッセージの影響を受けますか? もしそうなら、重複保護 (つまり、冪等性) はアプリケーションの責任ですか、それとも erlang/OTP は何らかの形でこれを助けてくれますか?

4

3 に答える 3

138

これをポイントに分けて、意味をなすことを願っています。The Hitchhiker's Guide to Concurrencyに書いたことを少し再ハッシュするかもしれません。Erlang でメッセージ パッシングが行われる方法の背後にある理論的根拠の詳細を得るために、それを読みたいと思うかもしれません。


1.メッセージ送信

Erlang でのメッセージ パッシングは、メールボックス (データを格納するための一種のキュー) に送信される非同期メッセージを通じて行われます。メッセージが受信されたかどうか、または有効なプロセスに送信されたかどうかについても、まったく仮定がありません。これは、[言語レベルで] 誰かがおそらく 4 日でメッセージを処理したいと考え、特定の状態に達するまでその存在を認めようとしないと想定するのがもっともらしいためです。

このランダムな例として、データを 4 時間処理する長時間実行プロセスを想像してみてください。メッセージを処理できない場合、メッセージを受信したことを本当に認めるべきでしょうか? そうすべきかもしれませんし、そうでないかもしれません。それは本当にあなたのアプリケーションに依存します。そのため、仮定は行われません。メッセージの半分を非同期にすることができ、そうでないものは 1 つだけにすることができます。

Erlang は、必要に応じて確認メッセージを送信する (そしてタイムアウトで待機する) ことを期待しています。タイムアウトに関係する規則と応答の形式は、指定するプログラマーに任されています -- Erlang は、タスクが完了したときに、メッセージが一致するかどうか (メッセージコードの新しいバージョンがホットロードされると、4 時間以内に一致する可能性があります) など。

簡単に言えば、メッセージが読まれなかったり、受信に失敗したり、送信中に誰かがプラグを抜いて中断したりしても、あなたがそれを望まなければ問題ではありません。それを問題にしたい場合は、プロセス全体のロジックを設計する必要があります。

Erlang プロセス間で高レベルのメッセージ プロトコルを実装する負担は、プログラマに与えられます。


2. メッセージプロトコル

あなたが言ったように、これらのメッセージは一時的なメモリに保存されます。プロセスが終了すると、まだ読み取られていないすべてのメッセージが失われます。もっと欲しい場合は、さまざまな戦略があります。それらのいくつかは次のとおりです。

  • メッセージをできるだけ速く読み取り、必要に応じてディスクに書き込み、確認応答を返信して後で処理します。これを、RabbitMQ や ActiveMQ などの永続的なキューを持つキュー ソフトウェアと比較してください。
  • プロセス グループを使用して、複数のノード上のプロセス グループ全体でメッセージを複製します。この時点で、トランザクションのセマンティクスを入力できます。これは、トランザクション コミット用の mnesia データベースに使用されます。
  • すべてがうまくいったという確認応答または失敗メッセージを受け取るまで、何も機能していると思い込まないでください。
  • プロセス グループと失敗メッセージの組み合わせ。最初のプロセスが (ノードがダウンしたために) タスクの処理に失敗した場合、通知が VM によって自動的にフェールオーバー プロセスに送信され、そこで代わりに処理されます。この方法は、ハードウェア障害を処理するために完全なアプリケーションで使用されることがあります。

当面のタスクに応じて、これらの 1 つまたは複数を使用できます。それらはすべて Erlang で実装することが可能であり、多くの場合、モジュールはすでに作成されており、面倒な作業を行うことができます。

したがって、これはあなたの質問に答えるかもしれません。プロトコルは自分で実装するため、メッセージが複数回送信されるかどうかはユーザーが選択できます。


3. 耐障害性とは

上記の戦略のいずれを選択するかは、フォールト トレランスがあなたにとって何を意味するかによって異なります。場合によっては、「データが失われることはなく、タスクが失敗することもありません」と言う意味があります。フォールト トレランスを使用して、「ユーザーがクラッシュすることは決してない」と言う人もいます。Erlang システムの場合、通常の意味は、システムを稼働させ続けることです。全員が電話を切るよりも、1 人のユーザーが電話を切っても問題ありません。

ここでのアイデアは、失敗したものは失敗させますが、残りは実行し続けることです。これを実現するために、VM が提供するものがいくつかあります。

  • プロセスが停止した時期とその理由を知ることができます
  • 相互に依存しているプロセスのいずれかが失敗した場合、それらのプロセスを一緒に停止させることができます
  • キャッチされていないすべての例外を自動的にログに記録するロガーを実行でき、独自の例外を定義することもできます
  • ノードを監視できるので、ノードがいつダウンしたか (または切断されたか) を知ることができます
  • 失敗したプロセス (または失敗したプロセスのグループ) を再起動できます。
  • 1 つのノードで障害が発生した場合、アプリケーション全体を別のノードで再起動する
  • OTP フレームワークにはさらに多くのものがあります

これらのツールと、さまざまなシナリオを処理する標準ライブラリのいくつかのモジュールを使用すると、Erlang の非同期セマンティクスの上に必要なものをほとんど実装できます。


4. いくつかの注意事項

ここでの私の個人的な意見は、純粋なトランザクション セマンティクスが必要でない限り、Erlang に存在する以上の仮定を持つことはかなり難しいということです。常に問題になる問題の 1 つは、ノードがダウンすることです。サーバーが実際にクラッシュしたためにダウンしたのか、ネットワークに障害が発生したためにダウンしたのかはわかりません。

サーバーがクラッシュした場合、タスクをやり直すだけで十分です。ただし、ネット スプリットでは、いくつかの重要な操作が 2 回実行されないようにする必要がありますが、失われることもありません。

それは通常、基本的に 3 つのオプションを提供するCAP 定理に要約され、そのうちの 2 つを選択する必要があります。

  1. 一貫性
  2. 分割耐性
  3. 可用性

自分の位置に応じて、さまざまなアプローチが必要になります。CAP 定理は通常、データベースを説明するために使用されますが、データを処理する際にある程度の耐障害性が必要な場合は常に、同様の質問をする必要があると思います。

于 2010-07-05T01:49:14.493 に答える
5

erlang OTP システムは耐障害性があります。それでも、同じようにフォールト トレラントなアプリを構築する必要がなくなるわけではありません。erlang と OTP を使用する場合、信頼できるものがいくつかあります。

  1. プロセスが終了すると、そのプロセスは再起動されます。
  2. ほとんどの場合、プロセスがクラッシュしてもアプリ全体がダウンすることはありません
  3. メッセージが送信されると、受信者が存在する場合に受信されます。

私が知る限り、erlang のメッセージは重複しません。メッセージを送信し、プロセスがそれを受信すると、メッセージはキューから削除されます。ただし、メッセージを送信し、プロセスがそのメッセージを受信して​​も処理中にクラッシュした場合、そのメッセージはなくなり、処理されません。その事実は、システムの設計で考慮する必要があります。OTP は、プロセスを使用してインフラストラクチャの重要なコード (スーパーバイザー、gen_servers など) を、クラッシュする可能性のあるアプリケーション コードから分離することで、これらすべてを処理するのに役立ちます。

たとえば、作業をプロセス プールにディスパッチする gen_server があるとします。プール内のプロセスがクラッシュして再起動する可能性があります。ただし、gen_server は、メッセージを受信して​​プールにディスパッチして処理することだけを目的としているため、稼働したままです。これにより、プールでエラーやクラッシュが発生してもシステム全体が稼働し続けることができ、常に何かがあなたのメッセージを待っています.

システムがフォールト トレラントであるからといって、アルゴリズムがフォールト トレラントであるとは限りません。

于 2010-07-04T20:53:33.433 に答える
3

回答はErlangとはまったく関係がないと思います。これは、クライアント サーバー プロトコルに「少なくとも 1 回」、「最大 1 回」、または「正確に 1 回」の保証を実装することを選択できるクライアント サーバー相互作用のセマンティクスにあります。これらの呼び出しセマンティクスはすべて、一意のタグ、再試行、およびクライアント要求を送信または実行する前にクライアントとサーバーの両方でログに記録することで実装できるため、クラッシュ後にサーバーが取得できます。重複に加えて、メッセージが失われたり、孤立したり、遅延したりする可能性があります。

于 2011-01-15T01:05:37.007 に答える