我试图找出使用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")
条消息,但没有任何反应,连接关闭。有没有人对如何正确设置这个有任何想法?谢谢!
答案 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。