I'm trying to create simple tcp server with help of akka.
My main
import java.net.InetSocketAddress
import akka.actor._
import akka.io._
import Tcp._
object Main extends App {
implicit val actorSystem: ActorSystem = ActorSystem()
val serverRef = actorSystem.actorOf(server.Server.props)
val tcpManager = IO(Tcp)
tcpManager ! Bind(serverRef, new InetSocketAddress("localhost", 8099))
}
My server actor:
package server
import akka.actor._
import akka.io.Tcp
import Tcp._
object Server {
def props = Props(new Server())
class SimplisticHandler extends Actor {
def receive = {
case Received(data) => sender() ! Write(data)
case PeerClosed => context.stop(self)
}
}
}
class Server extends Actor {
override def receive: Receive = {
case b # Bound(localAddress) =>
println(s"connected to ${localAddress.toString}")
context.parent ! b
case CommandFailed(_: Bind) => context.stop(self)
case c # Connected(remote, local) =>
val handler = context.actorOf(Server.props)
val connection = sender()
connection ! Register(handler)
case msg =>
println("Unknown message")
}
}
But when i run it i get dead letter log event:
table [INFO] [11/08/2019 18:34:11.558]
[default-akka.actor.default-dispatcher-6] [akka://default/deadLetters]
Message [akka.io.Tcp$Bound] from
Actor[akka://default/system/IO-TCP/selectors/$a/0#-176629096] to
Actor[akka://default/deadLetters] was not delivered. [1] dead letters
encountered. If this is not an expected behavior then
Actor[akka://default/deadLetters] may have terminated unexpectedly.
This logging can be turned off or adjusted with configuration settings
'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
The Bind message should be sent from an actor. That actor will eventually receive Bound message from TCP manager (or CommandFailed). In this case, Server actor should send Bind message. For instance:
class Server extends Actor {
IO(Tcp) ! Bind(self, new InetSocketAddress("localhost", 8099)) //<--------
override def receive: Receive = {
case b # Bound(localAddress) =>
println(s"connected to ${localAddress.toString}")
context.parent ! b
case CommandFailed(_: Bind) => context.stop(self)
case c # Connected(remote, local) =>
val handler = context.actorOf(Server.props)
val connection = sender()
connection ! Register(handler)
case msg =>
println("Unknown message")
}
}
Related
I am using an actor as a Sink as the following:
import akka.Done
import akka.actor.{Actor, ActorLogging, Props}
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest}
import akka.stream.{ActorMaterializer, Materializer}
import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import scala.concurrent._
import scala.util.{Failure, Success}
object WsActor {
def props: Props = Props(new WsActor)
}
final class WsActor extends Actor with ActorLogging {
import com.sweetsoft.WsConnector._
implicit val materializer: Materializer = ActorMaterializer()
implicit val ec: ExecutionContextExecutor = context.system.dispatcher
implicit val actor = context.system
// Future[Done] is the materialized value of Sink.foreach,
// emitted when the stream completes
private val incoming: Sink[Message, Future[Done]] =
Sink.foreach[Message] {
case message: TextMessage.Strict =>
println(message.text)
case _ =>
println("Unknown messages.")
}
//private val outgoing: Source[Message, Promise[Option[Message]]] =
// Source.maybe[Message]
log.info("Websocket actor started.")
override def receive: Receive = {
case Initialized =>
log.info("Initialization to receive messages via stream.")
sender() ! Ack
case Completed =>
log.info("Streams completed.")
sender() ! Ack
case Msg(value) =>
// the Future[Done] is the materialized value of Sink.foreach
// and it is completed when the stream completes
val flow: Flow[Message, Message, Future[Done]] =
Flow.fromSinkAndSourceMat(incoming, Source.single(TextMessage(value)))(Keep.left)
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
// completes or fails when the connection succeeds or fails
// and closed is a Future[Done] representing the stream completion from above
val (upgradeResponse, _) =
Http().singleWebSocketRequest(WebSocketRequest("ws://echo.websocket.org"), flow)
upgradeResponse.flatMap { upgrade =>
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
Future.successful(Done)
} else {
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
}
}.andThen {
case Success(_) =>
log.info("Sending ACK")
sender() ! Ack
}.onComplete {
case Success(_) =>
log.info("Success proceed")
case Failure(ex) => log.error(ex.getMessage)
}
//sender() ! Ack
case Failed(ex) =>
log.info(s"Stream failed with ${ex.getMessage}.")
}
}
The actor consumes the messages and redirect further to websocket server.
Somewhere in the code, I send an Ack to the Sender actor to signalize, that it ready to receive further messages. But the sender actor never receive the Ack message.
If I put sender() ! Ack the FUTURE chain, then it works as expected.
Is it possible to put sender() ! Ack within FUTURE` chain.
Try remembering the sender on message receive and then using it later in the code instead of sender() instead of calling sender() downstream, this that value might not be constant during processing (e.g when receiving other messages in the meantime when ur current task does not block processing of the message queue due to futures )
case Msg(value) =>
val sender = sender()
.
.
sender ! Ack
Your mistake is the following lines:
}.andThen {
case Success(_) =>
log.info("Sending ACK")
sender() ! Ack
}.onComplete {
The andThen callback will be performed by another thread, and the sender() call may be empty (relatively lucky) or even another actor alltogether. You need to capture the sender before performing the first asynchronous action.
val respondTo = sender()
upgradeResponse.flatMap { upgrade =>
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
Future.successful(Done)
} else {
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
}
}.andThen {
case Success(_) =>
log.info("Sending ACK")
respondTo ! Ack
}.onComplete {
case Success(_) =>
log.info("Success proceed")
case Failure(ex) => log.error(ex.getMessage)
}
I have the following code, that does not keep the connection open to the websocket server:
import akka.Done
import akka.actor.{Actor, ActorLogging, Props}
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest}
import akka.stream.{ActorMaterializer, Materializer}
import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import scala.concurrent._
import scala.util.{Failure, Success}
object WsActor {
def props: Props = Props(new WsActor)
}
final class WsActor extends Actor with ActorLogging {
import com.sweetsoft.WsConnector._
implicit val materializer: Materializer = ActorMaterializer()
implicit val ec: ExecutionContextExecutor = context.system.dispatcher
implicit val actor = context.system
// Future[Done] is the materialized value of Sink.foreach,
// emitted when the stream completes
private val incoming: Sink[Message, Future[Done]] =
Sink.foreach[Message] {
case message: TextMessage.Strict =>
println(message.text)
case _ =>
println("Unknown messages.")
}
//private val outgoing: Source[Message, Promise[Option[Message]]] =
// Source.maybe[Message]
// val flow: Flow[Message, Message, Promise[Option[Message]]] =
// Flow.fromSinkAndSourceMat(incoming, Source.maybe[Message])(Keep.right)
log.info("Websocket actor started.")
override def receive: Receive = {
case Initialized =>
log.info("Initialization to receive messages via stream.")
sender() ! Ack
case Completed =>
log.info("Streams completed.")
sender() ! Ack
case Msg(value) =>
val replyTo = sender()
val flow: Flow[Message, Message, Promise[Option[Message]]] =
Flow.fromSinkAndSourceMat(incoming, Source.single(TextMessage(value)).concatMat(Source.maybe[Message])(Keep.right))(Keep.right)
val (upgradeResponse, _) =
Http().singleWebSocketRequest(WebSocketRequest("ws://127.0.0.1:7000/ws"), flow.mapAsync(4)(msg => Future(msg)))
upgradeResponse.flatMap { upgrade =>
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
Future.successful(Done)
} else {
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
}
}.onComplete {
case Success(_) =>
replyTo ! Ack
log.info("Done")
case Failure(ex) => log.error(ex.getMessage)
}
case Failed(ex) =>
log.info(s"Stream failed with ${ex.getMessage}.")
}
}
So every time, when a message is received, it will close the connection and open a new connection for the next request.
The question is, how can I keep the connection open?
Http().webSocketClientFlow in stead of Http().singleWebSocketRequest
Http().webSocketClientFlow will give you a Flow Flow[Message, Message, Future[WebSocketUpgradeResponse]]
This will not create a new connection every time.
You should declare it in the companion object, so every instance of the class can use the same connection.
First declare your actor system for entire application in a separate package.
object ActorEssentials {
implicit val actorSystem = ActorSystem("test")
}
Then you can declare the following
object WsActor {
import ActorEssentials._
def props: Props = Props[WsActor]
val flow = Http()(actorSystem).webSocketClientFlow(...)
}
I have two actors coded as follows.
class Actor1 extends Actor {
val r : ActorRef = actorOf (Props[Actor2], "master")
def receive: Receive = {
case class Mul (a,b) =>
r ! CalcMul (a,b)
case class MulReply (ans) =>
println("Multiply result : " + ans)
// want to send "ans" value to testObject..
}
}
class Actor2 extends Actor {
def receive: Receive = {
case class CalcMul (a,b) =>
sender ! MulReply (a*b)
}
}
object testObject extends App {
val a = ActorSystem("ActorSystem").actorOf(Props[Actor1], "root")
a ! CalcMul (5, 15)
// how to receive "ans" value here?
}
I am able to receive and print the result in Actor1 but need those values in testObject so I can use them for future operations. Cannot have a receive method in testObject as done to receive a message in Actor1 from Actor2, so cannot send them with tell method.
As you want to receive a response from an actor you can use ask pattern for this purpose.
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern._
import akka.util.Timeout
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration.SECONDS
case class CalsMul(a: Int, b: Int)
class Actor1 extends Actor {
val r: ActorRef = context.actorOf(Props[Actor2], "master")
def receive: Receive = {
case req: CalsMul =>
println("received message by Actor1")
r forward req
}
}
class Actor2 extends Actor {
def receive: Receive = {
case request: CalsMul =>
println("received message by Actor2")
Future.successful(request.a * request.b) pipeTo sender
}
}
object testObject extends App {
implicit val system: ActorSystem = ActorSystem("ActorSystem")
val a: ActorRef = system.actorOf(Props[Actor1], "root")
implicit val timeout: Timeout = Timeout(20, SECONDS)
println(system, "sending message to Actor1")
val ans: Future[Int] = (a ? CalsMul(5, 15)).mapTo[Int] // as you are returning the multiplication of a*b
ans.foreach(println)
}
Note: CPU bound operations are not advised to use with actors it can have adverse effect on the performance of your application
Scala 2.12 using Akka here. When one of my actors receives a particular type of message (say, Fizzbuzz), I want it to forward that message on to a small number of other actors, exactly as-is:
I tried:
class Foo extends Actor {
override def receive: Receive = {
case Bar =>
println("Bar!")
case Whitstle =>
println("Whistle!")
case Fizzbuzz =>
val actor1 = context.actorSelection("/user/a1")
val actor2 = context.actorSelection("/user/a2")
val actor3 = context.actorSelection("/user/a3")
actor1 ! _
actor2 ! _
actor3 ! _
}
}
And while that compiles and doesn't throw any exceptions, it doesn't work (none of the 3 actors ever receive the Fizzbuzz message). Any ideas?
In the receive block, collect your msg in a variable and then forward that msg to other actors. Please refer the code below :-
class Foo extends Actor {
override def receive: Receive = {
case Bar =>
println("Bar!")
case Whitstle =>
println("Whistle!")
case msg : Fizzbuzz =>
val actor1 = context.actorSelection("/user/a1")
val actor2 = context.actorSelection("/user/a2")
val actor3 = context.actorSelection("/user/a3")
actor1 ! msg
actor2 ! msg
actor3 ! msg
}
}
This should solve your problem. Please let me know if any doubt persists.
New query: I am trying to pass DSum() as parameter to RemoteActor from localActor, DSum() will do some calculation at Remote node. I am unable to send this to RemoteActor. IS it possible ??(code below)
Done:I am trying to connect Remote actor and local actor, and trying to send objects using case class, but it is unable to get the Message class ( Common.Message(msg) ) of the RemoteActor when being called from localActor, instead it is getting "case _ => println("Received unknown msg from local ")"
1.package.scala
package object check {
trait Context
case object Start
case class Message(msg: String)
case class CxtDA(cxtA: List[CxtA])
case class RCxt(var cxtA: List[CxtA], var cxtB: List[CxtB], var v1: Int, var v2: String) extends Context
case class CxtA(var cxtC: List[CxtC], var v1: Int) extends Context
case class CxtB(var cxtC: List[CxtC], var v1: Int) extends Context
case class CxtC(var v1: String, var v2: Int) extends Context
case class Task(var t1: DSum())
}
2. Remote Actor
package com.akka.remote
import java.io.File
import akka.actor._
import com.typesafe.config.ConfigFactory
import check._
/**
* Remote actor which listens on port 5150
*/
class RemoteActor extends Actor {
override def toString: String = {
return "You printed the Local";
}
def receive = {
case msg: String => {
println("remote received " + msg + " from " + sender)
sender ! "hi"
}
case Message(msg) =>
println("RemoteActor received message "+ msg)
sender ! Message("Hello from server")
case CxtDA(cxtA) =>
println("cxtA "+ cxtA)
case Task(taskA) =>
println ("recieved closure")
case _ => println("unknown msg")
}
}
object RemoteActor{
def main(args: Array[String]) {
//get the configuration file from classpath
val configFile = getClass.getClassLoader.getResource("remote_application.conf").getFile
// //parse the config
val config = ConfigFactory.parseFile(new File(configFile))
// //create an actor system with that config
val system = ActorSystem("RemoteSystem" , config)
// //create a remote actor from actorSystem
val remoteActor = system.actorOf(Props[RemoteActor], name="remote")
println("remote is ready")
remoteActor ! Message("Hello from active remote")
}
}
3.Local Actor
package com.akka.local
import java.io.File
import akka.actor.{Props, Actor, ActorSystem}
import com.typesafe.config.ConfigFactory
import check._
import scala.util.Random
/**
* Local actor which listens on any free port
*/
trait CxtTask {
type CxtT <: Context
def work(ctx: CxtT): CxtT
}
class DSum extends CxtTask with Serializable{
override type CxtT = CxtA
def work(ctx: CxtA): CxtA = {
val sum = ctx.cxtC.foldRight(0)((v, acc) => v.v2 + acc)
ctx.cxtC= List()
ctx.v1 = sum
println("ctx: " + ctx)
ctx
}
}
class LocalActor extends Actor{
// import Common._
#throws[Exception](classOf[Exception])
val remoteActor = context.actorSelection("akka.tcp://RemoteSystem#127.0.0.1:5150/user/remote")
println("That 's remote:" + remoteActor)
remoteActor ! "hi"
var counter = 0
override def toString: String = {
return "You printed the Local";
}
def receive = {
case msg:String => {
println("got message from remote" + msg)
}
case Start =>
println("inside Start.local "+ remoteActor)
remoteActor ! Message("Hello from the LocalActor")
case Message(msg) =>
println("LocalActor received message: "+ msg)
if (counter < 5) {
sender ! Message("Hello back to you")
counter += 1
}
case CxtDA(cxtA) =>
remoteActor ! CxtDA(cxtA)
case Task(t1) =>
remoteActor ! Task(t1)
}
}
object LocalActor {
def main(args: Array[String]) {
val configFile = getClass.getClassLoader.getResource("local_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("ClientSystem",config)
val localActor = system.actorOf(Props[LocalActor], name="local")
localActor ! Start
def createRndCxtC(count: Int):List[CxtC] = (for (i <- 1 to count) yield CxtC(Random.nextString(5), 3)).toList
def createRndCxtB(count: Int): List[CxtB] = (for (i <- 1 to count) yield CxtB(createRndCxtC(count), Random.nextInt())).toList
def createRndCxtA(count: Int): List[CxtA] = (for (i <- 1 to count) yield CxtA(createRndCxtC(count), Random.nextInt())).toList
val tree = RCxt(createRndCxtA(2),createRndCxtB(2),1,"")
val workA = new DSum()
tree.cxtA.foreach(ctxa =>workA.work(ctxa))
localActor ! Task(new DSum())
}
}
[Remote actor output][1]
[1]: https://i.stack.imgur.com/mtmvU.jpg
The key thing here is that you have defined two different protocols for each actor:
Common object that resides in the RemoteActor.scala file
Common object that resides in the LocalActor.scala file
Hence, when sending a Common.Message within the Local Actor, you are basically creating a message with a different type than the Common.Message from the Remote Actor. Hence, the actor is not able to process it.
As a good practice in Akka, whenever an actor has a specific message procotol, that should be defined in its companion object. However, if you have multiple actors that share the same protocol (their behavior is defined by processing those types of messages), then you should put that protocol in an object and import it from your actors.
I hope this is helpful.