Использование веб-сервиса с Akka Actors и игровой платформой

Я создал веб-сервис, используя Play & Akka, и теперь мне нужно интегрировать другой веб-сервис, где мой веб-сервис является клиентом.

Мой контроллер по умолчанию (со связанным файлом маршрутов) выглядит так

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
}

Моя проблема в том, что мне теперь нужно вызвать новый веб-сервис от этого актера. Этот веб-сервис представляет собой базу данных, которая будет регистрировать результаты этого актера.

Я видел и следовал инструкциям (среди прочих)

  1. https://playframework.com/documentation/2.5.x/ScalaWS
  2. Внедрение зависимостей с абстрактным классом и объектом в Play Framework 2.5

Согласно инструкциям выше, я должен внедрить WSClient в класс, где мне нужно получить к нему доступ.

Я могу решить инъекцию зависимостей во второй контроллер, как показано ниже

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

Это работает, и я могу выполнить действие "post", получив доступ к URL-адресу, которому он сопоставлен в файле маршрутов, и, следовательно, получить доступ к веб-службе. У меня тоже сейчас есть два контроллера.

Моя проблема заключается в доступе к методу "post" контроллера веб-службы из ActorMgr (Akka Actor). Как мне это включить?

1 ответ

После долгих исследований я хотел обновить свои выводы здесь. Хотя я смог решить свою конкретную проблему, как показано ниже, здесь можно сказать гораздо больше.

Мое конкретное решение сначала -

Вместо 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, НО создает не "инъекцию" вашего актера высшего уровня и системы актеров.

Вот как бы вы решили все вышеперечисленное.

  1. Для (1) - Обратитесь к обучающей программе guice akka
  2. Для (2) и (3) - см. Документацию Playframework
  3. Для (4) это немного сложнее

Вам нужно будет расширить "IndirectActorProducer", а затем использовать его для создания вашего ActorRef. Проблема в том, что "реквизит" не умеет взаимодействовать с Guice. Это также является частью решения в (1)

Пример кода ниже показывает все 4 варианта использования и компилирует. В коде ниже

ParentActor - Указывает на использование case (2) выше, ChildActor на использование case (3) и ParentActor_2 & ChildActor_2 на использование case (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"
Другие вопросы по тегам