Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala tcp上的akka流_Scala_Tcp_Akka_Akka Stream - Fatal编程技术网

Scala tcp上的akka流

Scala tcp上的akka流,scala,tcp,akka,akka-stream,Scala,Tcp,Akka,Akka Stream,设置如下:我希望能够通过tcp连接将消息(JSON转换为bytestrings)从发布服务器流式传输到远程服务器订阅服务器。 理想情况下,发布者将是一个参与者,它将接收内部消息,对它们进行排队,然后将它们流式传输到订户服务器(当然,如果有未解决的需求)。我知道这需要扩展ActorPublisher类,以便在需要时onNext()消息。 我的问题是,到目前为止,我只能发送(正确接收和解码)一次性消息到每次打开新连接的服务器。我没有设法绕过akka文档,并能够使用ActorPublisher设置正确

设置如下:我希望能够通过tcp连接将消息(JSON转换为bytestrings)从发布服务器流式传输到远程服务器订阅服务器。
理想情况下,发布者将是一个参与者,它将接收内部消息,对它们进行排队,然后将它们流式传输到订户服务器(当然,如果有未解决的需求)。我知道这需要扩展
ActorPublisher
类,以便在需要时
onNext()
消息。
我的问题是,到目前为止,我只能发送(正确接收和解码)一次性消息到每次打开新连接的服务器。我没有设法绕过akka文档,并能够使用
ActorPublisher
设置正确的tcp
Flow
以下是发布者提供的代码:

def send(message: Message): Unit = {
    val system = Akka.system()
    implicit val sys = system

    import system.dispatcher

    implicit val materializer = ActorMaterializer()

    val address =     Play.current.configuration.getString("eventservice.location").getOrElse("localhost")
    val port = Play.current.configuration.getInt("eventservice.port").getOrElse(9000)

    /*** Try with actorPublisher ***/
    //val result = Source.actorPublisher[Message]    (Props[EventActor]).via(Flow[Message].map(Json.toJson(_).toString.map(ByteString(_))))

    /*** Try with actorRef ***/
    /*val source = Source.actorRef[Message](0, OverflowStrategy.fail).map(
  m => {
    Logger.info(s"Sending message: ${m.toString}")
    ByteString(Json.toJson(m).toString)
  }
)
    val ref = Flow[ByteString].via(Tcp().outgoingConnection(address, port)).to(Sink.ignore).runWith(source)*/

    val result = Source(Json.toJson(message).toString.map(ByteString(_))).
  via(Tcp().outgoingConnection(address, port)).
  runFold(ByteString.empty) { (acc, in) ⇒ acc ++ in }//Handle the future
}
最后,演员的代码非常标准:

import akka.actor.Actor
import akka.stream.actor.ActorSubscriberMessage.{OnComplete, OnError}
import akka.stream.actor.{ActorPublisherMessage, ActorPublisher}

import models.events.Message

import play.api.Logger

import scala.collection.mutable

class EventActor extends Actor with ActorPublisher[Message] {
   import ActorPublisherMessage._
   var queue: mutable.Queue[Message] = mutable.Queue.empty

   def receive = {
      case m: Message =>
         Logger.info(s"EventActor - message received and queued: ${m.toString}")
         queue.enqueue(m)
         publish()

      case Request => publish()

      case Cancel =>
          Logger.info("EventActor - cancel message received")
          context.stop(self)

      case OnError(err: Exception) =>
          Logger.info("EventActor - error message received")
          onError(err)
          context.stop(self)

      case OnComplete =>
          Logger.info("EventActor - onComplete message received")
          onComplete()
          context.stop(self)
   }

    def publish() = {
     while (queue.nonEmpty && isActive && totalDemand > 0) {
     Logger.info("EventActor - message published")
     onNext(queue.dequeue())
   }
 }
如有必要,我可以从订户处提供代码:

def connect(system: ActorSystem, address: String, port: Int): Unit = {
implicit val sys = system
import system.dispatcher
implicit val materializer = ActorMaterializer()

val handler = Sink.foreach[Tcp.IncomingConnection] { conn =>
  Logger.info("Event server connected to: " + conn.remoteAddress)
  // Get the ByteString flow and reconstruct the msg for handling and then output it back
  // that is how handleWith work apparently
  conn.handleWith(
    Flow[ByteString].fold(ByteString.empty)((acc, b) => acc ++ b).
      map(b => handleIncomingMessages(system, b.utf8String)).
      map(ByteString(_))
  )
}

val connections = Tcp().bind(address, port)
val binding = connections.to(handler).run()

binding.onComplete {
  case Success(b) =>
    Logger.info("Event server started, listening on: " + b.localAddress)
  case Failure(e) =>
    Logger.info(s"Event server could not bind to $address:$port: ${e.getMessage}")
    system.terminate()
}
}

提前感谢您的提示。

我的第一个建议是不要编写自己的队列逻辑。Akka提供了开箱即用的服务。您也不需要编写自己的演员,Akka Streams也可以提供

首先,我们可以创建一个流,通过Tcp将您的发布服务器连接到您的订阅服务器。在publisher代码中,您只需创建一次ActorSystem,并连接到外部服务器一次:

//this code is at top level of your application

implicit val actorSystem = ActorSystem()
implicit val actorMaterializer = ActorMaterializer()
import actorSystem.dispatcher

val host = Play.current.configuration.getString("eventservice.location").getOrElse("localhost")
val port    = Play.current.configuration.getInt("eventservice.port").getOrElse(9000)

val publishFlow = Tcp().outgoingConnection(host, port)
publishFlow
是一个将输入要发送到外部订阅者的
ByteString
数据并输出来自订阅者的ByteString数据的程序:

//  data to subscriber ----> publishFlow ----> data returned from subscriber
下一步是发布源。您可以使用将流写入
ActorRef
,而不是编写自己的Actor。基本上,流将成为ActorRef供我们以后使用:

//these values control the buffer
val bufferSize = 1024
val overflowStrategy = akka.stream.OverflowStrategy.dropHead

val messageSource = Source.actorRef[Message](bufferSize, overflowStrategy)
我们还需要一个流来将消息转换为ByteString

val marshalFlow = 
  Flow[Message].map(message => ByteString(Json.toJson(message).toString))
最后,我们可以连接所有的部件。由于您没有从外部订户接收到任何数据,我们将忽略从连接传入的任何数据:

val subscriberRef : ActorRef = messageSource.via(marshalFlow)
                                            .via(publishFlow)
                                            .runWith(Sink.ignore)     
现在,我们可以将此流视为参与者:

val message1 : Message = ???

subscriberRef ! message1

val message2 : Message = ???

subscriberRef ! message2

使用Akka中指定的双向流,并特别使用其tcp示例。