Использование веб-сервиса с 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
}
Моя проблема в том, что мне теперь нужно вызвать новый веб-сервис от этого актера. Этот веб-сервис представляет собой базу данных, которая будет регистрировать результаты этого актера.
Я видел и следовал инструкциям (среди прочих)
- https://playframework.com/documentation/2.5.x/ScalaWS
- Внедрение зависимостей с абстрактным классом и объектом в 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")
}
}
Сказав это, инъекция дала мне массу проблем. Я наконец понял, что здесь есть несколько различных вариантов использования -
- Использование Akka & Guice и не использование Playframework
- Использование Playframework + Akka + Guice и введение актера высшего уровня
- Использование Playframework + Akka + Guice и инъекция детских актеров
- Использование playframework + Akka + Guice, НО создает не "инъекцию" вашего актера высшего уровня и системы актеров.
Вот как бы вы решили все вышеперечисленное.
- Для (1) - Обратитесь к обучающей программе guice akka
- Для (2) и (3) - см. Документацию Playframework
- Для (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"