Ozzがすでに述べたように、一般的なアプローチはメッセージキューです。設計の観点から見ると、メッセージキューは基本的にFIFOキューであり、かなり基本的なデータ型です。
メッセージキューを特別なものにしているのは、アプリケーションがエンキューを担当している間、別のプロセスがデキューを担当するということです。キューイングの用語では、アプリケーションはメッセージの送信者であり、デキュープロセスは受信者です。明らかな利点は、処理するメッセージがある限り、プロセス全体が非同期であり、受信側が送信側とは独立して動作することです。明らかな欠点は、すべてが機能するために追加のコンポーネントである送信者が必要なことです。
アーキテクチャはメッセージを交換する2つのコンポーネントに依存するようになったため、プロセス間通信という派手な用語を使用できます。
キューを導入すると、アプリケーションの設計にどのような影響がありますか?
アプリケーションの特定のアクションは、電子メールを生成します。メッセージキューを導入すると、それらのアクションはメッセージを代わりにキューにプッシュする必要があります(それ以上何もしない)。これらのメッセージには、受信者がメールを処理するときにメールを作成するために必要な最小限の情報が含まれている必要があります。
メッセージの形式と内容
メッセージの形式と内容は完全にあなた次第ですが、小さいほど良いことに留意する必要があります。キューの書き込みと処理は可能な限り高速にする必要があります。大量のデータをスローすると、ボトルネックが発生する可能性があります。
さらに、いくつかのクラウドベースのキューサービスはメッセージサイズに制限があり、大きなメッセージを分割する場合があります。分割されたメッセージは、要求すると1つのメッセージとして配信されますが、複数のメッセージに対して課金されます(もちろん、料金が必要なサービスを使用している場合)。
受信機の設計
Webアプリケーションについて説明しているので、レシーバーの一般的なアプローチは単純なcronスクリプトです。それは毎x
分(または秒)ごとに実行され、次のようになります。
n
キューからのメッセージのポップ量、
- メッセージを処理します(つまり、メールを送信します)。
getまたはfetchの代わりにpopと言っていることに注意してください。これは、レシーバがキューからアイテムを取得するだけでなく、アイテムをクリアする(キューからアイテムを削除するか、処理済みとしてマークする)ためです。それがどのように起こるかは、メッセージキューの実装とアプリケーションの特定のニーズによって異なります。
もちろん、私が説明しているのは基本的にバッチ操作であり、キューを処理する最も簡単な方法です。ニーズに応じて、より複雑な方法でメッセージを処理することもできます(より複雑なキューも必要になります)。
トラフィック
受信者はトラフィックを考慮し、実行時のトラフィックに基づいて処理するメッセージの数を調整できます。単純なアプローチは、過去の交通量データに基づいて交通量の多い時間を予測し、1 x
分ごとに実行されるcronスクリプトを使用すると仮定すると、次のようになります。
if(
now() > 2pm && now() < 7pm
) {
process(10);
} else {
process(100);
}
function process(count) {
for(i=0; i<=count; i++) {
message = dequeue();
mail(message)
}
}
非常に素朴で汚いアプローチですが、動作します。そうでない場合、他のアプローチは、各反復でサーバーの現在のトラフィックを見つけて、それに応じて処理アイテムの数を調整することです。絶対に必要ではない場合は、最適化を行わないでください。時間を無駄にします。
キューストレージ
アプリケーションがすでにデータベースを使用している場合、その上の単一のテーブルが最も簡単なソリューションになります。
CREATE TABLE message_queue (
id int(11) NOT NULL AUTO_INCREMENT,
timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed enum('0','1') NOT NULL DEFAULT '0',
message varchar(255) NOT NULL,
PRIMARY KEY (id),
KEY timestamp (timestamp),
KEY processed (processed)
)
それは本当にそれより複雑ではありません。もちろん、必要に応じて複雑にすることもできます。たとえば、優先度フィールドを追加できます(つまり、FIFOキューではなくなりますが、実際に必要な場合は誰が気にしますか?)。処理済みのフィールドをスキップすることで、より簡単にすることもできます(ただし、処理後は行を削除する必要があります)。
データベーステーブルは、1日あたり2000メッセージに理想的ですが、おそらく 1日あたり数百万のメッセージに対して適切にスケーリングされません。考慮すべき要素は100万個あります。インフラストラクチャ内のすべてが、アプリケーションの全体的なスケーラビリティに影響します。
いずれの場合も、データベースベースのキューを既にボトルネックとして特定していると仮定すると、次のステップはクラウドベースのサービスを調べることです。Amazon SQSは私が使用したサービスの1つであり、約束どおりに実行しました。似たようなサービスがたくさんあると確信しています。
メモリベースのキューも、特に短期間のキューの場合に考慮する必要があります。memcachedは、メッセージキューストレージとして優れています。
キューを構築するストレージが何であれ、賢く抽象化します。送信者も受信者も特定のストレージに縛られるべきではありません。さもなければ、後で別のストレージに切り替えることは完全なPITAになります。
現実のアプローチ
私はあなたがやっていることと非常に似ているメールのメッセージキューを構築しました。これはPHPプロジェクト上にあり、異なるストレージ用の複数のアダプターを提供するZend FrameworkのコンポーネントであるZend Queueを中心に構築しました。私のストレージ:
- 単体テスト用のPHP配列、
- 本番環境のAmazon SQS、
- 開発環境およびテスト環境でのMySQL。
私のメッセージはできる限りシンプルで、私のアプリケーションは重要な情報を持つ小さな配列を作成しました([user_id, reason]
)。メッセージストアは、その配列のシリアル化されたバージョンでした(最初はPHPの内部シリアル化形式、次にJSONでしたが、切り替えた理由を覚えていません)。これreason
は一定であり、もちろんreason
、より詳細な説明に対応する大きなテーブルがどこかにあります(1 reason
回で完全なメッセージの代わりに約500通の電子メールをクライアントに送信しました)。
参考文献
基準:
ツール:
興味深い読み物: