Akka TCP Request Response need Thread.sleep - scala

Good morning , I hava a problem with basic akka IO by TCP
I've a basic implemetation of Client and Server as shown on akka documentation:
Client is https://github.com/akka/akka/blob/v2.5.20/akka-docs/src/test/scala/docs/io/IODocSpec.scala#L67-L103
And Handler is [SimpleEchoHandler] (https://github.com/akka/akka/blob/v2.5.20/akka-docs/src/test/scala/docs/io/EchoServer.scala#L227-L304) but also the other act as the same way.
I've a main test method that stop on first connection to the server:
package core.september
import java.net.InetSocketAddress
import akka.actor.{Actor, ActorLogging, ActorRef, ActorSystem, Props}
import akka.util.ByteString
import com.typesafe.config.ConfigFactory
import core.september.fastchain.network.Client
/**
* #author ${user.name}
*/
object App {
class ClientHandler extends Actor with ActorLogging {
def receive = {
case toLog ⇒ {
log.debug("Client received "+ toLog.toString)
}
//Thread.sleep(200)
}
}
def main(args : Array[String]) {
val config = ConfigFactory.parseString("akka.loglevel = DEBUG")
implicit val system = ActorSystem("EchoServer", config)
var clientHand:ActorRef = system.actorOf(Props(classOf[ClientHandler]))
var address:InetSocketAddress = new InetSocketAddress("localhost",5080)
var ackServer = system.actorOf(Props(classOf[EchoManager], classOf[SimpleEchoHandler],5080), "simple")
var client:ActorRef = system.actorOf(Props(classOf[Client],address,clientHand));
//Thread.sleep(200)
client ! ByteString("echo")
//Thread.sleep(200)
client ! "close"
}
}
If I' don't comment out the two Thread.sleep after each message I can't see the output of the sent message, the output, without sleep is just:
[DEBUG] [02/07/2019 15:47:21.812] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:47:21.816] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Connection established to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:47:21.825] [EchoServer-akka.actor.default-dispatcher-3] [akka://EchoServer/user/$a] Client received Connected(localhost/127.0.0.1:5080,/127.0.0.1:54616)
I completely loose ByteString message and the "close" message.
My question is why i need to put the main thread in sleep to show also other messages.
With thread.sleep message are correctly logged:
[DEBUG] [02/07/2019 15:53:55.988] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:53:55.999] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/system/IO-TCP/selectors/$a/0] Connection established to [localhost/127.0.0.1:5080]
[DEBUG] [02/07/2019 15:53:56.011] [EchoServer-akka.actor.default-dispatcher-5] [akka://EchoServer/user/$a] Client received Connected(localhost/127.0.0.1:5080,/127.0.0.1:54712)
[DEBUG] [02/07/2019 15:53:56.157] [EchoServer-akka.actor.default-dispatcher-2] [akka://EchoServer/user/$a] Client received ByteString(101, 99, 104, 111)
[DEBUG] [02/07/2019 15:53:56.374] [EchoServer-akka.actor.default-dispatcher-4] [akka://EchoServer/user/$a] Client received connection closed
ClientActor implementation is:
package core.september.fastchain.network
import akka.actor.{ Actor, ActorRef, Props }
import akka.io.{ IO, Tcp }
import akka.util.ByteString
import java.net.InetSocketAddress
object Client {
def props(remote: InetSocketAddress, replies: ActorRef) =
Props(classOf[Client], remote, replies)
}
class Client(remote: InetSocketAddress, listener: ActorRef) extends Actor {
import Tcp._
import context.system
import akka.io.Tcp
/*if (listener == null) {
listener = Tcp.get(context.system).manager
}*/
IO(Tcp) ! Connect(remote)
def receive = {
case CommandFailed(_: Connect) ⇒
listener ! "connect failed"
context stop self
case c # Connected(remote, local) ⇒
listener ! c
val connection = sender()
connection ! Register(self)
context become {
case data: ByteString ⇒
connection ! Write(data)
case CommandFailed(w: Write) ⇒
// O/S buffer was full
listener ! "write failed"
case Received(data) ⇒
listener ! data
case "close" ⇒
connection ! Close
case _: ConnectionClosed ⇒
listener ! "connection closed"
context stop self
}
}
}
thank you vey much.

You gotta wait for the actor to process your messages before exiting the app.
The easiest way is to use Akka's gracefulStop pattern:
import akka.pattern.gracefulStop
client ! ByteString("echo")
client ! "close"
Await.result(gracefulStop(client, 1 second)(system)

Related

Remote actor not responding to Identify?

I'm just messing around with some basics to learn akka remoting, and I'm running into an issue where my proxy class sends Identify messages to my backend, but never receives a response back.
I've verified that the backend receives messages sent to the ActorSelection, and I've seen a logging message where the backend actor says it will use xxx for serialization of the ActorIdentity messages. Not sure where I'm getting it wrong.
Here is my proxy class:
package remoting
import akka.actor.{Actor, ActorIdentity, ActorRef, Identify, ReceiveTimeout, Terminated}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
class BackendProxyActor extends Actor {
context.setReceiveTimeout(3.seconds)
val path = createPath
val backendSelection = context.actorSelection(path)
println(f"BackendSelection is $backendSelection")
override def preStart(): Unit = backendSelection ! Identify(1)
override def receive: Receive = {
case ActorIdentity(1, Some(actor)) =>
context.setReceiveTimeout(Duration.Undefined)
context.watch(actor)
context.become(active(actor))
case ActorIdentity(1, None) =>
println("Backend actor not available")
case ReceiveTimeout =>
backendSelection ! Identify(1)
case msg: Any => println(f"Received $msg while identifying backend")
}
def active(backend: ActorRef): Receive = {
case msg: Any => backend ! msg
case Terminated(backend) =>
println("backend terminated, going to identifying state")
context.setReceiveTimeout(3.seconds)
context.become(receive)
}
def createPath: String = {
val config = ConfigFactory.load("frontend").getConfig("backend")
val name = config.getString("name")
val host = config.getString("host")
val port = config.getString("port")
val system = config.getString("system")
val protocol = config.getString("protocol")
f"$protocol://$system#$host:$port/$name"
}
}
Here is my backend class:
package remoting
import akka.actor.{Actor, Identify, PoisonPill}
import com.typesafe.config.ConfigFactory
class BackendActor extends Actor {
val config = ConfigFactory.load("backend")
override def receive: Receive = {
case "stop" => self ! PoisonPill
case msg: Any => println(f"Received $msg")
}
}
My frontend class:
package remoting
import akka.actor.{Actor, Props}
class FrontendActor extends Actor {
val proxy = context.actorOf(Props[BackendProxyActor], "backendProxy")
override def receive: Receive = {
case msg: Any => proxy ! msg
}
}
and finally my App class:
package remoting
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Main extends App {
val frontendConfig = ConfigFactory.load("frontend")
val frontend = ActorSystem("frontend", frontendConfig)
val frontendActor = frontend.actorOf(Props[FrontendActor], "FrontendActor")
(1 to 20).foreach(i => frontendActor ! f"Msg #$i")
frontendActor ! "stop"
}
My backend is being started in another process, and is run on port 2551, while my frontend is on port 2552.

How to wait for Akka actor to start during tests?

I have a test that needs to make an assertion on something that happens during an actor's preStart(), but I haven't figured out how to wait until that happens, and sometimes it doesn't happen before the assertion is made (and sometimes it does). I have tried this:
EventFilter.debug(start = "started", occurrences = 1).assertDone(10.seconds)
but I get an error message when using it:
java.lang.AssertionError: assertion failed: 1 messages outstanding on DebugFilter(None,Left(started),false)
You could place the creation of the actor inside an intercept block:
import akka.actor._
import akka.testkit.EventFilter
import com.typesafe.config.ConfigFactory
class MyActor extends Actor with ActorLogging {
override def preStart(): Unit = {
log.debug("started MyActor...")
}
def receive = {
case m => log.debug(s"Received this message: $m")
}
}
object MyActor {
def props() = Props[MyActor]
}
object EventFilterTest extends App {
implicit val system = ActorSystem("testsystem", ConfigFactory.parseString("""
akka.loggers = ["akka.testkit.TestEventListener"]
akka.loglevel = "DEBUG"
"""))
EventFilter.debug(start = "started", occurrences = 1) intercept {
val myActor = system.actorOf(MyActor.props)
myActor ! "cows"
}
}
Running the above code produces the following output:
[DEBUG] [...] [run-main-0] [EventStream(akka://testsystem)] logger log1-TestEventListener started
[DEBUG] [...] [run-main-0] [EventStream(akka://testsystem)] Default Loggers started
[DEBUG] [...] [testsystem-akka.actor.default-dispatcher-5] [akka://testsystem/user/$a] Received this message: cows
The intercept "catches" the debug statement in the actor's preStart hook.

How can I test a publisher to a DistributedPubSub in Akka Cluster?

I have an actor whose sole responsibility is to forward messages it receives from external interfaces (command-line, user, etc.) to appropriate topics. I want to test that it correctly publishes these messages.
I will need to create some dummy subscriber who will expect messages published to a certain topic and make assertions about the messages it receives.
Here's my code that I attempted to realize that:
Messages.scala
case class Foo(foo: String)
InterfaceForwardingActor.scala
import akka.actor.{Actor, ActorLogging}
import akka.cluster.pubsub.{DistributedPubSub, DistributedPubSubMediator}
import com.typesafe.config.Config
/** Actor responsible for forwarding stimuli external to the system.
* For instance, messages from the command-line interface or from a UI.
*
*/
class InterfaceForwardingActor extends Actor with ActorLogging {
import DistributedPubSubMediator.Publish
protected val mediator = DistributedPubSub(context.system).mediator
log.info(s"Hello from interface forwarder.")
final val topic = "info"
def receive = {
case foo: Foo => {
log.info("Forwarding a Foo message")
mediator ! Publish(topic, foo)
}
}
}
and the test code
InterfaceForwardingActorTest.scala
import akka.actor.{ActorSystem, Props}
import akka.cluster.client.ClusterClient.Publish
import akka.cluster.pubsub.DistributedPubSub
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
class InterfaceForwardingActorTest extends
TestKit(ActorSystem("InterfaceForwardingActorSpec")) with ImplicitSender with
WordSpecLike with Matchers with BeforeAndAfterAll {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
"An InterfaceForwardingActor" must {
val interfaceForwardingActor = system.actorOf(Props[InterfaceForwardingActor])
val probe = TestProbe()
val mediator = DistributedPubSub(system).mediator
// subscribe the test probe to the "info" topic
mediator ! Publish("info", probe.ref)
"publish a Foo message" in {
val msg = Foo("test")
interfaceForwardingActor ! msg
probe.expectMsg(msg)
}
}
}
What I find is that the probe, which is subscribed to the info topic, doesn't receive the message within the default timeout period of 3 seconds and the assertion fails. The interesting part, however, is that I do see the log message stating that the interface forwarding actor is indeed forwarding a Foo message.
What am I doing wrong in my test?
The TestProbe should be subscribed to the topic in the the test code:
mediator ! Subscribe("info", probe.ref)
instead of
mediator ! Publish("info", probe.ref)
Documentation page of distributed pub-sub is here, for reference.

Akka Testkit does not work when using state variable in stub in other than last test case

I have a problem when using the following stub with Akka Testkit:
import akka.actor.{Props, Status}
import akka.camel.{CamelExtension, CamelMessage, Consumer}
import akka.testkit.{ImplicitSender, TestProbe}
import org.scalatest._
[import own packages]
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
class ActorSpec extends AkkaTestBase
with ImplicitSender with WordSpecLike
with Matchers with BeforeAndAfterAll with IOSugars {
var stubResponse: String = ""
var doNotRespond = false
val stubEndpoint = "direct:test"
val reportProbe = TestProbe()
val actorUnderTest= TestProbe()
// create stub consumer
val stubConsumer = system.actorOf(Props(new Consumer {
override val endpointUri = stubEndpoint
override def replyTimeout = 1 second
override def receive: Receive = {
case msg: CamelMessage if doNotRespond => ()
case msg: CamelMessage => sender() ! stubResponse;
case _ => sender() ! "ack"
}
}), "stubActor")
[...]
}
When I run the following test cases:
"Respond with failure when camel produces a failure" in {
doNotRespond = true
actorUnderTest ! // Send relevant message
// (which returns the () above because of doNotRespond set to true)
doNotRespond = false // Return to original state
testProbe.expectMsg // Expect relevant message
"Respond with a failure when 'ERROR' status code is returned" in {
stubResponse = readStringFromResource("/failure.xml")
actorUnderTest ! // send relevant message
expectMsgClass(classOf[Status.Failure])
}
Unfortunately this does not work. We get a timeout:
ERROR o.a.c.processor.DefaultErrorHandler - Failed delivery for (MessageId: xxx on ExchangeId: xxx). Exhausted after delivery attempt: 1 caught: java.util.concurrent.TimeoutException: Failed to get response from the actor [ActorEndpointPath(akka://xxx/stubActor] within timeout [1 second].
When the test case that changes the doNotRespond state variable is put as last test case everything does work.
Why is this and how can it be fixed?

spray-can simple-http-server example how are Bound messages handled?

In the spray-can HTTP server example, when I run the server, I don't see any "Bound" message showing up in the log:
https://github.com/spray/spray/tree/master/examples/spray-can/simple-http-server/src/main
However, if I try to create my own server, and ignore the Bound message I get them going to dead-letters:
$ sbt run
[info] Set current project to pingpong (in build file:/D:/Projects/pingpong/)
[info] Updating {file:/D:/Projects/pingpong/}pingpong...
[info] Resolving jline#jline;2.12.1 ...
[info] Done updating.
[info] Running drozdyuk.pingpong.Main
[INFO] [08/03/2015 20:05:47.246] [default-akka.actor.default-dispatcher-2] [akka
://default/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:8081
[INFO] [08/03/2015 20:05:47.246] [default-akka.actor.default-dispatcher-2] [akka
://default/deadLetters] Message [akka.io.Tcp$Bound] from Actor[akka://default/us
er/IO-HTTP/listener-0#-892116855] to Actor[akka://default/deadLetters] was not d
elivered. [1] dead letters encountered. This logging can be turned off or adjust
ed with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letter
s-during-shutdown'.
My question is mainly out of curiosity, how does the Bound message get handled in the example? Is it some kind of logging that swallows them or something else?
Code for my Main.scala:
package drozdyuk.pingpong
import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
object Main extends App {
implicit val system = ActorSystem()
// the handler actor replies to incoming HttpRequests
val handler = system.actorOf(Props[WebService], name = "handler")
val PORT = 8081
val DOMAIN = "localhost"
IO(Http) ! Http.Bind(handler, interface = DOMAIN, port = PORT)
}
and my WebService.scala:
package drozdyuk.pingpong
import akka.actor._
import spray.http.HttpMethods.{GET}
import spray.can.Http
import spray.http.{HttpRequest, HttpResponse, Uri}
class WebService extends Actor with ActorLogging {
def receive = {
// New connection - register self as handler
case _: Http.Connected => sender ! Http.Register(self)
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
sender ! HttpResponse(entity = "pong")
case HttpRequest(GET, Uri.Path("/pong"), _, _, _) =>
sender ! HttpResponse(entity = "ping")
case _: HttpRequest => sender ! HttpResponse(status = 404, entity = "Unknown resource!")
}
}
You could use another version of tell in your Main:
IO(Http).tell(Http.Bind(handler, interface = DOMAIN, port = PORT), sender = handler)
And then handle Bound message in your WebService:
class WebService extends Actor with ActorLogging {
def receive = {
case Http.Bound(address) =>
//all other cases...
}
}
In your case you are using
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
and there is no implicit sender in scope of Main so default noSender will be used.
I tried this and it works great for me:
import akka.actor._
import akka.io._
import spray.can.Http
import spray.http.HttpResponse
import spray.http.HttpRequest
import spray.http.Uri
import spray.http.HttpMethods.{GET}
class WebService extends Actor with ActorLogging {
def receive = {
case _: Http.Connected => sender ! Http.Register(self)
case _: HttpRequest => sender ! HttpResponse(status = 404, entity = "Unknown resource!")
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
sender ! HttpResponse(entity = "pong")
//all other cases...
}
}
object TestSpray extends App {
implicit val system = ActorSystem()
val myListener: ActorRef = system.actorOf(Props[WebService], name = "handler")
IO(Http) ! Http.Bind(myListener, interface = "localhost", port = 8080)
}