如何重用Akka TCP连接来写消息?

时间:2017-09-01 02:31:56

标签: scala tcp akka

我试图找出使用Akka TCP模块的一些基本功能 - 我也是Akka的新手。我想创建一个TCP客户端actor,然后发送该actor消息以将数据写入TCP通道。

TCP客户端的实现(非常类似于TCP客户端示例 - http://doc.akka.io/docs/akka/current/scala/io-tcp.html#connecting

import java.net.InetSocketAddress
import akka.actor.{Actor, Props}
import akka.io.{IO, Tcp}
import akka.util.ByteString
import com.hca.cdm.log.Logg
import com.hca.cdm.tcp.AkkaTcpClient.{Ping, SendMessage}

object AkkaTcpClient {
  var prop : Props = _
  def props(host :String,port :Int) = {
    if(prop == null) prop = Props(classOf[AkkaTcpClient], new 
InetSocketAddress(host,port))
    prop
  }

  final case class SendMessage(message: ByteString)
  final case class Ping(message: String)
}

class AkkaTcpClient(remote: InetSocketAddress) extends Actor with Logg {
  import akka.io.Tcp._
  import context.system

  info("Connecting to " +  remote.toString)

  val manager = IO(Tcp)
  manager ! Connect(remote)

  override def receive: Receive = {
    case CommandFailed(con: Connect) =>
      error("Connection failed")
      error(con.failureMessage.toString)
      context stop self

    case c @ Connected(remote, local) =>
      info(s"Connection to $remote succeeded")
      val connection = sender
      connection ! Register(self)

      context become {
        case SendMessage(message) =>
          info("Sending message: " + message.utf8String)
          connection ! Write(message)
        case Ping(message) =>
          info("hello: " + message)
        case data: ByteString =>
          info("Sending request data: " + data.utf8String)
          connection ! Write(data)
        case CommandFailed(w: Write) =>
          error("Failed to write request.")
          error(w.failureMessage.toString)
        case Received(data) =>
          info("Received response.")
          info("data: " + data.utf8String)
        case "close" =>
          info("Closing connection")
          connection ! Close
        case _: ConnectionClosed =>
          info("Connection closed by server.")
          context stop self
       }

    case e: Exception =>
      error(e.printStackTrace().toString)

    case t: Throwable =>
      error(t)

    case _ =>
      error("Something else is happening")

  }
}

在应用程序中使用actor的示例

val actorSys = ActorSystem.create("MyActorSys")
val tcpActor = actorSys.actorOf(AkkaTcpClient.props(addr, port), "tcpActor")
tcpActor ! SendMessage(ByteString("hello"))

这不能将消息写入TCP通道。我在日志中看到info(s"Connection to $remote succeeded")条消息,但没有任何反应,连接关闭。有没有人对如何正确设置这个有任何想法?谢谢!

1 个答案:

答案 0 :(得分:0)

使用host作为127.0.0.1运行代码会产生以下输出(我用println语句替换了您的日志记录):

Connecting to /127.0.0.1:19002
Something else is happening
Connection to /127.0.0.1:19002 succeeded

上述输出表明tcpActor在建立连接之前正在处理SendMessage(ByteString("hello"))消息。因此,我在发送消息之前添加了暂停(以及第二条消息):

val tcpActor = actorSys.actorOf(AkkaTcpClient.props(addr, port), "tcpActor")
Thread.sleep(1000)
tcpActor ! SendMessage(ByteString("hello"))
tcpActor ! SendMessage(ByteString("hello again"))

输出:

Connecting to /127.0.0.1:19002
Connection to /127.0.0.1:19002 succeeded
Sending message: hello
Sending message: hello again

在另一个终端中,netcat正在侦听端口:

> nc -l 19002
hellohello again

如您所见,消息按预期写入连接。通过微小的调整,您的设置可以正常工作。

虽然您的整体方法是合理的,但还有一些问题:

  • props方法不必要地使用var。移除var并将props简化为以下内容:

    def props(host: String, port: Int) =
      Props(classOf[AkkaTcpClient], new InetSocketAddress(host, port))
    
  • 我不认为您的case e: Exception =>case t: Throwable =>分支符合您的认为。它们不处理actor处理消息时发生的异常和错误;它们处理发送给actor的Exception类型和Throwable类型的消息。也就是说,如果有人执行以下诡计,他们会发挥作用:

    tcpActor ! new RuntimeException("I should not be doing this")
    tcpActor ! new Error("This is Bad")
    

    删除这两个分支。处理actor中异常的常用方法是定义supervisor strategy