在Akka Actors和play框架中使用WebService

时间:2016-10-02 08:14:52

标签: scala playframework dependency-injection akka guice-3

我使用Play& amp ;; Akka现在需要集成另一个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服务。此Web服务是一个将记录此actor的结果的数据库。

我已经看到并遵循了(其中包括)

的指示
  1. https://playframework.com/documentation/2.5.x/ScalaWS
  2. Dependency injection with abstract class and object in Play Framework 2.5
  3. 根据上面的说明,我应该将WSClient注入到需要访问它的类中。

    我能够将依赖注入解决到第二个控制器,如下所示

    class DbController @Inject() (ws: WSClient) extends Controller {  
      def post = Action { 
             // access webservice
      }
    }
    

    这是可行的,我可以通过访问路由文件中映射到的URL来执行“post”操作,从而访问Web服务。我现在也有两个控制器。

    我的问题是从ActorMgr(Akka Actor)访问Web服务控制器“post”方法。我如何启用它?

1 个答案:

答案 0 :(得分:0)

经过大量研究后,我想在此更新我的发现。虽然我能够解决下面我的具体问题,但这里还有很多内容可以说。

我的具体解决方案 -

而不是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")     
    }
}

话虽如此,注射东西给了我很多问题。我终于意识到这里有一些不同的用例 -

  1. 使用Akka& Guice并且不使用Playframework
  2. 使用Playframework + Akka + Guice并注入顶级演员
  3. 使用Playframework + Akka + Guice并注入儿童演员
  4. 使用playframework + Akka + Guice但是不能创建"注入"你的顶级演员&演员系统。
  5. 以下是解决上述问题的方法。

    1. 对于(1) - 请参阅guice akka tutorial
    2. For(2)& (3) - 参考Playframework Documentation
    3. For(4)这有点棘手
    4. 您需要扩展" IndirectActorProducer"然后使用它来创建ActorRef。问题是"道具"不知道如何与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"