過去数か月で、私と同僚は、プッシュ通知を iPhone デバイスにディスパッチするためのサーバー側システムの構築に成功しました。基本的に、ユーザーは RESTful Web サービス ( Spray-Server、最近HTTP レイヤーとしてSpray-canを使用するように更新されました) を介してこれらの通知を登録し、ロジックは Akka のスケジューラーを使用して、将来のディスパッチのために 1 つまたは複数のメッセージをスケジュールします。
私たちが構築したこのシステムは、単純に機能します。1 秒間に数百、場合によっては数千の HTTP リクエストを処理でき、1 秒あたり 23,000 の速度で通知を送信できます。送信側のアクター (したがって、Apple とのより多くの接続) と、使用する Java ライブラリ ( java-apns ) で実行する最適化が行われる可能性があります。
この質問は、Right(tm) を行う方法に関するものです。私の同僚は、Scala とアクター ベースのシステム全般に精通しており、アプリケーションが「純粋な」アクター ベースのシステムではないことを指摘しており、その通りです。私が今疑問に思っているのは、それを正しく行う方法です。
現時点では、HttpService
サブクラス化されていない単一の Spray アクターがあり、HTTP サービス ロジックの概要を説明する一連のディレクティブで初期化されています。現在、非常に単純化された、次のようなディレクティブがあります。
post {
content(as[SomeBusinessObject]) { businessObject => request =>
// store the business object in a MongoDB back-end and wait for the ID to be
// returned; we want to send this back to the user.
val businessObjectId = persister !! new PersistSchedule(businessObject)
request.complete("/businessObject/%s".format(businessObjectId))
}
}
さて、これが正しければ、アクターからの「応答を待つ」ことは、アクターベースのプログラミングでは禁止されています (さらに、!! は非推奨です)。request
それを行う「正しい」方法は、オブジェクトをpersister
メッセージでアクターに渡しrequest.complete
、バックエンドから生成された ID を受信したらすぐに呼び出すことだと私は信じています。
これを行うために、アプリケーションのルートの 1 つを書き直しました。アクターに送信されるメッセージでは、リクエスト オブジェクト/参照も送信されます。これは、次のように機能するようです。
content(as[SomeBusinessObject]) { businessObject => request =>
persister ! new PersistSchedule(request, businessObject)
}
ここでの私の主な懸念は、request
オブジェクトを「ビジネス ロジック」、この場合は永続化プログラムに渡しているように見えることです。パーシスタは、 call などの追加の責任と、request.complete
それが実行されるシステムに関する知識 (つまり、それが Web サービスの一部である) を取得するようになりました。
このような状況を処理する正しい方法は何でしょうか?永続化アクターは、それが http サービスの一部であることを認識せず、生成された ID を出力する方法を知る必要がありません。
リクエストは引き続き永続化アクターに渡す必要があると考えていますが、永続化アクターが request.complete を呼び出す代わりに、SchedulePersisted(request, businessObjectId)
単に を呼び出す HttpService アクター (メッセージ) にメッセージを送り返しますrequest.complete("/businessObject/%s".format(businessObjectId))
。基本的:
def receive = {
case SchedulePersisted(request, businessObjectId) =>
request.complete("/businessObject/%s".format(businessObjectId))
}
val directives = post {
content(as[SomeBusinessObject]) { businessObject => request =>
persister ! new PersistSchedule(request, businessObject)
}
}
私はこのアプローチで正しい軌道に乗っていますか?
spray-server
サブクラスHttpService
化して receive メソッドをオーバーライドしてもいいですか、それともそのように壊れますか? (アクターのサブクラス化や、認識されていないメッセージを「親」アクターに渡す方法についてはわかりません)
最後の質問は、request
アプリケーション全体を通過する可能性のあるアクター メッセージでオブジェクト/参照を渡すことです。