For learning purposes I'm trying to implement a simple play application that gets data from a remote actor. The code for the actor is as follows:
import akka.actor.{Props, ActorSystem, Actor}
class NumbersServer extends Actor {
var number = 0
protected def receive = {
case 'next => {
number += 1
number
}
case 'reset => number = 0
case 'exit => context.stop(self)
case 'get => sender ! number
}
}
object Server {
def main(args: Array[String]) {
val system = ActorSystem("ServerSystem")
val server = system.actorOf(Props[NumbersServer], "server")
}
}
I package it into a jar and start it from the command line. If I try to send messages to this actor from a Scala console opened from another window, all works fine. Now I want to get the actor from the Play framework. In the Application object I define the following method:
def numbers = Action {
Ok(views.html.numbers(Client.actor.path.name))
}
Then in the models package I define the Client object:
object Client {
import play.api.Play.current
val actor = Akka.system.actorFor("akka://ServerSystem#127.0.0.1:2552/user/server")
}
The numbers.html.scala file:
#(message: String)
#main("Header") {
<h1>#message</h1>
}
So I expect that when I go to 127.0.0.1:9000/numbers, I'd get a page with the path to the server actor. Instead of this, I get <h1>deadLetters</h1>. What do I do wrong and how this should be done correctly?
Please follow the configuration given in
https://groups.google.com/forum/#!topic/akka-user/Vw-B8nQeagk
And also add akka-remote dependency
val appDependencies = Seq(
"com.typesafe.akka" % "akka-remote" % "2.0.2"
)
Related
I am trying to write a simple integration test for a scala application that uses the AKKA framework.
I want to
have the application start on my local host
write test cases that hit the application directly
I have done similar things things using springboottest but i can't seem to find anything remotely similar to this. I have been trying to read up on testkit routes and what not but it seems more like a unit test then it is a full application integration testing.
Any pointers or recommendations on what I am looking for would be great.
Thanks!
First import
val akkaVersion = "2.6.10"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion)
Create the actors
import akka.actor.{Actor, ActorSystem, Props}
object ActorsIntro extends App {
run()
def run() = {
val actorSystem = ActorSystem("ActorsIntro")
println(actorSystem.name)
val worldCounter = actorSystem.actorOf(Props[WordCountActor], "WordCounter")
val anotherWorldCounter = actorSystem.actorOf(Props[WordCountActor], "AnotherWordCounter")
worldCounter ! "I am reviewing Akka using Scala and it is pretty damn awesome !"
worldCounter ! "asynchronous message Akka Scala"
anotherWorldCounter ! "asynchronous message Akka Scala"
val person = actorSystem.actorOf(Person.props("Bob"))
person ! "hi"
}
class WordCountActor extends Actor {
var totalWords = 0
override def receive: PartialFunction[Any, Unit] = {
case message: String =>
println(s"Message received[ $message ]")
totalWords += message.split(" ").length
println(s"Total words counted: $totalWords")
case msg => println(s"word count. I cannot understand ${msg.toString}")
}
}
object Person {
def props(name: String) = Props(new Person(name))
val propsPersonActor = {
Props(new Person(""))
}
}
class Person(name: String) extends Actor {
override def receive: Receive = {
case "hi" =>
val reply = s"Hi, my name is $name"
println(reply)
sender() ! reply
case message => sender() ! message
}
}
}
And its test case
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
class ActorsIntroTest extends TestKit(ActorSystem("ActorsIntroSpec"))
with ImplicitSender
with AnyWordSpecLike
with Matchers
with BeforeAndAfterAll {
override def afterAll(): Unit = {
TestKit.shutdownActorSystem(system)
}
"The ActorsIntro actor" should {
"send back hi replay" in {
val name = "Bob"
val actorPerson = system.actorOf(ActorsIntro.Person.props(name))
val hi = "hi"
val hiReply = s"Hi, my name is $name"
actorPerson ! hi
expectMsg(hiReply)
}
"or send back the same message" in {
val name = "Bob"
val actorPerson = system.actorOf(ActorsIntro.Person.props(name))
val message = "hello, test"
actorPerson ! message
expectMsg(message)
}
}
}
If you want the application to run on localhost, I recommend you consider using akka http, so that you can bind a localhost server and test your app.
Akka Testkit AutoPilot documentation examples show that we can send messages to a TestProbe right after invoking setAutoPilot:
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any): TestActor.AutoPilot =
msg match {
case "stop" ⇒ TestActor.NoAutoPilot
case x ⇒ testActor.tell(x, sender); TestActor.KeepRunning
}
})
//#autopilot
probe.ref ! "hallo"
On the other handsetAutoPilot has been implemented as sending a message to the testActor:
def setAutoPilot(pilot: TestActor.AutoPilot): Unit = testActor ! TestActor.SetAutoPilot(pilot)
According to Akka message receive order guarantees, there is no way for testActor (probe.ref) to receive "hallo" before TestActor.SetAutoPilot(pilot) because both are being sent from the same origin.
However, if we used a third actor (created using system.actorOf(...)) to send a "hello" to probe.ref,
wouldn't it be possible that, under some circumstances, it got received by probe.ref before TestActor.SetAutoPilot(pilot) thus ending up being ignored?
In theory - yes, definitely - and that's basically your own answer to the question. In practice, this is unlikely, since the other message travels the longer path, so something very unusual needs to happen so that it arrives earlier.
Since theorizing around this wouldn't give any actionable answer, I've written a test to have an empirical observation:
Build.sbt:
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % "2.5.17"
)
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-testkit" % "2.5.17",
"org.scalactic" %% "scalactic" % "3.0.5",
"org.scalatest" %% "scalatest" % "3.0.5",
"org.scalacheck" %% "scalacheck" % "1.14.0"
) map (_ % "test")
Test:
import scala.concurrent.duration.DurationInt
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.pattern.ask
import akka.testkit.{TestActor, TestKit, TestProbe}
import akka.util.Timeout
import org.scalatest.{Matchers, PropSpecLike}
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.prop.PropertyChecks
class AutopilotTest extends TestKit(ActorSystem("test"))
with PropSpecLike with PropertyChecks with ScalaFutures with Matchers {
private implicit val askTimeout: Timeout = Timeout(100.millis)
property("Test probe receives autopilot before any other message from same origin") {
forAll(minSuccessful(1000)) { msg: String =>
val probe = TestProbe()
probe.setAutoPilot((sender: ActorRef, msg: Any) => msg match {
case x => sender ! x; TestActor.KeepRunning
})
whenReady((probe.ref ? msg).mapTo[String]) {_ shouldBe msg}
}
}
private class ProxyActor(target: ActorRef) extends Actor {
override def receive: Receive = { case msg: Any => target forward msg }
}
private object ProxyActor { def props(target: ActorRef): Props = Props(new ProxyActor(target)) }
property("Test probe receives autopilot before any other message from other origin") {
// set minSuccessuful to as high as you want, but note that current version takes ~38 seconds on my laptop to run
forAll(minSuccessful(1000)) { msg: String =>
val probe = TestProbe()
val proxy = system.actorOf(ProxyActor.props(probe.ref))
val result = (proxy ? msg).mapTo[String]
probe.setAutoPilot((sender: ActorRef, msg: Any) => msg match {
case x => sender ! x; TestActor.KeepRunning
})
whenReady(result) {_ shouldBe msg}
}
}
}
Practically, I went up all they way to 10000 repeats for the second test, and the tests always passed - also I've made sure it fails if autopilot is set after sending a message to proxy, or if testProbe does not respond.
So, I would say the potential issue you're describing either does not happen at all, or is highly unlikely. This test is a very simplistic one of course, so under other conditions (i.e. blocking, parallel test execution, CI, etc.) observations might be different, but at least it provides some evidence for a most common/simple case.
I'm trying to send a message to a singleton actor that was deployed on a remote node through another actor.
This is the manager that is waiting for a memberUp event, then deploys Worker actor on that node and then sends the singleton a message:
object Manager extends App {
val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
sys.actorOf(Props[Manager], "manager")
}
class Manager extends Actor with ActorLogging {
override def receive: Receive = {
case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
context.system.actorOf(ClusterSingletonManager.props(
singletonProps = Props(classOf[Worker]),
singletonName = "worker",
terminationMessage = End,
role = Some("worker")).withDeploy(Deploy(scope = RemoteScope(member.address))))
context.actorOf(ClusterSingletonProxy.props(
singletonPath = s"/user/singleton/worker",
role = Some(s"worker")), "worker") ! "hello"
}
override def preStart(): Unit = {
Cluster(context.system).subscribe(self,classOf[MemberUp])
}
}
This is the worker:
object Worker extends App{
ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))
}
class Worker extends Actor with ActorLogging {
override def receive: Receive = {
case msg =>
println(s"GOT MSG : $msg from : ${sender().path.name}")
}
}
And the application.conf:
manager {
akka {
actor {
provider = "akka.cluster.ClusterActorRefProvider"
}
cluster {
auto-down-unreachable-after = 20s
seed-nodes = [
"akka.tcp://mySys#127.0.0.1:2552"
]
roles.1 = "manager"
}
remote.netty.tcp.port = 2552
}
}
worker {
akka {
cluster {
auto-down-unreachable-after = 20s
seed-nodes = [
"akka.tcp://mySys#127.0.0.1:2552"
]
roles.1 = "worker"
}
remote.netty.tcp.port = 2554
actor {
provider = "akka.cluster.ClusterActorRefProvider"
}
}
}
The worker is initialized (and I can see in the logs the state change [Start -> Oldest] message) but the message sent from the manager never arrives to the worker. It used to work fine when I was deploying the singleton on the remote node, but now I want the manager the deploy it.
I also tried to deploy it as the child of the manager (using context instead of context.system) and changed the singleton path to user/manager/singleton/worker, but it didn't work.
I'm using Akka 2.3.11
Edit:
sbt file:
name := "MyProject"
version := "1.0"
scalaVersion := "2.10.5"
libraryDependencies +=
"com.typesafe.akka" %% "akka-actor" % "2.3.11",
"com.typesafe.akka" %% "akka-cluster" % "2.3.11",
"joda-time" % "joda-time" % "2.0",
"com.typesafe.akka" %% "akka-contrib" % "2.3.11"
So I played around a bit with different options of creating ClusterSingletonManagers and I think deploying them remotely is breaking something within the singleton pattern. I have gathered a few indicators for this:
Since it is a remote deployment the path of the ClusterSingletonManager on the worker node is /remote/akka.tcp/mySys#127.0.0.1:2552/user/worker. I don't think the library can / will handle this, since it expects /user/worker
When trying to send the message from the master node using ClusterSingletonProxy log in DEBUG mode states No singleton available, stashing message hello worker and Trying to identify singleton at akka.tcp://mySys#127.0.0.1:2552/user/worker/singleton (which fails and retries) -> It is looking for the singleton on the wrong node, since no manager is available and it is apparently not aware that the singleton is on the worker node.
When creating the ClusterSingletonManager on the worker node directly everything works as expected.
You also had an issue with your naming of the manager. Your singletonName is worker and your manager itself (the actor) does not have any name. When you create the proxy you use the path /user/singleton/worker, but the path should be as follows: /user/{actorName}/{singletonName}. So in my code I used worker as the actorName and singleton as the singletonName.
So here's my working code:
object Manager extends App {
val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
sys.actorOf(Props[Manager], "manager")
}
class Manager extends Actor with ActorLogging {
override def receive: Receive = {
case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
context.actorOf(ClusterSingletonProxy.props(
singletonPath = s"/user/worker/singleton",
role = Some("worker")), name = "workerProxy") ! "hello worker"
}
override def preStart(): Unit = {
Cluster(context.system).subscribe(self,classOf[MemberUp])
}
}
object Worker extends App{
val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))
sys.actorOf(ClusterSingletonManager.props(
singletonProps = Props(classOf[Worker]),
singletonName = "singleton",
terminationMessage = PoisonPill,
role = Some("worker")), name = "worker")
}
class Worker extends Actor with ActorLogging {
override def receive: Receive = {
case msg =>
println(s"GOT MSG : $msg from : ${sender().path.name}")
}
}
application.conf and build.sbt stayed the same.
EDIT
Got it to work with by referencing the ClusterSingletonProxy with the actual path on the worker node (calculating in that it is a network path). I am not sure if I would recommend this, since I am still not sure, if that library is designed to be able to do this, but it works at least in this minimal example:
object Manager extends App {
val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("manager"))
sys.actorOf(Props[Manager], "manager")
}
class Manager extends Actor with ActorLogging {
override def receive: Receive = {
case MemberUp(member) if member.address != Cluster(context.system).selfAddress =>
val ref = context.system.actorOf(ClusterSingletonManager.props(
singletonProps = Props(classOf[Worker]),
singletonName = "singleton",
terminationMessage = PoisonPill,
role = Some("worker")).withDeploy(Deploy(scope = RemoteScope(member.address))), name = "worker")
context.actorOf(ClusterSingletonProxy.props(
singletonPath = s"${ref.path.toStringWithoutAddress}/singleton", // /remote/akka.tcp/mySys#127.0.0.1:2552/user/worker/singleton
role = Some("worker")), name = "workerProxy") ! "hello worker"
}
override def preStart(): Unit = {
Cluster(context.system).subscribe(self,classOf[MemberUp])
}
}
object Worker extends App{
val sys = ActorSystem("mySys", ConfigFactory.load("application").getConfig("worker"))
}
class Worker extends Actor with ActorLogging {
override def receive: Receive = {
case msg =>
println(s"GOT MSG : $msg from : ${sender().path.name}")
}
}
I'm trying to create a websocket using Play ScalaWebsockets. I referred to their documentation which is located here. And as of the moment, I am using these SBT plugins which is required to use Play's websockets.
"com.typesafe.akka" %% "akka-actor" % "2.4.7",
"com.typesafe.akka" %% "akka-testkit" % "2.4.7" % "test",
This is my Actor:
package actors.chat
import akka.actor._
import akka.stream._
object ChatSocketActor {
def props(out: ActorRef) = Props(new ChatSocketActor(out))
}
class ChatSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! ("I received your message: " + msg)
}
}
In my Controller, I have this socket function:
#Singleton
class ChatController #Inject()(
val messagesApi: MessagesApi,
val pageMetaApi: PageMetaApi,
implicit val materializer: Materializer,
implicit val system: ActorSystem,
implicit val wja: WebJarAssets
) extends Controller with I18nSupport with PageMetaSupport {
import actors.chat.ChatSocketActor
def index = Action { implicit request =>
Ok(views.html.chat.index());
}
def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef(out => ChatSocketActor.props(out))
}
}
After I compile and refresh the browser, I get this 426 response.
Upgrade to WebSocket required
Is there something I missed?
PROBLEM SOLVED:
I'm new to websockets. Problem was I was accessing the socket on a standard HTTP. I changed my routes to
# Chat
GET /chat controllers.ChatController.index
GET /websocket controllers.ChatController.socket
And a JS script in the /chat page which calls the websocket connection.
var ws = new WebSocket("ws://127.0.0.1:9000/websocket");
console.log(ws)
I am working on Play framework 2.5.3 and Akka 2.4.7 using Scala. I have an application made in play based webSockets using Akka. I am using the project as a jar in another project.
My code in new project looks like this:
For accepting connections:
val postActor = actorSystem.actorOf(Props[PostActorClass])
def wsSocket = WebSocket.accept[JsValue,JsValue]{ request =>
ActorFlow.actorRef(out => wsConnObj.HandlerClass.props(out,postActor))
}
For getting references of all connections stored in my jar project:
def getConnReferences:Set[ActorRef] = {
implicit val timeout = Timeout(5 seconds)
val future = postActor ? GetReferences
val result = Await.result(future, timeout.duration).asInstanceOf[Set[ActorRef]]
(result)
}
For sending messages to all the references:
def sendMsg = {
val msg = Json.parse("""{"username":"Server","message":"Reached finally.","pub_key":"empty","target":"user"}""")
val refs = getConnReferences
refs.foreach( _ ! msg)
}
I am trying to use these methods in my new project's HomeController. But unable to work them together, like wsSocket should be executed , then sendMsg (which itself calls getConnReferences also).
How will I achieve that? If am calling all these blocks in single action method, I can't establish connection even.
Code for WebSocketConnect class:
class WebSocketConnect #Inject() (cache:CacheApi) (implicit actorSystem: ActorSystem,materializer: Materializer){
object HandlerClass {
homelogger.info(logMessages.passingActorRef)
def props(out: ActorRef, postActor: ActorRef) = {
Props(new SocketHandlerClass(out, postActor, cache, postActorToUsernameMap))
}
}
}