I want to do some server-side events (SSE) to a web app. I think I have all the SSE plumbing up and going. I now need to create a Source on the Akka HTTP side of the house.
I found you can do something like this:
val source = Source.actorRef(5, akka.stream.OverflowStrategy.dropTail)
What I want to do is somehow "publish" to this source, presumably by sending an actor a message. I see from the docs that this call creates Source<T,ActorRef>.
How can I get this ActorRef instance so I can send messages to it?
To obtain the materialized ActorRef from Source.actorRef, the stream has to be running. For example, let's say that you want to send the SSE payload data (in the form of a String) to this actor, which converts that data to ServerSentEvent objects to send to the client. You could do something like:
val (actor, sseSource) =
Source.actorRef[String](5, akka.stream.OverflowStrategy.dropTail)
.map(s => /* convert String to ServerSideEvent */)
.keepAlive(1.second, () => ServerSentEvent.heartbeat)
.toMat(BroadcastHub.sink[ServerSentEvent])(Keep.both)
.run()
// (ActorRef, Source[ServerSentEvent, NotUsed])
Now you can send messages to the materialized actor:
actor ! "quesadilla"
And use sseSource in your route:
path("events") {
get {
complete(sseSource)
}
}
Note that there is no backpressure with this approach (i.e., messages to the actor are fired-and-forgotten).
Related
I have a route that I am testing. I use stub://jms:queue:whatever to send/receive messages and extending CamelTestSupport for my test classes. I am having an issue with one of the routes that has a bean that uses an idempotent repo to store messages by "message id" for which it reads and stores the JMSMessageID property from exchange.
The problem I run into is that I can't figure out a way to set this property on messages sent on stubbed endpoints. Every time the method that requires this prop is called, the id returns null and i have to handle it as a null pointer. I can do this but the cleanest approach would be to just set the header on the test message. I tried includeSentJMSMessageId=true on endpoint, I tried using sendBodyAndHeader on producer and passing "JMSMessageID", "ID: whatever" in arguments, doesn't appear to work? I read that the driver/connectionfactory is supposed to set the header, but I'm not too familiar with how/where to do this. And since I am using a stubbed end points, I'm not creating any brokers/connectionfactories in my uts.
So dont stud out the JMS component replace it with a processor and then add the preferred JMSMessageID in the processor.
Something like this code:
#Test
void testIdempotency() throws Exception {
mockOut.expectedMinimumMessageCount(1);
//specify the route to test
AdviceWithRouteBuilder.adviceWith(context, "your-route-name", enrichRoute -> {
//replace the from with a end point we can call directly.
enrichRoute.replaceFromWith("direct:start");
//replace the jms endpoint with a processor so it can act as the JMS Endpoint.
enrichRoute.weaveById("jms:queue:whatever").replace().process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
//Set that ID to the one I want to test
exchange.getIn().setHeader("JMSMEssageID", "some-value-to-test");
}
});
// add an endpoint at the end to check if received a mesage
enrichRoute.weaveAddLast().to(mockOut);
});
context.start();
//send some message
Map<String,Object> sampleMsg = getSampleMessageAsHashMap("REQUEST.json");
//get the response
Map<String,Object> response = (Map<String,Object>)template.requestBody("direct:start", sampleMsg);
// you will need to check if the response is what you expected.
// Check the headers etc.
mockOut.assertIsSatisfied();
}
The JMSMessageID can only be set by the provider. It cannot be set by a client despite the fact that javax.jms.Message has setJMSMessageId(). As the JavaDoc states:
This method is for use by JMS providers only to set this field when a message is sent. This message cannot be used by clients to configure the message ID. This method is public to allow a JMS provider to set this field when sending a message whose implementation is not its own.
I have the following source queue definition.
lazy val (processMessageSource, processMessageQueueFuture) =
peekMatValue(
Source
.queue[(ProcessMessageInputData, Promise[ProcessMessageOutputData])](5, OverflowStrategy.dropNew))
def peekMatValue[T, M](src: Source[T, M]): (Source[T, M], Future[M]) {
val p = Promise[M]
val s = src.mapMaterializedValue { m =>
p.trySuccess(m)
m
}
(s, p.future)
}
The Process Message Input Data Class is essentially an artifact that is created when a caller calls a web server endpoint, which is hooked upto this stream (i.e. the service endpoint's business logic puts messages into this queue). The Promise of process message out is something that is completed downstream in the sink of the application, and the web server then has an on complete callback on this future to return the response back.
There are also other sources of ingress into this stream.
Now the buffer may be backed up since the other source may overload the system, thereby triggering stream back pressure. The existing code just drops the new message. But I still want to complete the process message output promise to complete with an exception stating something like "Throttled".
Is there a mechanism to write a custom overflow strategy, or a post processing on the overflowed element that allows me to do this?
According to https://github.com/akka/akka/blob/master/akkastream/src/main/scala/akka/stream/impl/QueueSource.scala#L83
dropNew would work just fine. On clients end it would look like.
processMessageQueue.offer(in, pr).foreach { res =>
res match {
case Enqueued => // Code to handle case when successfully enqueued.
case Dropped => // Code to handle messages that are dropped since the buffier was overflowing.
}
}
this might be a stupid question, but I need to ask since I havent found an answer to it yet. I have used the akka-http with routing with the typical routing pattern of a path with a
complete with a HttpRequest.
For instance:
~ path("reactJS") {
complete(
HttpResponse(entity = HttpEntity(ContentTypes.`text/html(UTF-8)`, Source.fromFile(reactJS).mkString))
)
}
However, I would like to have a separate actor that handles a file system and then, in my mind, I would like the server to pass the request over to the file handler actor. So my question would be, how would one naturally do a complete of a request with a dependency on another actor? I guess then the server would have a routing looking like:
~ path("patient" / IntNumber) { index =>
FileHandler ! index
}
class FileHandler extends Actor{
def receive = {
case msg:Int => sender() ! file handling
}
and the serving of the request would have to be a case in the receive method of the server, right?
looking into:How to respond with the result of an actor call?
I think your best bet is to use the ask pattern (?) and then use the onComplete directive within your routing tree to handle the Future that comes back from the ask. Taking your example, and modifying it a bit to show how you could leverage ask is shown below:
path("patient" / IntNumber) { index =>
import akka.pattern.ask
implicit val timeout = akka.util.Timeout(10 seconds)
val fut = (fileHandlerActor ? index).mapTo[String]
onComplete(fut){
case util.Success(fileData) =>
complete(HttpResponse(entity = HttpEntity(
ContentTypes.`text/html(UTF-8)`, fileData))
case util.Failure(ex) =>
complete(HttpResponse(StatusCodes.InternalServerError))
}
}
The assumption here is that your actor is responding with a String that is to become the HTTP response entity. Also, that timeout is a requirement of using ask, but you could very easily define it elsewhere in your code, as long as it's in scope here.
Provided I'm getting the name of an actor as an input parameter, say from a call to http://example/restservices/{actorname}.
What would be the preferred way to get a reference to an actor:
Akka.system.actorOf(Props[MyActor], name = actorname) ! SOMETHING("some1")
or
Akka.system.actorSelection("/user/"+actorname) ! SOMETHING("some1")
?
The method you need to use depends on whether you have an existing Actor which can handle your message or not. Using actorOf in your case i risky because you provide a name for an actor and by the design you can't have two actor with the same name in on system. As well as using actorSelection, it won't throw any error if actor doesn't exists, but the message won't be processed at all, it would go into DeadLetters mailbox. In this case the most reasonable solution would be to subscribe on DeadLetters with some existing actor -
system.eventStream.subscribe(actorRed, classOf[DeadLetter])
You can intercept DeadLetter in your receive method:
def receive = {
case DeadLetter(msg, from, to) =>
// process message
}
With this you might use actorSelection and when you receive a DeadLetter create an actor, then actorSelection should work as expected
Lets say I ask (?) the same actor for two responses.
It stores the sender for later.
Later, it gets messages back to go to the senders. We get the right sender (the one hashed to the message) but how does Akka know which message the response is for?
Is there something in the ActorRef that indicates which message each response is for?
Is it the 'channel'?
I'd like to understand the underlying technology better.
I'll try to read the source at the same time but I think this is a really good question.
Code example:
class TestActor
[...]
def onReceive = {
case r: MessageToGoOut ⇒
messageId += 1
val requestId = clientConnectionId + messageId
senders += (requestId -> sender) //store sender for later
anotherActor ! WrappedUpMessage(requestId, MessageOut))
case m: MessageToGoBackToSender ⇒
val requestId = m.requestId
senders.get(requestId) map { client ⇒
client ! Response(m.message)
senders -= requestId
}
}
val futures = for(i <- 1 to 100) yield testActor ? new MessageToGoOut ("HEYO!" + i)
Now how does akka ensure the messages get back to the right actor??
Every Actor has a path. From inside of the Actor, you could say:
context.path
From outside an Actor, if you had an ActorRef, you could just say:
ref.path
This path if the address of that individual actor instance, and it's how I believe the internal routing system routes messages to the mailboxes for actors instances. When you are outside of an Actor, like you are when you are looping and sending messages in your example, when you use ask (the ?), a temporary Actor instance is started up so that when the Actor that received the message needs to response, it has a path to respond to. This is probably a bit of an oversimplification, and it might not be the level of detail that you are looking for, so I apologize if I missed the gist of your question.
Also, the sender var in an Actor is an ActorRef, thus it has a path so you can route back to it.
When a Future is created, akka creates a temporary (and addressable) Actor that is basically servicing that Future. When that temporary Actor sends to another Actor, its ActorRef is transmitted as the sender. When the receiving actor is processing that specific message, the sender var is set to the ActorRef for that temp actor, meaning that you have an address to respond to. Even if you decide to hold on to that sender for later, you still have an address to send back to and eventually complete the Future that the temporary actor is servicing. The point is, as long as you have an ActorRef, whether it's a request or a response, all it's doing is routing a message to the path for that ActorRef.
Ask (?) and tell (!) really aren't much different. Ask is basically a tell where the sender is expecting the receiver to tell a message back to it.