Akka 2.4.1 Java API here. I don't have the bandwidth right now to learn Scala, so I would ask that code examples here also use the Java API.
I have an existing ActorSystem that is chock full of asynchronous actors and which works beautifully. I now have a need to reuse this actor system in a synchronous context like so:
// Groovy pseudo-code; NOT inside the actor system here!
ComputationRequest request = new ComputationRequest(1, 7, true)
MasterActor master = actorSystem.get(...)
ComputationResult result = actorSystem.tell(master, request)
Nowhere in Akka's documentation do I see a clear example of sending equests into an actor system (from the outside) and then retrieving the results. Could I use Futures here perhaps? What's the standard way of handling this pattern in Akka (code samples)?
There is the ask pattern does what you want. It expects the target actor to "return" a response by telling it to getSender(). You'll get a Future for the response and can work with that (blocking, if you must).
import akka.dispatch.*;
import scala.concurrent.ExecutionContext;
import scala.concurrent.Future;
import scala.concurrent.Await;
import scala.concurrent.Promise;
import akka.util.Timeout;
ComputationRequest request = new ComputationRequest(1, 7, true);
MasterActor master = actorSystem.get(...);
Timeout timeout = new Timeout(Duration.create(5, "seconds"));
Future<Object> future = Patterns.ask(master, request, timeout);
ComputationResult result = (ComputationResult) Await.result(future, timeout.duration());
You can inject a callback, wrapped into an actor, into akka system and use it as a "sender" with tell mechanics. Full example:
import akka.actor.*;
import akka.dispatch.Await;
import akka.dispatch.Future;
import akka.pattern.Patterns;
import akka.util.Timeout;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) throws Exception {
// init your akka system
final ActorSystem system = ActorSystem.create("Cambria");
final ActorRef worker = system.actorOf(new Props(Worker.class), "worker");
worker.tell(new Work(10));
// make callback
final Consumer<Object> callback = a -> System.out.println("from the other side: " + a);
// wrap call back into sub-actor
class TheSpy extends UntypedActor {
#Override
public void onReceive(final Object message) throws Exception {
callback.accept(message);
}
}
// inject callback into the system
final ActorRef theSpy = system.actorOf(new Props(TheSpy::new), "spy");
// find actor you want to hack
final ActorSelection found = system.actorSelection("/user/worker");
// send it a message and observe using callback)
found.tell(new Work(20), theSpy);
final Timeout timeout = new Timeout(5, TimeUnit.SECONDS);
final Future<Object> future = Patterns.ask(worker, new Work(30), timeout);
final Work result = (Work) Await.result(future, timeout.duration());
System.out.println(result);
system.shutdown();
system.awaitTermination();
}
}
public class Worker extends UntypedActor {
public void onReceive(Object message) {
if (message instanceof Work) {
Work work = (Work) message;
System.out.println("work = " + work);
getSender().tell(new Work(work.getStart() + 1));
} else {
unhandled(message);
}
}
}
public class Work {
private final int start;
public Work(int start) {
this.start = start;
}
public int getStart() {
return start;
}
#Override
public String toString() {
return "Work{" +
"start=" + start +
'}';
}
}
Related
I have a model:
package com.example.asyncmethod;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class User {
private String name;
private String blog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBlog() {
return blog;
}
public void setBlog(String blog) {
this.blog = blog;
}
#Override
public String toString() {
return "User [name=" + name + ", blog=" + blog + "]";
}
}
a service call like below
package com.example.asyncmethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
#Service
public class GitHubLookupService {
private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);
private final RestTemplate restTemplate;
public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
#Async
public CompletableFuture<User> findUser(Integer user) throws InterruptedException {
logger.info("Looking up " + user);
String url = String.format("https://api.github.com/users/%s", user);
User results = restTemplate.getForObject(url, User.class);
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
Async runner class:
package com.example.asyncmethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
#Component
public class AppRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);
private final GitHubLookupService gitHubLookupService;
public AppRunner(GitHubLookupService gitHubLookupService) {
this.gitHubLookupService = gitHubLookupService;
}
#Override
public void run(String... args) throws Exception {
// Start the clock
long start = System.currentTimeMillis();
// in real use case before the for loop I will have a database call to get the number of records and based on the size I need to call that many number of times.
for(int i=0; i <= 10; i++){
CompletableFuture<User> page1 = gitHubLookupService.findUser(1);
}
// Wait until they are all done
CompletableFuture.allOf(page1).join();
// Print results, including elapsed time
logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
logger.info("--> " + page1.get());
}
}
So in the above for loop, I might call the findUser as per the number of records returned by DB.
Also in the future I might need to add few more async calls like findOrders, findInventory, findAccess etc which will call another services over http.
Considering the async call may succeed for one record and fail for another record, how can I approach here to call the async in the best possible way?
If you want to realize complete advantage of Async calls, I will suggest to avoid using CompletableFuture.get() or CompletableFuture.join().
Using this calls blocks your main thread till the time all the tasks (as part of CompletableFuture.allOf() ) are completed.
Instead you can use various functions that are provided to run a Lamba function on completion of all the futures (Also allows to handle failure in any of the Future).
Please refer the Java Docs for more details and check which method is more convenient in your implementation:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
I'm attempting to follow best practices for sending messages between Akka actors and reading the following :
https://github.com/akka/akka/blob/v2.6.15/akka-docs/src/test/java/jdocs/actor/ActorDocTest.java#L101-L129
https://doc.akka.io/docs/akka/current/actors.html
It seems best practice is to define a static class that contains the message to be sent.
From reading the docs I've defined the following :
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.persistence.AbstractPersistentActor;
class RecoveryActor extends AbstractPersistentActor {
public static class Msg1 {}
public static class Msg2 {}
private final LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);
/**
* Handler that will be called on recovery.
* #return
*/
#Override
public Receive createReceiveRecover() {
return receiveBuilder()
.match(Msg1.class, this::receiveMsg1)
.match(Msg2.class, this::receiveMsg2)
.build();
}
private void receiveMsg1(Msg1 msg) {
log.info("in receive message 1");
}
private void receiveMsg2(Msg2 msg) {
log.info("in receive message 2");
}
/**
* The normal receive method.
* #return
*/
#Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> s.equals("cmd"), s -> persist("evt", this::handleEvent))
.build();
}
private void handleEvent(String event) {
System.out.println("in handle event");
}
#Override
public String persistenceId() {
return "simple-accountant"; //best practice, the id should be unique.
}
}
But I'm unsure how to send messages using the Msg1 and Msg2
I've defined a new class to send a message :
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class ActorMessaging {
public static void main(String args[]){
final ActorSystem system = ActorSystem.create("example");
final ActorRef actor = system.actorOf(Props.create(RecoveryActor.class));
actor.tell(RecoveryActor.Msg1);
}
}
But it fails with compiler error :
java: cannot find symbol
symbol: variable Msg1
location: class recoverydemo.RecoveryActor
How can I send messages to the Actor of type Msg1?
Is this best practice for sending messages between actors, wrapping the messages in a custom class ?
actor.tell is accepting object. RecoveryActor.Msg1 is not that, it's not even class reference. How about trying to send instance of Msg1 like this:
actor.tell(new RecoveryActor.Msg1(), actorRef)
In order to "talk" to remote actor, you first need to obtain reference to it. That's usually done by ActorSystem
Silly mistake on my part. I overrode the incorrect method. I should have overridden createReceive instead :
public Receive createReceive() {
return receiveBuilder()
.match(RecoveryActor.Msg1.class, this::receiveMsg1)
.match(Msg2.class, this::receiveMsg2)
.build();
// return receiveBuilder()
// .match(String.class, s -> s.equals("cmd"), s -> persist("evt", this::handleEvent))
// .build();
}
I'm trying to test my code which uses the new Java 11 java.net.http.HttpClient.
In my production code I have something like this:
HttpClient httpClient = ... (gets injected)
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:1234"))
.POST(HttpRequest.BodyPublishers.ofByteArray("example".getBytes()))
.build();
return httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
And in my test I mock the HttpClient and so get the java.net.http.HttpRequest. How do I get/test its request body (= my "example")? I can call request.bodyPublisher() to get a HttpRequest.BodyPublisher, but then I'm stuck.
I've tried to cast it to jdk.internal.net.http.RequestPublishers.ByteArrayPublisher (which it actually is), but it won't compile because the corresponding package is not exported by the module.
I've checked the available methods in the HttpRequest.BodyPublisher-interface (.contentLength(), .subscribe(subscriber)) but I guess it's not possible with them.
I've tried to just create a new BodyPublisher and compare them using .equals(), but there is no real implementation of it and so the comparison was always false.
If you are interested in how body will look like in handler you can know it with help of HttpRequest.BodyPublisher Subscriber. We call subscription.request in order to receive all body items and collect them.
Our custrom subscriber:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Flow;
public class FlowSubscriber<T> implements Flow.Subscriber<T> {
private final CountDownLatch latch = new CountDownLatch(1);
private List<T> bodyItems = new ArrayList<>();
public List<T> getBodyItems() {
try {
this.latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return bodyItems;
}
#Override
public void onSubscribe(Flow.Subscription subscription) {
//Retrieve all parts
subscription.request(Long.MAX_VALUE);
}
#Override
public void onNext(T item) {
this.bodyItems.add(item);
}
#Override
public void onError(Throwable throwable) {
this.latch.countDown();
}
#Override
public void onComplete() {
this.latch.countDown();
}
}
Usage in the test:
#Test
public void test() {
byte[] expected = "example".getBytes();
HttpRequest.BodyPublisher bodyPublisher =
HttpRequest.BodyPublishers.ofByteArray(expected);
FlowSubscriber<ByteBuffer> flowSubscriber = new FlowSubscriber<>();
bodyPublisher.subscribe(flowSubscriber);
byte[] actual = flowSubscriber.getBodyItems().get(0).array();
Assert.assertArrayEquals(expected, actual);
}
I have an UDP server created by vertx.
The purpose of the server: it listen logs from another service , then according to message it make one of the following action:
1) Save message to db
2)Delete message from db according to id from message
3)Update message in db
My code is:
#AllArgsConstructor
public final class UdpServerVerticle extends AbstractVerticle {
private final Action action;
#Override
public void start() throws Exception {
final DatagramSocket socket = this.vertx.createDatagramSocket(new DatagramSocketOptions());
socket.listen(9000, "0.0.0.0", asyncRes -> {
if (asyncRes.succeeded()) {
socket.handler(packet -> {
final byte[] bytes = packet.data().getBytes(0, packet.data().length());
final String body = this.body(bytes);
this.action.choose(body);
});
} else {
System.out.println("ERROR");
}
});
}
#SneakyThrows
private String body(final byte[] bytes) {
return new String(bytes, "UTF-8");
}
}
Action class:
public final class DefaultAction implements Action {
private final ServerEvent onConnect;
private final ServerEvent onDisconnect;
private final ServerEvent onMatchBegin;
private final ServerEvent onMatchEnd;
#Autowired
public DefaultAction(#EventQualifier(event = EventTypes.CONNECT)final ServerEvent onConnect,
#EventQualifier(event = EventTypes.DISCONNECT)final ServerEvent onDisconnect,
#EventQualifier(event = EventTypes.MATCH_BEGIN)final ServerEvent onMatchBegin,
#EventQualifier(event = EventTypes.MATCH_END)final ServerEvent onMatchEnd) {
this.onConnect = onConnect;
this.onDisconnect = onDisconnect;
this.onMatchBegin = onMatchBegin;
this.onMatchEnd = onMatchEnd;
}
#Override
public void choose(final String body) {
if (this.diconnect(body)) {
this.onDisconnect.make(body);
} else if (this.connect(body)) {
this.onConnect.make(body);
} else if (this.gameBegin(body)) {
this.onMatchBegin.make(body);
} else if (this.gameOver(body)) {
this.onMatchEnd.make(body);
}
}
I badly know vertx (but i need to use it) what i know is that vertx use single thread to handle all my messages. I want to replace ServerEvent interface to
4 difference verticles and pass my message to appropriate verticle using event bus because of it's not good to block main verticle thread (UDP server) . Is it wisely to do replacement in my situation?
You misunderstand how VertX works. It's multithreaded, but has thread affinity, which is not the same as being single threaded.
As of your suggestion, that's the way to go. Put each of your actions into a separate verticle, and communicate with them using EventBus.
I am new to Akka and I am trying to start an Actor and send messages to that actor from various other actors. The receiver is called Hero and the senders are Messengers
Here is my Hero class
import akka.actor.UntypedActor;
public class Hero extends UntypedActor {
#Override
public void onReceive(Object arg0) throws Exception {
System.out.println("Received = " + arg0);
}
}
I start the Hero using the below code
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class TestHero {
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("testHero");
ActorRef master = system.actorOf(Props.create(Hero.class), "master");
master.tell("I am here", ActorRef.noSender());
}
}
When I run the above code I get the message "Received = I am Here".
Now I have my Messenger class's constructor as follows
private static ActorRef hero;
public Messenger() {
ActorSelection master = context().actorSelection("akka://localhost/user/serviceA/master");
hero = master.anchor();
}
When I print the hero object it is always null. What am I missing here? Is this the right way to search for an Actor. The point is these 2 actors will be running in 2 different JVMs.
I enabled remoting and modified the Messenger class as below.
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
public class Messenger extends UntypedActor {
private static ActorRef hero;
public Messenger() {
ActorSelection master = context().actorSelection("akka.tcp://testHero#127.0.0.1:2552/user/master");
System.out.println(master);
hero = master.anchor();
}
#Override
public void onReceive(Object arg0) throws Exception {
System.out.println("msg = " + arg0);
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("test");
ActorRef actor = system.actorOf(Props.create(Messenger.class), "msgnr");
System.out.println(actor.getClass() + " >> " + actor);
System.out.println(hero);
actor.tell("Hi", hero);
}
}
The output is given below
class akka.actor.RepointableActorRef >> Actor[akka://test/user/msgnr#-975452280]
null
ActorSelection[Anchor(akka://test/deadLetters), Path(/user/master)]
msg = Hi
How to wait till the Messenger is created so that hero actor ref is instantiated?
I solved the issue by adding an application.conf file to the Messenger also.