Akka TCP Comand Failed - scala

I'm writing a client and server applications using Akka Tcp and I'm having a issue with a high throughput.
When I write too many messages on the client side, I'm having too many CommandFailed messages and I can't figure out why...
Here's my server:
class Server(listener: ActorRef) extends Actor {
import Tcp._
import context.system
IO(Tcp) ! Bind(self, new InetSocketAddress("localhost", 9090))
def receive = {
case CommandFailed(_: Bind) => {
println("command failed error")
context stop self
}
case c#Tcp.Connected(remote, local) =>
listener ! GatlingConnected(c.remoteAddress.toString)
println("Connected: " + c.remoteAddress)
val handler = context.actorOf(Props(classOf[ServerHandler], listener, c.remoteAddress.toString))
val connection = sender
connection ! Register(handler)
}
}
class ServerHandler(listener: ActorRef, remote: String) extends Actor {
import Tcp._
override def receive: Receive = {
case Received(data) => listener ! data.utf8String
case PeerClosed => {
listener ! Finished(remote)
context stop self
}
}
}
Message and Finished are just case classes that I've createad.
Here's the client (where I think its the source of this problem):
private class TCPMessageSender(listener: ActorRef) extends BaseActor {
final val MESSAGE_DELIMITER = "\n"
val buffer = new ListBuffer[Any]
val failedMessages = new ListBuffer[Write]
IO(Tcp) ! Connect(new InetSocketAddress(configuration.data.tcp.host, configuration.data.tcp.port))
override def receive = {
case msg # (_: UserMessage | _: GroupMessage | _: RequestMessage) =>
logger.warn(s"Received message ($msg) before connected. Buffering...")
buffer += msg
case CommandFailed(_: Connect) =>
logger.warn("Can't connect. All messages will be ignored")
listener ! Terminate
context stop self
case c # Connected(remote, local) =>
logger.info("Connected to " + c.remoteAddress)
val connection = sender
connection ! Register(self)
logger.info("Sending previous received messages: " + buffer.size)
buffer.foreach(msg => {
val msgString: String = JsonHelper.toJson(Map[String, Any]("message_type" -> msg.getClass.getSimpleName, "message" -> msg))
connection ! Write(ByteString(msgString + MESSAGE_DELIMITER))
})
buffer.clear
logger.info("Sent")
context become {
case msg # (_: UserMessage | _: GroupMessage | _: RequestMessage) =>
val msgString: String = JsonHelper.toJson(Map[String, Any]("message_type" -> msg.getClass.getSimpleName, "message" -> msg))
logger.trace(s"Sending message: $msgString")
connection ! Write(ByteString(msgString + MESSAGE_DELIMITER))
case CommandFailed(w: Write) =>
logger.error("Command failed. Buffering message...")
failedMessages += w
connection ! ResumeWriting
case CommandFailed(c) => logger.error(s"Command $c failed. I don't know what to do...")
case Received(data) =>
logger.warn(s"I am not supposed to receive this data: $data")
case "close" =>
connection ! Close
case _: ConnectionClosed =>
logger.info("Connection closed")
context stop self
case WritingResumed => {
logger.info("Sending failed messages")
failedMessages.foreach(write => connection ! write)
failedMessages.clear
}
}
}
}
Sometimes I receive a lot of CommandFailed messages and I call ResumeWrite and never receive a WritingResumed (and the connection never closes in this cases). Am I doing something wrong?

I think the problem is that when you register your actor sending the Register message, you also have to set the useResumeWriting parameter to true:
connection ! Register(handler, false, true)
The doc of resumeWriting command states:
When `useResumeWriting` is in effect as was indicated in the [[Tcp.Register]] message
then this command needs to be sent to the connection actor in order to re-enable
writing after a [[Tcp.CommandFailed]] event. All [[Tcp.WriteCommand]] processed by the
connection actor between the first [[Tcp.CommandFailed]] and subsequent reception of
this message will also be rejected with [[Tcp.CommandFailed]].

Related

Can you refresh or redisplay unflushed telnet input?

I'm writing an old school text based telnet server that right now is a glorified chat room in Scala with Akka actor based IO. What is happening is that client the client will start to type something and then an event will happen and when it gets written, it wipes out anything that has already been typed. In the following example, Tom has started to type "say How are you?" but Fred arrives after he has only typed "say How ar" and this input is wiped out:
Tom > say How ar
Fred has arrived.
Tom >
Is there any way to get telnet to redisplay it's output buffer it hasn't flushed yet?
Here is the server:
class TcpServer(port: Int) extends Actor {
import TcpServer._
import context.system
val log = Logging(system, this)
var connectionNum: Int = 1
log.info(STARTING_SERVER)
IO(Tcp) ! Bind(self, new InetSocketAddress("0.0.0.0", port))
def receive = {
case b # Bound(localAddress) =>
log.info(PORT_BOUND, localAddress)
case c # Connected(remote, local) =>
log.info(CONNECTION_ACCEPTED)
context.actorOf(ConnectionHandler.connectionProps(sender()), s"conn$connectionNum")
connectionNum += 1
case CommandFailed(_: Bind) =>
log.error(BINDING_FAILED)
context stop self
}
}
Here is the ConnectionHandler, it's companion object, and the message case classes it uses:
class ConnectionHandler(connection: ActorRef) extends Actor {
import context._
val log = Logging(context.system, this)
connection ! Register(self)
var delegate = actorOf(Props[LoginHandler], "login")
watch(delegate)
def receive = {
case Received(data) =>
val dataString = data.utf8String.trim
log.info("Received data from connection: {}", dataString)
delegate ! Input(dataString)
case Output(content) =>
connection ! Write(ByteString(content))
case LoggedIn(user) =>
unwatch(delegate)
delegate ! PoisonPill
delegate = actorOf(UserHandler.connectionProps(user), user.name.toLowerCase)
watch(delegate)
case Terminated =>
log.warning("User delegate died unexpectedly.")
connection ! ConfirmedClose
case CloseConnection(message) =>
connection ! Write(ByteString(message + "\n"))
connection ! ConfirmedClose
log.info("User quit.")
case ConfirmedClosed =>
log.info("Connection closed.")
stop(self)
case PeerClosed =>
log.info("Connection closed by client.")
stop(self)
}
}
object ConnectionHandler {
def connectionProps(connection: ActorRef): Props = Props(new ConnectionHandler(connection))
}
case class Input(input: String)
case class Output(output: String)
case class LoggedIn(user: User)
case class CloseConnection(message: String)
Okay, after finally phrasing my google queries correctly, I found what I needed here:
Force telnet client into character mode
The basic solution is that I forced the client into character at a time mode and echo'd back the characters I care about. The bonus to this is that now I can do tab completion, command history, and make the passwords not show up.
Here is the relevant code snippet:
val controlString = ByteString('\u00ff','\u00fb','\u0001','\u00ff','\u00fb','\u0003','\u00ff','\u00fc','\u0022')
connection ! Write(controlString)

Why does connection close automatically after 1 minute using Akka TCP?

I'm writing an Interactive Broker API using Scala and Akka actors.
I have a Client actor that connects to the server and communicate with the IO manager to send requests and receive responses from TWS. The connection works fine and I'm able to send a request and get the response.
Then I receive automatically a PeerClosed message from the IO manager after 1 minute. I would like that the connection stays open unless I explicitly close it. I tried to set keepOpenOnPeerClosed = true but it changes nothing.
Here is the Actor:
class Client(remote: InetSocketAddress, clientId: Int, extraAuth: Boolean, onConnected: Session => Unit, listener: EWrapper) extends Actor {
final val ClientVersion: Int = 63
final val ServerVersion: Int = 38
final val MinServerVerLinking: Int = 70
import Tcp._
import context.system
IO(Tcp) ! Connect(remote)
def receive = {
case CommandFailed(_: Connect) =>
print("connect failed")
context stop self
case c#Connected(remote, local) => {
val connection = sender()
connection ! Register(self, keepOpenOnPeerClosed = true)
context become connected(connection,1)
val clientVersionBytes = ByteString.fromArray(String.valueOf(ClientVersion).getBytes() ++ Array[Byte](0.toByte))
println("Sending Client Version " + clientVersionBytes)
sender() ! Write(clientVersionBytes)
}
}
def connected(connection: ActorRef, serverVersion: Int): Receive = {
case request: Request =>
print("Send request " + request)
connection ! Write(ByteString(request.toBytes(serverVersion)))
case CommandFailed(w: Write) =>
connection ! Close
print("write failed")
case Received(data) => {
println(data)
implicit val is = new DataInputStream(new ByteArrayInputStream(data.toArray))
EventDispatcher.consumers.get(readInt()) match {
case Some(consumer) => {
consumer.consume(listener, serverVersion)
}
case None => {
listener.error(EClientErrors.NoValidId, EClientErrors.UnknownId.code, EClientErrors.UnknownId.msg)
}
}
}
case _ : ConnectionClosed => context stop self
}
I don't have the same behaviour if I connect using IBJts API (using a standard Java Socket)
Have you tried it with the keep alive option?
sender ! Tcp.SO.KeepAlive(on = true)

Akka scala-io ask and wait for respone

I'm using scala-io in my akka actors, in my case I need to send request and wait for response, in official docs (http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html) I can see the answer is asynchronous.
How can I wait for response, can I use somehow ? (ask) pattern
class SocketClient(remoteAddress: InetSocketAddress, listener: ActorRef) extends Actor {
import Tcp._
import context.system
IO(Tcp) ! Connect(remoteAddress)
def receive = {
case CommandFailed(_: Connect) =>
listener ! ConnectFailure
context stop self
case Connected(remote, local) =>
listener ! ConnectSuccess
val connection = sender
connection ! Register(self)
context become {
case data: ByteString =>
connection ! Write(data)
case CommandFailed(w: Write) =>
Logger.error(s"Error during writing")
case Received(data) =>
listener ! data
case Disconnect =>
connection ! Close
case _: ConnectionClosed =>
Logger.error(s"Connection has been closed ${remoteAddress.getAddress}")
context stop self
}
}
}
Can I use something like:
connection ? Write(data)
Yes, but you should take in account the fact that ask-pattern allows you to receive only first reply from actor.
In your case it's connection, which may reply some additional or even unrelated objects (it depends on back-pressure/acknowledgement mode you choose. For example, if you use Write - you may receive the written (to the socket) object's acknowledge instead of response.
You can avoid it by:
using NoAck as an AckEvent (see http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html, Throttling Reads and Writes section).
use atomic requests/replies (no multi-part)
use one actor per protocol (per each "ping-pong" sequence)
In other words, ask pattern just creates own internal actor per message and make it a sender, so all replies (for this particular message) are going to this micro-actor. When it receives first reply - future (returned by ?) becomes completed - and internal actor destroyed (so other replies will be ignored).
Also, connection automatically replies to registered (by Register message) listener instead of sender - so you should create mediate actor:
class Asker(connection: ActorRef) extends Actor {
import Tcp._
connection ! Register(self);
def receive = {
case x =>
val parent = sender()
connection ! x
context become {case x => parent ! x; context.unbecome()}
}
}
trait TcpAskSupport {
self: Actor =>
def asker(connection: ActorRef) =
context.child(connection.path.name + "Asker")
.getOrElse(system.actorOf(Props(classOf[Asker], connection),
connection.path.name + "Asker"))
}
Usage example:
class Client extends Actor with TcpAskSupport {
import Tcp._
import context.system
IO(Tcp) ! Connect(new InetSocketAddress("61.91.16.168", 80))
implicit val timeout = Timeout(new FiniteDuration(5, SECONDS))
def receive = {
case CommandFailed(_: Connect) =>
println("connect failed")
context stop self
case c # Connected(remote, local) =>
println("Connected" + c)
val connection = sender()
asker(connection) ? Write(ByteString("GET /\n", "UTF-8"), NoAck) onComplete {
case x => println("Response" + x)
}
case x => println("[ERROR] Received" + x)
}
}

Scala-redis subscribes to * but receives zero messages

Integrating redis with my Scala application using Akka but for some reason it does not receive any messages. I can confirm that redis does have a ton of traffic on its side by opening the redis-cli on the command line.
After a pSubscribe it receives: subscribed to * and count = 1
My guess is that it might be related to the way Akka is set up to receive callbacks. I had to strip out Scala Actors in the scala-redis lib and replace them with Akka actors due to some conflicts.
Here's the code:
The Subscriber Actor
class Subscriber(client: RedisClient) extends Actor {
var callback: PubSubMessage => Any = { m => }
def receive: Receive = {
case Subscribe(channels) =>
client.subscribe(channels.head, channels.tail: _*)(callback)
case pSubscribe(channels) =>
client.pSubscribe(channels.head, channels.tail: _*)(callback)
case pSubscribeAll(channels) =>
Logger.info("Subscribing to all channels")
client.pSubscribe(channels.head, channels.tail: _*)(callback)
case Register(cb) =>
Logger.info("Callback is registered")
callback = cb
case Unsubscribe(channels) =>
client.unsubscribe(channels.head, channels.tail: _*)
case UnsubscribeAll =>
client.unsubscribe
}
}
Initializing the Subscriber
class RelaySub extends Actor {
// important config values
val system = ActorSystem("pubsub")
val conf = play.api.Play.current.configuration
val relayPubHost = conf.getString("relays.redis.host").get
val relayPubPort = conf.getInt("relays.redis.port").get
val rs = new RedisClient(relayPubHost, relayPubPort)
val s = system.actorOf(Props(new Subscriber(rs)))
s ! Register(callback)
s ! pSubscribeAll(Array("*"))
Logger.info("Engine Relay Subscriber has started up")
def receive: Receive = {
case Register(callback) =>
}
def callback(pubsub: PubSubMessage) = pubsub match {
case S(channel, no) => Logger.info("subscribed to " + channel + " and count = " + no)
case U(channel, no) => Logger.info("unsubscribed from " + channel + " and count = " + no)
case M(channel, msg) =>
msg match {
// exit will unsubscribe from all channels and stop subscription service
case "exit" =>
Logger.info("unsubscribe all ... no handler yet ;)")
// message "+x" will subscribe to channel x
case x if x startsWith "+" =>
Logger.info("subscribe to ... no handler yet ;)")
// message "-x" will unsubscribe from channel x
case x if x startsWith "-" =>
Logger.info("unsubscribe from ... no handler yet ;)")
// other message receive
case x =>
Logger.info("Engine: received redis message")
val channelVars = channel.split(".").toArray[String]
if(channelVars(0)!=Engine.instanceID)
channelVars(1) match {
case "relay" =>
EngineSyncLocal.constructRelay(channel, msg)
case _ =>
Logger.error("Engine: received unknown redis message")
}
}
}
}
Thanks for your help!
I found the problem. It appears to be a bug in the scala-redis client.
I added some logging in the consumer class and began receiving Engine: weird message errors which means that it doesn't recognize the incoming traffic. I'll contact the author and put in a pull request.
The code:
class Consumer(fn: PubSubMessage => Any) extends Runnable {
def start () {
val myThread = new Thread(this) ;
myThread.start() ;
}
def run {
whileTrue {
asList match {
case Some(Some(msgType) :: Some(channel) :: Some(data) :: Nil) =>
Logger.info("Engine: redis traffic")
msgType match {
case "subscribe" | "psubscribe" => fn(S(channel, data.toInt))
case "unsubscribe" if (data.toInt == 0) =>
fn(U(channel, data.toInt))
break
case "punsubscribe" if (data.toInt == 0) =>
fn(U(channel, data.toInt))
break
case "unsubscribe" | "punsubscribe" =>
fn(U(channel, data.toInt))
case "message" | "pmessage" =>
fn(M(channel, data))
case x => throw new RuntimeException("unhandled message: " + x)
}
case _ => Logger.error("Engine: weird redis message")
}
}
}
}
case x => throw new RuntimeException("unhandled message: " + x)
}
case Some(Some("pmessage")::Some(pattern)::Some(channel):: Some(message)::Nil)=>
fn(M(channel, message))
asList match is missing a case

How do I disconnect a Scala Remote Actor?

In scala it is very easy to make a connection to a remote actor, but the documentation does not tell me anything about disconnecting. Simply throwing away the reference does not work, because remote actors are actors, so these won't be collected until stopped. So how do I disconnect?
This does not Terminate after exit:
import actors.{DaemonActor,remote}
import remote.{RemoteActor,Node}
object SimpleClient{
val messageHandler = new DaemonActor{
def act{
loop{
react{
case message:String =>
println("got message: " + message)
case _ =>
}
}
}
start
}
def main(args:Array[String]){
val server = RemoteActor.select(Node("localhost",9999),'server)
server.send('Connect,messageHandler)
var exit = false
while(!exit){
val message = Console.readLine
if(message == "exit" || message == "quit") {
exit = true
server ! 'Disconnect
}
else
server ! message
}
}
}
This is the Server:
import actors.{Actor,OutputChannel}
import actors.remote.RemoteActor
object Server extends Actor{
val clients = new collection.mutable.HashSet[OutputChannel[Any]]
def act{
loop{
react{
case 'Connect =>
clients += sender
case 'Disconnect =>
clients -= sender
case message:String =>
for(client <- clients)
client ! message
}
}
}
def main(args:Array[String]){
start
RemoteActor.alive(9999)
RemoteActor.register('server,this)
}
}
[Disclaimer: I'm PO of Akka]
May I suggest taking a look at Akka, which was built with Remote Actors in mind from day 1?
www.akka.io
Your question is not really clear enough about what problem you think you are experiencing. Actors do not "connect" to each other (like a socket). You send an actor a message because you have a reference to it (or a proxy, in the case of remote actors).
Having such a reference does not prevent the actor (either actor) from shutting down. If there are no longer any references to an actor and it is not running, there is nothing to stop it being garbage-collected
The Reactor trait defines protected[actors] def exit(): Nothing which the actor can call itself upon reception of a message telling it to do so.
sealed trait Msg
case object Apoptosis extends Msg
// ... more messages
class RRActor extends Reactor[Msg] {
def act = loop {
react {
// ... Whatever messages the actor handles
case Apoptosis => this.exit
}
}
}
Edit: I have not tested this ever with remote actors.
Here's a working version of your source, with pertinent changes commented inline:
import actors.{DaemonActor,remote}
import remote.{RemoteActor,Node}
case class Send(message: String)
case object Disconnect
object SimpleClient{
val messageHandler = new DaemonActor{
def act{
// keep the reference to the proxy inside the client-side actor
val server = RemoteActor.select(Node("localhost",9999),'server)
server ! 'Connect
loop{
react{
case message:String =>
println("got message: " + message)
case Send(message) => server ! message
case Disconnect => {
// disconnect and exit the client-side actor
server ! 'Disconnect //'
exit
}
case _ =>
}
}
}
start
}
def main(args:Array[String]){
var exit = false
while(!exit){
val message = Console.readLine
if(message == "exit" || message == "quit") {
exit = true
// tell the client-side actor to exit
messageHandler ! Disconnect
} else {
messageHandler ! Send(message)
}
}
}
}