futures used with actors in Akka - scala

I want to create a server/client system using akka remoting. At first I create a simple remote Server. (I wrote the code below for testing purposes and to clarify my concepts so it really doesn't do much.)
I want my client to send a username and a password to the server which the server can verify and reply back. At first I create a client actor. From my client object I send this actor the username and password (I use future here). The client actor then uses another future to send this username and password to the server.
The server in my code gets the username and password and prints it out. The problem is I dont get anything back from the server. Since I used a future to send the information to the server, it should reply back with a result. This is where I think I have a conceptual problem. Going through the akka documentation did not clarify this. But I an sure I am messing up something very basic. The server code is:
EDITED after suggestions from TrustNoOne and cmbaxter.
package server
import collection.mutable
import akka.actor._
import com.typesafe.config.ConfigFactory
import shared._
import shared.CaseClass._
object Server extends App {
val system = ActorSystem("ServerSystem",
ConfigFactory.parseString("""
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname = 127.0.0.1
port = 5555
}
}
}
"""))
system.actorOf(Props[ServerActorClass], "ServerActor")
}
class ServerActorClass extends Actor {
def receive = {
case userPass: UserPass => {
sender ! verified()
println(userPass)
}
case testMsg: String => println("Got a msg"+ testMsg)
}
}
The client code is:
package client
import ...
object Client extends App {
val system = ActorSystem("ClientSystem",
ConfigFactory.parseString("""
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname = 127.0.0.1
port = 0
}
}
}
"""))
val clientActor = system.actorOf(Props[ClientActor], name = "ClientActor") //the local actor
implicit val timout = Timeout(50 seconds)
val f = ask(clientActor, UserPass("a","b"))
f.onSuccess {
case GO => println("Got something back from Client actor") //Still doesn't happen!
}
}
class ClientActor extends Actor {
// create the remote actor
val server = context.actorFor("akka.tcp://ServerSystem#127.0.0.1:5555/user/ServerActor")
implicit val timout = Timeout(1 seconds)
def receive = {
case a: String => println("back" + a)
case a: UserPass => {
val f: Future[Any] = (server ? a)
f.onSuccess {
case response: verified => {
println("Got something back from server") //Does happen now!
val asker = sender()
asker ! GO()
}
case response: verificationFailed => {
val asker = sender()
asker ! NO()
}
}
}
}
}
Case classes that are shared by both the client and the server:
package shared
case object CaseClass {
case class verified //Server msg to acknowledge user and pass successful verification
case class verificationFailed //Server msg saying user pass verification failed
case class GO
case class NO
case class UserPass(user:String, pass:String)
I want to know what I am doing wrong. If someone could explain rather than just point out the problem, it would be great, since I am looking to learn.

In the server actor, you're sending the response like this:
sender ! CaseClass.verified
You are actually sending the "verified" class companion object back to the client. You should either make the verified class a case object or send back to the client a verified instance:
sender ! CaseClass.verified()
You are doing other (unrelated) errors:
closing over sender in the future callback (make a local alias val replyTo = sender())
not respecting naming conventions (capital letters, etc)
using deprecated "actorFor". you have to use actorSelection (see akka docs)

You're exiting without waiting for a response.
implicit val timout = Timeout(50 seconds)
val f: Future[Any] = clientActor ? UserPass("s","a")
f.onSuccess {
case GO => println("Got something back from Client actor") //Doesnt happen!
}
That sets up a handler for a callback, but then your program just exists.
Minimally, you could scala.concurrent.Await.result(f)

Related

Spray reverse proxy: keep transferring data after client has disconnected

I'm trying to implement a reverse HTTP proxy with Spray/Akka, but runs into trouble. I found that under some circumstances, my proxy server will keep receivving data from upstream server even after the client has disconnected.
Here's how I implement my Spray proxy directive (just a little modification to bthuillier's implementation):
trait ProxyDirectives {
private def sending(f: RequestContext ⇒ HttpRequest)(implicit system: ActorSystem): Route = {
val transport = IO(Http)(system)
ctx ⇒ transport.tell(f(ctx), ctx.responder)
}
/**
* Re-shape the original request, to match the destination server.
*/
private def reShapeRequest(req: HttpRequest, uri: Uri): HttpRequest = {
req.copy(
uri = uri,
headers = req.headers.map {
case x: HttpHeaders.Host => HttpHeaders.Host(uri.authority.host.address, uri.authority.port)
case x => x
}
)
}
/**
* proxy the request to the specified uri
*
*/
def proxyTo(uri: Uri)(implicit system: ActorSystem): Route = {
sending(ctx => reShapeRequest(ctx.request, uri))
}
}
This reverse proxy will work well if I put one proxy layer between the client and the server (that is, client <-> proxyTo <-> server), but it will have trouble if I put two layers between the client and the server. For example, if I've got the following simple Python HTTP server:
import socket
from threading import Thread, Semaphore
import time
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
class MyHTTPHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
self.send_response(200)
self.send_header('Transfer-Encoding', 'chunked')
self.end_headers()
for i in range(100):
data = ('%s\n' % i).encode('utf-8')
self.wfile.write(hex(len(data))[2:].encode('utf-8'))
self.wfile.write(b'\r\n')
self.wfile.write(data)
self.wfile.write(b'\r\n')
time.sleep(1)
self.wfile.write(b'0\r\n\r\n')
class MyServer(ThreadingMixIn, HTTPServer):
def server_bind(self):
HTTPServer.server_bind(self)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
def server_close(self):
HTTPServer.server_close(self)
if __name__ == '__main__':
server = MyServer(('127.0.0.1', 8080), MyHTTPHandler)
server.serve_forever()
Which basically does nothing but open a chunked response (for long-term running, so that we can exam the issues). And if I chain two layers of proxies in the following way:
class TestActor(val target: String)(implicit val system: ActorSystem) extends Actor
with HttpService
with ProxyDirectives
{
// we use the enclosing ActorContext's or ActorSystem's dispatcher for our Futures and Scheduler
implicit private def executionContext = actorRefFactory.dispatcher
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
val serviceRoute: Route = {
get {
proxyTo(target)
}
}
// runs the service routes.
def receive = runRoute(serviceRoute) orElse handleTimeouts
private def handleTimeouts: Receive = {
case Timedout(x: HttpRequest) =>
sender ! HttpResponse(StatusCodes.InternalServerError, "Request timed out.")
}
}
object DebugMain extends App {
val actorName = "TestActor"
implicit val system = ActorSystem(actorName)
// create and start our service actor
val service = system.actorOf(
Props { new TestActor("http://127.0.0.1:8080") },
s"${actorName}Service"
)
val service2 = system.actorOf(
Props { new TestActor("http://127.0.0.1:8081") },
s"${actorName}2Service"
)
IO(Http) ! Http.Bind(service, "::0", port = 8081)
IO(Http) ! Http.Bind(service2, "::0", port = 8082)
}
Use curl http://localhost:8082 to connect to the proxy server, and you will see the Akka system keeps transferring data even after curl has been killed (you may turn on the logs of DEBUG level to see details).
How can I deal with this problem? Thanks.
Well, it turns out to be a very complex problem, while my solution takes nearly 100 lines of codes.
Actually, the problem does not only exist when I'm stacking two layers of proxies. When I'm using one layer proxy, the problem does exist, but no log is printed, so I've not aware of this problem before.
The key problem is that while we use IO(Http) ! HttpRequest, it is actually a host-level API from spray-can. The connections of host-level APIs are managed by Spray HttpManager, which is not accessible by our code. Thus we can do nothing with that connection, unless we send a Http.CloseAll to IO(Http), which will cause all the upstream connections to be closed.
(If anyone knows how to get the connection from HttpManager, please tell me).
We have to use connection-level APIs from spray-can to serve for this situation. So I've come up with something like this:
/**
* Proxy to upstream server, where the server response may be a long connection.
*
* #param uri Target URI, where to proxy to.
* #param system Akka actor system.
*/
def proxyToLongConnection(uri: Uri)(implicit system: ActorSystem): Route = {
val io = IO(Http)(system)
ctx => {
val request = reShapeRequest(ctx.request, uri)
// We've successfully opened a connection to upstream server, now start proxying data.
actorRefFactory.actorOf {
Props {
new Actor with ActorLogging {
private var upstream: ActorRef = null
private val upstreamClosed = new AtomicBoolean(false)
private val clientClosed = new AtomicBoolean(false)
private val contextStopped = new AtomicBoolean(false)
// Connect to the upstream server.
{
implicit val timeout = Timeout(FiniteDuration(10, TimeUnit.SECONDS))
io ! Http.Connect(
request.uri.authority.host.toString,
request.uri.effectivePort,
sslEncryption = request.uri.scheme == "https"
)
context.become(connecting)
}
def connecting: Receive = {
case _: Http.Connected =>
upstream = sender()
upstream ! request
context.unbecome() // Restore the context to [[receive]]
case Http.CommandFailed(Http.Connect(address, _, _, _, _)) =>
log.warning("Could not connect to {}", address)
complete(StatusCodes.GatewayTimeout)(ctx)
closeBothSide()
case x: Http.ConnectionClosed =>
closeBothSide()
}
override def receive: Receive = {
case x: HttpResponse =>
ctx.responder ! x.withAck(ContinueSend(0))
case x: ChunkedMessageEnd =>
ctx.responder ! x.withAck(ContinueSend(0))
case x: ContinueSend =>
closeBothSide()
case x: Failure =>
closeBothSide()
case x: Http.ConnectionClosed =>
closeBothSide()
case x =>
// Proxy everything else from server to the client.
ctx.responder ! x
}
private def closeBothSide(): Unit = {
if (upstream != null) {
if (!upstreamClosed.getAndSet(true)) {
upstream ! Http.Close
}
}
if (!clientClosed.getAndSet(true)) {
ctx.responder ! Http.Close
}
if (!contextStopped.getAndSet(true)) {
context.stop(self)
}
}
} // new Actor
} // Props
} // actorOf
} // (ctx: RequestContext) => Unit
}
The code is little long, and I doubt there should be some more clean and simple implementation (actually I'm not familiar with Akka). Nevertheless, this code works, so I put this solution here. You may post your solution to this problem freely, if you've found some better one.

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)

Attach a callback to run after a scala spray server successfully sends a response

I want to do something like the following:
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
complete {
// has a response indicating "OK"
// also, restarts the network interface
handleConfig(config)
}
}
}
}
}
}
The problem is that handleConfig reinitializes the network interface, so remote hosts accessing this endpoint never receive their response.
One way to solve this is to run handleConfig in a separate thread and complete the request immediately with some response like "OK". This isn't a good solution however because it introduces a race condition between the future and the request completion (also, it always fails if the future is executed in a "same thread" execution context).
Therefore, an ideal solution would be to attach a callback to a "write response" future and perform the network re-initialization there, after the response has been successfully sent. Is there a way to achieve this in the spray framework?
As a simple example of the race condition, consider the following two examples:
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
ctx =>
ctx.complete("OK")
System.exit(0) // empty response due to this executing before response is sent
}
}
}
}
}
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
import system.dispatcher
startServer(interface = "0.0.0.0", port = 8080) {
post {
path("configNetwork") {
entity(as[Config]) { config =>
ctx =>
ctx.complete("OK")
Thread.sleep(1000)
System.exit(0) // response is "OK" because of the sleep
}
}
}
}
}
You can use the withAck method on HttpResponse to receive notification when the response is sent on the wire. Here is a sketch of what that would look like in code, though I suspect if you're reconfiguring the low-level network interface then you will need to actually close the http listener and rebind.
case object NetworkReady
class ApiManager extends HttpServiceActor with Directives {
override def receive: Receive = networkReady
private def networkReady: Receive = runRoute(routes) orElse networkManagementEvents
private def networkManagementEvents: Receive = {
case Config =>
context.become(reconfiguringNetwork)
magicallyReconfigureNetwork pipeTo self
}
private def magicallyReconfigureNetwork: Future[NetworkReady] = ???
private def reconfiguringNetwork: Receive = {
case NetworkReady => context.become(networkReady)
case _: HttpRequest => sender() ! HttpResponse(ServiceUnavailable)
case _: Tcp.Connected => sender() ! Tcp.Close
}
private def routes: Route = {
(post & path("configNetwork") & entity(as[Config])) { config =>
complete(HttpResponse(OK).withAck(config))
}
}
}

Akka ZeroMQExtension Req socket sending procedure

I have a tiny Rep server that I'm able to successfully send a message to using the below code:
import akka.actor.Actor
import akka.zeromq._
import org.zeromq.ZMQ
import akka.util.ByteString
class Sender extends Actor {
override def preStart(): Unit = {
val context = ZMQ.context(1)
val socket = context.socket(ZMQ.REQ)
socket.connect ("tcp://127.0.0.1:1234")
socket.send("test".getBytes(), 0)
println("Sent!")
//val reqSocket = ZeroMQExtension(context.system).newReqSocket(
// Array(Connect("tcp://127.0.0.1:1234")))
//reqSocket ! ZMQMessage(ByteString("test"))
}
def receive : Receive = {
case _ =>
}
}
However, if I use the commented out part instead (and comment out everything else in the def), then the server is unable to detect any message. Am I supposed to use some other sort of procedure to send the message when using ZeroMQExtension? I'm using ZeroMQ version 2.2.0.
For reference, here is the server code:
import akka.actor._
import akka.zeromq._
class ReplyActor extends Actor {
def receive = {
case _ =>
println("Received something!")
}
}
object Replyer extends App {
val system = ActorSystem("zmq")
val serverSocket = ZeroMQExtension(system).newRepSocket(
Array(Bind("tcp://127.0.0.1:1234"),
Listener(system.actorOf(Props[ReplyActor]))))
}
In your server, switch the case _ => code to the following:
case x => println("msg is: " + x)
You will then find that your ReplyActor and your Sender end up getting a 'Connecting' message. I have found that you can't send/receive anything until you get this 'Connecting' message. It just means that you are actually connected. Once you get that, then you can start sending / receiving messages.

How do I get the absolute remote actor url from inside the actor?

I want to do do something like this .
Actor A :
actorB ! "info"
def receive()={
case _ => {
println("Remote address is "+ _)
}
}
Actor B : (this deployed remotely )
def receive()={
case "info" => {
sender tell self.path.address.toString
}
}
I want it to return me the string akka://10.4.20.40:2555/slave/user/slaverunner . But what I get is just akka://slave. How do I get the remote host and port? . The properties host,port and hostport on the address object dont return anything
If you call
sender.path.toString
in actor A you will get the address of the sender. So you don't need to pass the address to another actor system as long as you can send a message to it.
Akka will not give you a remote path for an actor in the local system, which is why self.path.address.toString in actor B won't work.
If you really want to send the host and port from B to A then you'll need to get access to a RemoteActorRefProvider via the ExtendedActorSystem. The official way to do that is through an Extension. For example:
class MyExtensionImpl(system: ExtendedActorSystem) extends Extension {
def address = system.provider match {
case rarp: RemoteActorRefProvider => rarp.transport.address
case _ => system.provider.rootPath.address
}
}
object MyExtension extends ExtensionKey[MyExtensionImpl]
val address = MyExtension(system).address
And that will give you the exact address that you need to communicate remotely with B.
(note this code works with Akka 2.0.x. In 2.1.x you can avoid going through RemoteActorRefProvider by using system.provider.getDefaultAddress)
In Akka if you are using constructing actor addresses this way for use with actorFor then you need to make sure the hostnames match exactly.
For example if the ActorSystem thinks the host is foo.bar.com, then it will ignore the messages sent by a remote host to actorFor("akka://slave#foo:2555/user/slaverunner")
For recent akka versions (2.1+) use this:
class RemoteAddressExtensionImpl(system: ExtendedActorSystem) extends Extension {
def address = system.provider.getDefaultAddress
}
object RemoteAddressExtension extends ExtensionKey[RemoteAddressExtensionImpl]
val remoteAddr = RemoteAddressExtension(context.system).address
val remotePath = self.path.toStringWithAddress(remoteAddr))
the remotePath is what you were looking for:
I want it to return me the string
akka://10.4.20.40:2555/slave/user/slaverunner
For more recent akka versions (2.5.3) use this:
class RemoteAddressExtensionImpl(system: ExtendedActorSystem) extends Extension {
def address = system.provider.getDefaultAddress
}
object RemoteAddressExtension extends ExtensionId[RemoteAddressExtensionImpl]
with ExtensionIdProvider {
override def lookup = RemoteAddressExtension
override def createExtension(system: ExtendedActorSystem) = new RemoteAddressExtensionImpl(system)
override def get(system: ActorSystem): RemoteAddressExtensionImpl = super.get(system)
}
I got this wrapped String:
ConfigString("akka://serversys#127.0.0.1:2552")
Using system.settings:
context.system.settings.config.getValue("akka.actor.deployment.\"/root\".remote")
Actor was a "root" actor, in a fabric scheme suggested by Viktor Klang in a mail group discussion concerning "determine if actor is dead". It was created like this:
lazy val rootActor = actorSystem.actorOf(Props[Root], "root")
from ActorSystem that was created like this:
lazy val actorSystem = ActorSystem("serversys", ConfigFactory.parseString("""
akka {
loglevel = "DEBUG"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
}
}
serverconf {
include "common"
akka {
actor {
deployment {
/root {
remote = "akka://serversys#127.0.0.1:2552"
}
}
}
remote {
netty {
hostname = "127.0.0.1"
port = 2552
}
}
}
}
""").getConfig("serverconf"))
Tested in Scala 2.9.2 REPL with Akka 2.0.3 that I still use.
Unwrap actual value using Config docs here: http://typesafehub.github.com/config/v0.3.0/
See public address of an actorsystem.
Read the full discussion, and note that "normally" you don't need to grab the host/port.