将 WebService 与 Akka Actors 和 play 框架一起使用
Using a WebService with Akka Actors and the play framework
我已经使用 Play & Akka 构建了一个 Web 服务,现在需要集成另一个 Web 服务,我的 Web 服务是客户端。
我的默认控制器(带有关联的路由文件)看起来像
class myController @Inject() (implicit val messagesApi: MessagesApi,
config: play.api.Configuration) extends Controller with I18nSupport {
// Actions
}
这启动了一个大型演员系统,一切都很好。
其中一位演员的定义如下 -
class ActorMgr ( jobId: Long,
config: Config) extends Actor with ActorLogging {
// Actor specific stuff
}
我的问题是我现在需要从这个 actor 调用一个新的网络服务。此 Web 服务是一个数据库,将记录此参与者的结果。
我已经看到并遵循了(以及其他)
的指示
按照上面的说明,我应该将 WSClient 注入到我需要访问它的 class 中。
我能够解决对第二个控制器的依赖注入,如下所示
class DbController @Inject() (ws: WSClient) extends Controller {
def post = Action {
// access webservice
}
}
这行得通,我可以执行 "post" 操作,方法是访问它在路由文件中映射到的 URL,从而访问 Web 服务。我现在也有两个控制器。
我的问题是从 ActorMgr(Akka Actor)访问 Web 服务控制器 "post" 方法。我该如何启用它?
经过大量研究,我想在这里更新我的发现。虽然我能够如下解决我的具体问题,但这里还有很多要说的。
我的具体解决方案先 -
我没有使用 DbController,而是按如下方式包装了我的服务,并在需要的地方注入了它 -
trait Db {
def post
}
class InfluxDb @Inject() (ws: WSClient) extends Db {
val logger = LoggerFactory.getLogger(classOf[InfluxDb])
logger.info("InfluxDb: Initiatlized")
def post = {
val req = ws.url("http://localhost:9086/write")
.withQueryString("db" -> "db1")
.withHeaders("Content-Type" -> "application/json")
.post("job_id,command=PUT value=99")
logger.debug("InfluxDb: Post")
}
}
话虽如此,注入东西给我带来了很多问题。我终于意识到这里有一些不同的用例 -
- 使用 Akka 和 Guice,不使用 Playframework
- 使用 Playframework + Akka + Guice 并注入顶级 actor
- 使用 Playframework + Akka + Guice 并注入儿童演员
- 使用 playframework + Akka + Guice 但未创建 "injecting" 您的顶级 actor 和 Actor 系统。
以下是解决上述问题的方法。
- 对于 (1) - 参考 guice akka tutorial
- 对于 (2) 和 (3) - 请参阅 Playframework Documentation
- 对于(4)这个有点棘手
您将需要扩展 "IndirectActorProducer",然后使用它来创建您的 ActorRef。问题是 "Props" 不知道如何与 Guice 交互。这也是(1)中解决方案的一部分
下面的示例代码显示了所有 4 个用例,并进行了编译。在下面的代码中
ParentActor - 指的是上面的用例 (2),ChildActor 指的是用例 (3),ParentActor_2 & ChildActor_2 指的是用例 (4)。
// play imports
import play.api.mvc._
import play.api.Logger
import play.api.mvc.Results
// actor imports
import akka.actor.{Actor, ActorSystem, ActorRef, Props, IndirectActorProducer}
// DI imports
import com.google.inject.{Injector, AbstractModule, Key, Provides}
import javax.inject._
import com.google.inject.assistedinject.Assisted
import play.libs.akka.AkkaGuiceSupport
import play.api.libs.concurrent.InjectedActorSupport
class MainCntrlr @Inject() (injector : Injector,
@Named("PActor") pa: ActorRef,
cfP: ParentActor_2.Factory)
extends Controller {
Logger.debug("MainCntrlr: created")
val pa_2 = ActorSystem("test")
.actorOf(Props(classOf[GuiceActorProducer], injector, "PActor_2"), "PA_2")
pa ! 12
pa_2 ! 100
def index = Action { Ok (views.html.index.render()) }
}
class ParentActor @Inject() (cf: ChildActor.Factory) extends Actor with InjectedActorSupport {
Logger.debug("ParentActor: created")
val cactor = injectedChild(cf(2),"childActor")
cactor ! 10
def receive = { case _ => Logger.debug("ParentActor received msg") }
}
object ChildActor { trait Factory { def apply(i: Int) : Actor } }
class ChildActor @Inject()( i: Injector, @Assisted v: Int) extends Actor {
Logger.debug("ChildActor: created with value " + v.toString)
def receive = { case _ => Logger.debug("ChildActor received msg") }
}
class ParentModule extends AbstractModule with AkkaGuiceSupport {
def configure () = {
bindActor(classOf[ParentActor],"PActor")
bindActorFactory(classOf[ChildActor], classOf[ChildActor.Factory])
bindActorFactory(classOf[ParentActor_2], classOf[ParentActor_2.Factory])
bindActorFactory(classOf[ChildActor_2], classOf[ChildActor_2.Factory])
}
}
object ParentActor_2 { trait Factory { def apply() : Actor } }
class ParentActor_2 @Inject() (cf: ChildActor_2.Factory) extends Actor with InjectedActorSupport {
Logger.debug("ParentActor_2: created")
val cactor = injectedChild(cf(4),"childActor_2")
cactor ! 10
def receive = { case _ => Logger.debug("ParentActor_2 received msg") }
}
object ChildActor_2 { trait Factory { def apply(i: Int) : Actor } }
class ChildActor_2 @Inject() ( i: Injector, @Assisted v: Int) extends Actor {
Logger.debug("ChildActor_2: created with value " + v.toString)
def receive = { case _ => Logger.debug("ChildActor_2 received msg") }
}
class GuiceActorProducer(val injector: Injector, val actorName: String)
extends IndirectActorProducer {
override def actorClass = classOf[ParentActor_2]
override def produce() =
injector.getBinding(Key.get(classOf[ParentActor_2])).getProvider.get()
}
然后在我的 application.conf
play.modules.enabled += "package.ParentModule"
我已经使用 Play & Akka 构建了一个 Web 服务,现在需要集成另一个 Web 服务,我的 Web 服务是客户端。
我的默认控制器(带有关联的路由文件)看起来像
class myController @Inject() (implicit val messagesApi: MessagesApi,
config: play.api.Configuration) extends Controller with I18nSupport {
// Actions
}
这启动了一个大型演员系统,一切都很好。
其中一位演员的定义如下 -
class ActorMgr ( jobId: Long,
config: Config) extends Actor with ActorLogging {
// Actor specific stuff
}
我的问题是我现在需要从这个 actor 调用一个新的网络服务。此 Web 服务是一个数据库,将记录此参与者的结果。
我已经看到并遵循了(以及其他)
的指示按照上面的说明,我应该将 WSClient 注入到我需要访问它的 class 中。
我能够解决对第二个控制器的依赖注入,如下所示
class DbController @Inject() (ws: WSClient) extends Controller {
def post = Action {
// access webservice
}
}
这行得通,我可以执行 "post" 操作,方法是访问它在路由文件中映射到的 URL,从而访问 Web 服务。我现在也有两个控制器。
我的问题是从 ActorMgr(Akka Actor)访问 Web 服务控制器 "post" 方法。我该如何启用它?
经过大量研究,我想在这里更新我的发现。虽然我能够如下解决我的具体问题,但这里还有很多要说的。
我的具体解决方案先 -
我没有使用 DbController,而是按如下方式包装了我的服务,并在需要的地方注入了它 -
trait Db {
def post
}
class InfluxDb @Inject() (ws: WSClient) extends Db {
val logger = LoggerFactory.getLogger(classOf[InfluxDb])
logger.info("InfluxDb: Initiatlized")
def post = {
val req = ws.url("http://localhost:9086/write")
.withQueryString("db" -> "db1")
.withHeaders("Content-Type" -> "application/json")
.post("job_id,command=PUT value=99")
logger.debug("InfluxDb: Post")
}
}
话虽如此,注入东西给我带来了很多问题。我终于意识到这里有一些不同的用例 -
- 使用 Akka 和 Guice,不使用 Playframework
- 使用 Playframework + Akka + Guice 并注入顶级 actor
- 使用 Playframework + Akka + Guice 并注入儿童演员
- 使用 playframework + Akka + Guice 但未创建 "injecting" 您的顶级 actor 和 Actor 系统。
以下是解决上述问题的方法。
- 对于 (1) - 参考 guice akka tutorial
- 对于 (2) 和 (3) - 请参阅 Playframework Documentation
- 对于(4)这个有点棘手
您将需要扩展 "IndirectActorProducer",然后使用它来创建您的 ActorRef。问题是 "Props" 不知道如何与 Guice 交互。这也是(1)中解决方案的一部分
下面的示例代码显示了所有 4 个用例,并进行了编译。在下面的代码中
ParentActor - 指的是上面的用例 (2),ChildActor 指的是用例 (3),ParentActor_2 & ChildActor_2 指的是用例 (4)。
// play imports
import play.api.mvc._
import play.api.Logger
import play.api.mvc.Results
// actor imports
import akka.actor.{Actor, ActorSystem, ActorRef, Props, IndirectActorProducer}
// DI imports
import com.google.inject.{Injector, AbstractModule, Key, Provides}
import javax.inject._
import com.google.inject.assistedinject.Assisted
import play.libs.akka.AkkaGuiceSupport
import play.api.libs.concurrent.InjectedActorSupport
class MainCntrlr @Inject() (injector : Injector,
@Named("PActor") pa: ActorRef,
cfP: ParentActor_2.Factory)
extends Controller {
Logger.debug("MainCntrlr: created")
val pa_2 = ActorSystem("test")
.actorOf(Props(classOf[GuiceActorProducer], injector, "PActor_2"), "PA_2")
pa ! 12
pa_2 ! 100
def index = Action { Ok (views.html.index.render()) }
}
class ParentActor @Inject() (cf: ChildActor.Factory) extends Actor with InjectedActorSupport {
Logger.debug("ParentActor: created")
val cactor = injectedChild(cf(2),"childActor")
cactor ! 10
def receive = { case _ => Logger.debug("ParentActor received msg") }
}
object ChildActor { trait Factory { def apply(i: Int) : Actor } }
class ChildActor @Inject()( i: Injector, @Assisted v: Int) extends Actor {
Logger.debug("ChildActor: created with value " + v.toString)
def receive = { case _ => Logger.debug("ChildActor received msg") }
}
class ParentModule extends AbstractModule with AkkaGuiceSupport {
def configure () = {
bindActor(classOf[ParentActor],"PActor")
bindActorFactory(classOf[ChildActor], classOf[ChildActor.Factory])
bindActorFactory(classOf[ParentActor_2], classOf[ParentActor_2.Factory])
bindActorFactory(classOf[ChildActor_2], classOf[ChildActor_2.Factory])
}
}
object ParentActor_2 { trait Factory { def apply() : Actor } }
class ParentActor_2 @Inject() (cf: ChildActor_2.Factory) extends Actor with InjectedActorSupport {
Logger.debug("ParentActor_2: created")
val cactor = injectedChild(cf(4),"childActor_2")
cactor ! 10
def receive = { case _ => Logger.debug("ParentActor_2 received msg") }
}
object ChildActor_2 { trait Factory { def apply(i: Int) : Actor } }
class ChildActor_2 @Inject() ( i: Injector, @Assisted v: Int) extends Actor {
Logger.debug("ChildActor_2: created with value " + v.toString)
def receive = { case _ => Logger.debug("ChildActor_2 received msg") }
}
class GuiceActorProducer(val injector: Injector, val actorName: String)
extends IndirectActorProducer {
override def actorClass = classOf[ParentActor_2]
override def produce() =
injector.getBinding(Key.get(classOf[ParentActor_2])).getProvider.get()
}
然后在我的 application.conf
play.modules.enabled += "package.ParentModule"