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