In den letzten Monaten haben meine Kollegen und ich erfolgreich ein serverseitiges System zur Versendung von Push-Benachrichtigungen an iPhone-Geräte entwickelt. Im Grunde registriert sich ein Benutzer für diese Benachrichtigungen über einen RESTful-Webservice ( Spray-Server , kürzlich aktualisiert, um die Sprühdose wie die HTTP-Schicht), und die Logik plant eine oder mehrere Nachrichten für den Versand in der Zukunft, indem sie den Scheduler von Akka verwendet.
Dieses System, so wie wir es aufgebaut haben, funktioniert einfach: Es kann Hunderte, vielleicht sogar Tausende von HTTP-Anfragen pro Sekunde verarbeiten und Benachrichtigungen mit einer Rate von 23.000 pro Sekunde versenden - möglicherweise sogar noch mehr, wenn wir die Protokollausgabe reduzieren, mehrere Benachrichtigungssender-Akteure hinzufügen (und damit mehr Verbindungen mit Apple), und es könnten einige Optimierungen in der von uns verwendeten Java-Bibliothek vorgenommen werden ( java-apns ).
In dieser Frage geht es darum, wie man es richtig(tm) macht. Mein Kollege, der sich mit Scala und akteurbasierten Systemen im Allgemeinen viel besser auskennt, bemerkte, dass die Anwendung kein "reines" akteurbasiertes System ist - und er hat Recht. Was ich mich jetzt frage, ist, wie man es richtig macht.
Zurzeit haben wir ein einziges Spray HttpService
Akteur, der nicht unterklassifiziert ist und mit einer Reihe von Direktiven initialisiert wird, die unsere HTTP-Dienstlogik umreißen. Derzeit haben wir, stark vereinfacht, Direktiven wie diese:
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))
}
}
Wenn ich das richtig verstehe, ist das Warten auf eine Antwort von einem Akteur in der akteurbasierten Programmierung ein Tabu (und das !! ist veraltet). Was ich glaube, ist der "richtige" Weg, um es zu tun ist, um die request
Objekt in den Bereich persister
Akteur in einer Nachricht, und lassen Sie ihn request.complete
sobald es eine generierte ID vom Back-End erhalten hat.
Ich habe eine der Routen in meiner Anwendung umgeschrieben, um genau dies zu tun; in der Nachricht, die an den Akteur gesendet wird, wird auch das Anforderungsobjekt / die Referenz gesendet. Dies scheint zu funktionieren, wie es sein soll:
content(as[SomeBusinessObject]) { businessObject => request =>
persister ! new PersistSchedule(request, businessObject)
}
Mein Hauptanliegen ist, dass wir an der request
Objekts an die "Geschäftslogik", in diesem Fall den Persister. Der Persister erhält nun zusätzliche Verantwortung, d.h. er ruft request.complete
und das Wissen darüber, in welchem System er läuft, d. h. dass er Teil eines Webservice ist.
Was wäre der richtige Weg, um eine Situation wie diese zu behandeln, so dass der Persister-Akteur wird nicht bewusst, dass es Teil eines http-Dienstes ist, und muss nicht wissen, wie die generierte ID auszugeben?
Ich denke, dass die Anforderung noch an den Persister-Akteur übergeben werden sollte, aber anstelle der Persister-Akteur Aufruf request.complete, sendet es eine Nachricht zurück an den HttpService-Akteur (ein SchedulePersisted(request, businessObjectId)
Nachricht), die einfach aufruft request.complete("/businessObject/%s".format(businessObjectId))
. Im Grunde genommen:
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)
}
}
Bin ich mit diesem Ansatz auf dem richtigen Weg?
Eine kleinere sekundäre spray-server
spezielle Frage: Ist es in Ordnung, die Unterklasse HttpService
und die Empfangsmethode außer Kraft setzen, oder mache ich die Sache auf diese Weise kaputt? (Ich habe keine Ahnung von der Unterklassifizierung von Akteuren oder wie man nicht erkannte Nachrichten an den "übergeordneten" Akteur weitergibt)
Letzte Frage: Ist das Bestehen der request
Objekt/Referenz in Akteursnachrichten, die die gesamte Anwendung durchlaufen können, ein guter Ansatz, oder gibt es eine bessere Möglichkeit, sich zu "erinnern", welche Anfrage als Antwort gesendet werden sollte, nachdem die Anfrage durch die Anwendung geleitet wurde?