タスクの説明
多くの場合、モバイルプラットフォームの開発者と同様に、Webアプリケーション開発者は、イベントの完了の通知を送信するタスクを持っています。 例:スケジュールされた会議、電話、特定のスケジュールエントリ(todoシート)のユーザーへの通知、または将来の他のアクションのリマインダーの作成。
同様のアプリケーションがソーシャルにある場合。 ネットワーク、そして直接送信通知のために、VKontakteネットワークはこれのために
secure.sendNotificationメソッドを提供します。
クラスメートの場合:
notification.sendSimple ;
AppleおよびAndroidデバイスの場合、そのような通知はそれぞれ
Apple Push Notification Serviceおよび
Android Cloud to Device Messaging(C2DM)を介して送信できます。
解決するツールの選択
この問題を解決するために、2つの固定資産が選択されました。
Node.js (0.6.x)および
Redis Plus
Expressは 、テスト用の受信および送信された通知と
Vowsの統計を表示するプリミティブUIを実装します。
最初は、Node.jsバージョン0.4クラスターが個別の
クラスターパッケージとして作成されるため、
クラスターモジュール(0.6に含まれる)を介してすべてのコアを簡単に使用する機能が含まれていました。
Redisは、すぐに使用できるさまざまなデータ型をサポートするために選択されました(同じMemcacheと比較)。 作業キューを整理するための便利なコマンドも用意されています。 また、
Youporn.comは100%RedisサイトになったためコアAPI
server_name / send_delayed-遅延通知(GETおよびPOSTパラメーターの両方を受け入れます)
- delay-送信する必要がある間隔(秒単位で設定)
- url-時間が来たときにリクエストする必要があるアドレス。 ソーシャルネットワークの場合、これはsendNotificationまたはnotification.sendSimpleを介して送信されます。プッシュを送信するには、APNSまたはC2DNへの送信を直接処理する追加サービスに送信できます。
- uid-ユーザーID。
- type-統計のパラメーター(appointment_1、planned_call)
- recheck_url-通知を送信する前に要求する必要がある追加のアドレス。 これは、タスクの現在のステータスを追跡するのに役立ちます。 たとえば、建設が加速または完全にキャンセルされました。 urlは「OK」または「エラー」と回答する必要があります。肯定的な回答の場合、通知が送信されます。 (パラメーターはオプションであり、存在しない場合、チェックは行われません)
- send_than_online-追加パラメーターが存在する場合、イベントはキューに追加されますが、送信は/ user_online要求が呼び出された場合にのみ実行されます。 このパラメーターの導入は、多くのネットワークが1ユーザーあたり1日あたりのパッケージ数を制限しているという事実によるものです。
server_name / user_online-特定のユーザーのイベントを取得し(時間が来た場合)、送信します
アプリの設計
キューイングの主な構造:
メッセージキュー -ランクがイベントの時間になるSORTEDSET。
オンラインメッセージのキュー -各uidのSORTEDSETは個別です。このランクはイベントの時間です。
処理用キュー -LIST-このメッセージがこのリストにある場合、メッセージは処理されます。
マスタープロセス
1.サーバー上のコアの数に等しい数個のワーカーを起動します。
2. REPLが開始されます。
3.
メッセージキュースキャナーを起動し
ます (
オンラインメッセージキューはスキャンされません)。
4.労働者の転倒を監視し、転倒した場合は再起動します。
5.完全に落下した場合、個々のイベントからメッセージキューを復元します。
6. UIサーバーを起動して、統計を追跡します。
スキャナーは、ZREVRANGEBYSCOREを介して、現在時刻と逆の順序で100に等しい量のイベントのパケットを
メッセージキューから
受信します 。 トランザクションでは、
処理のためにそれらを
処理キューに入れ、
メッセージキューから削除し
ます 。
別の労働者
1. send_delayedまたはuser_onlineコマンドを受け入れます。 これらのイベントは、JSON.stringifyを介してjson形式でHASHに記録されます。
msgpackのイベントストレージも試行されました。 上記のテストによると、より高速にすべきであると書かれていますが、単純なオブジェクトの場合、速度の向上はありませんでした。 イベントIDは、ランダムなハウスによって作成され、
メッセージキューに入れられ
ます 。 イベントの各キーについて、応答時間+設定からの遅延に等しいライフタイムが設定されます。
2.
処理のためにキュースキャナー
を起動し
ますスキャナーは、BLPOPを介してLISTからイベントを受信します(キューにある場合にのみ取得するため)。 サーバーを実行したまま./redis-cliでMONITORコマンドを実行すると、ログは次のようになります。
1336484301.674405 "zrevrangebyscore" "actual_queue_test-queue" "1336484301673" "-inf" "limit" "0" "100"
1336484301.675102 "zrevrangebyscore" "actual_queue_test-queue" "1336484301674" "-inf" "limit" "0" "100"
1336484301.675387 "zrevrangebyscore" "actual_queue_test-queue" "1336484301675" "-inf" "limit" "0" "100"
// fill processing queue
Worker.prototype.fetch = function(fn) {
var self = this;
this.client.zrevrangebyscore(this.queue_key, Date.now(),
"-inf", "limit", 0, self.pack, function(err, reply) {
process.nextTick(function() {
self.fetch();
}); // re-schedule fetch
...
この場合、次のティックでフェッチを実行するたびに。 ご覧のとおり、キューにイベントがない場合、ティックごとにチェックする意味がありません。
JSON.parseは、
最後の送信のキーが存在しない場合、または(現在の時間-キー> USER_POLL_THRESHOLD-30分)-recheck_urlがあるかどうかを確認した後、UrlSenderがhttp要求を送信するイベントデータイベントをトリガーします。 httpリクエストで200コードを受信した場合、イベント「event-sent」を開始し、データベース内のイベントを消去し、現在の時刻までにユーザーの
最後の送信のキーを設定し
ます 。
オンラインイベントを処理する順序は次のとおりです:特定のユーザーの
オンラインメッセージのキューを受信するZREVRANGEBYSCOREを介して、彼は最も近いイベントを受信します。 USET_POLL_THRESHOLD_ONLINEを設定することで、送信のタイミングが来たかどうかを確認し、
処理のために処理キューに入れて、
メッセージキューから削除し
ます 。
テスト中
自動テストは、vowsフレームワークを使用して作成されます。 テストの主なアイデアは、特別なロガーメソッドを使用してRedis LISTにイベントのシーケンスを記録し、それをイベントの参照シーケンスと比較することです。
logger.spec('event-processed'); //only test env
…
logger.spec('event-fetched'); //only test env
テストの実行:
1. config / settings.test.js-> config / settings.jsをコピーして、独自の設定に変更します。
2.サーバーの起動:APP_ENV = npm startのテスト
3.テストの実行:APP_ENV = test vows --spec test / delay-request-test.js
テストスクリプトの例(ルビー)
require 'uri'
require 'net/http'
api_url = "http://127.0.0.1:8000/send_delayed"
url = "http://127.0.0.1:8000/health"
recheck_url = "http://127.0.0.1:8000/recheck_url_correct"
# recheck_url = "http://127.0.0.1:8000/recheck_url_error"
uid=rand(1000)
type="test_common"
delay=1
post_url = URI.parse(api_url)
# res = Net::HTTP.post_form(post_url,{"url" => url, "delay" => delay,
# "uid" => uid, "type" => type, "recheck_url" => recheck_url, "send_than_online" => true})
res = Net::HTTP.post_form(post_url,{"url" => url, "delay" => delay,
"uid" => uid, "type" => type, "recheck_url" => recheck_url})
p res.inspect
統計ビューのインターフェースの例
サーバーのソースコード
ピンキーによる共同執筆
https://github.com/catz/actual_queue