fwi*_*tra 22 scala actor akka spray
在过去的几个月里,我和我的同事已经成功构建了一个服务器端系统,用于向iPhone设备发送推送通知.基本上,用户通过RESTful webservice(Spray-Server,最近更新为使用Spray-can作为HTTP层)注册这些通知,并且逻辑使用Akka的调度程序调度一个或多个消息以便将来调度.
这个系统,正如我们构建的那样,简单地工作:它可以每秒处理数百甚至数千个HTTP请求,并且可以以每秒23,000的速率发送通知 - 如果我们减少日志输出,可能会更多,添加多个通知发送方actor(以及与Apple的更多连接),并且可能在我们使用的Java库(java-apns)中进行一些优化.
这个问题是关于如何做对的(tm).我的同事,更了解Scala和基于演员的系统,注意到应用程序不是一个"纯粹的"基于演员的系统 - 他是对的.我现在想知道的是如何正确地做到这一点.
目前,我们有一个Spray HttpServiceactor,而不是子类,用一组指令来初始化,这些指令概述了我们的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))
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我做对了,那么来自演员的'等待回应'就是基于演员的编程中的禁忌(加上!!已被弃用).我认为"正确"的方法是将request对象传递persister给消息中的actor,并在request.complete从后端收到生成的ID后立即调用它.
我已经在我的应用程序中重写了其中一条路线来做这件事; 在发送给actor的消息中,还发送请求对象/引用.这看起来像是应该的:
content(as[SomeBusinessObject]) { businessObject => request =>
persister ! new PersistSchedule(request, businessObject)
}
Run Code Online (Sandbox Code Playgroud)
我在这里主要关注的是我们似乎将request对象传递给'业务逻辑',在这种情况下是持久性.持久性现在获得额外的责任,即调用request.complete,以及它运行的系统的知识,即它是Web服务的一部分.
处理这种情况的正确方法是什么,以便persister actor不会意识到它是http服务的一部分,并且不需要知道如何输出生成的ID?
我认为请求仍应传递给persister actor,但不是调用request.complete的persister actor,而是将消息发送回HttpService actor(一条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)
}
}
Run Code Online (Sandbox Code Playgroud)
我采用这种方法走在正确的轨道上吗?
一个较小的次要spray-server特定问题,是否可以继承HttpService和覆盖接收方法,或者我会以这种方式破坏它?(我对子类化actor没有任何线索,或者如何将无法识别的消息传递给'父'actor)
最后一个问题是,传递request整个应用程序中的actor消息中的对象/引用是否正常,或者是否有更好的方法来"记住"在通过应用程序传递请求后应该发送响应的请求?
关于你的第一个问题,是的,你走在正确的轨道上。(尽管我也希望看到一些替代方法来处理此类问题)。
我的一个建议是让persister演员完全不知道请求。您可以将请求作为Any类型传递。服务代码中的匹配器可以自动将 cookie 转换回Request.
case class SchedulePersisted(businessObjectId: String, cookie: Any)
// in your actor
override def receive = super.receive orElse {
case SchedulePersisted(businessObjectId, request: Request) =>
request.complete("/businessObject/%s".format(businessObjectId))
}
Run Code Online (Sandbox Code Playgroud)
关于你的第二个问题,演员班实际上和普通班没有什么不同。但您确实需要确保调用超类的receive方法,以便它可以处理自己的消息。我在原来的答案中还有一些其他方法可以做到这一点,但我认为我更喜欢像这样链接部分函数:
class SpecialHttpService extends HttpService {
override def receive = super.receive orElse {
case SpecialMessage(x) =>
// handle special message
}
}
Run Code Online (Sandbox Code Playgroud)