За последние несколько месяцев я и мои коллеги успешно создали серверную систему для отправки push-уведомлений на устройства iPhone. Обычно пользователь регистрируется для этих уведомлений через веб-службу RESTful ( Spray-Server , недавно обновленный для использования Spray-can в качестве уровня HTTP), а логика планирует одно или несколько сообщений. для отправки в будущем с помощью планировщика Akka.
Эта система в том виде, в котором мы ее создали, просто работает: она может обрабатывать сотни, а может быть, даже тысячи HTTP-запросов в секунду и может отправлять уведомления со скоростью 23 000 в секунду - возможно, даже больше, если мы сократим вывод журнала, добавьте несколько акторов-отправителей уведомлений (и, следовательно, больше соединений с Apple), и, возможно, потребуется некоторая оптимизация в используемой нами библиотеке Java ( java-apns ).
Этот вопрос о том, как это сделать правильно (тм). Мой коллега, гораздо более осведомленный о Scala и системах на основе акторов в целом, заметил, что приложение не является «чистой» системой, основанной на акторах, и он прав. Теперь мне интересно, как это сделать правильно.
На данный момент у нас есть один субъект Spray HttpService
, не подклассифицированный, который инициализируется набором директив, описывающих логику нашей службы 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
, как только получит сгенерированный идентификатор от серверной части.
Я переписал один из маршрутов в моем приложении именно так; в сообщении, которое отправляется актеру, также отправляется объект / ссылка запроса. Кажется, это работает так, как должно:
content(as[SomeBusinessObject]) { businessObject => request =>
persister ! new PersistSchedule(request, businessObject)
}
Меня больше всего беспокоит то, что мы, кажется, передаем объект request
в «бизнес-логику», в данном случае - в персистер. Теперь на персистера возлагается дополнительная ответственность, то есть вызов request.complete
и информация о том, в какой системе он работает, т.е. что это часть веб-службы.
Каким будет правильный способ справиться с подобной ситуацией, чтобы действующий субъект не осознавал, что он является частью службы http, и ему не нужно было знать, как выводить сгенерированный идентификатор?
I Я думаю, что запрос должен быть передан действующему субъекту, но вместо того, чтобы постоянный субъект, вызывающий request.complete, отправляет сообщение обратно субъекту HttpService (сообщение SchedulePersisted (request, businessObjectId)
), который просто вызывает 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)
}
}
На правильном ли я пути с этим подходом?
Небольшой вторичный спрей-сервер
, конкретный вопрос: можно ли создать подкласс HttpService
и переопределить метод приема , или я так сломаю? (Я понятия не имею о создании подклассов акторов или о том, как передавать нераспознанные сообщения «родительскому» актору)
Последний вопрос: передача объекта / ссылки запроса
в сообщениях акторов, которые могут проходить через все приложение - нормальный подход, или есть лучший способ «запомнить», на какой запрос следует отправить ответ после прохождения запроса через приложение?