This lightning talk discusses replacing threads with actors for making concurrent service calls in web applications. It describes how actors provide an alternative concurrency model that can be more resource efficient than threads. The talk shows how to implement a master/worker pattern using Akka actors to dispatch work to remote services in parallel from a servlet, collecting the responses to render the page. Key advantages of the actor model include easier reasoning about concurrency and built-in supervision to restart failed actors.
What's New in Teams Calling, Meetings and Devices March 2024
REPLACING THREADS WITH ACTORS FOR CONCURRENT WEB APPLICATION SERVICE CALLS
1. LIGHTNING TALK
REPLACING THREADS WITH ACTORS FOR
CONCURRENT WEB APPLICATION SERVICE
CALLS
Martin Anderson
LJC Open Conference 2011
2. CONCURRENT SERVICE CALLS
• Most web pages need data from multiple
sources
• Scatter/Gather approach to perform all the
service calls in parallel then rendering the
responses
2
3. CONCURRENCY MODEL
dispatch service
calls
rendering controller’s job
starts early done
3
4. THREADS
• Well understood (if not always well
implemented!)
• java.util.concurrent library eases the pain
• Resource usage?
4
5. ACTORS
• Been around since 1973
• Erlang popularised usage
• Actor library for Java? Akka!
• Most strongly associated with Scala but
has a Java API
5
6. USING AN EXECUTOR SERVICE IN A SERVLET
public void doGet(HttpServletRequest request, HttpServletResponse
response) throws…{
List<MyServiceCallable> myServiceCallables =
new ArrayList<MyServiceCallable>();
for (int ii = 0; ii < 10; ii++) {
myServiceCallables.add(new MyServiceCallablle(remoteClient));
}
List<Future<String>> futures =
executorService.invokeAll(myServiceCallables);
…
for (Future future : futures) {
writer.write( future.get() );
}
…
}
6
7. USING AKKA ACTORS IN A SERVLET
public void doGet(HttpServletRequest request, HttpServletResponse
response) throws…{
Works works = new Works();
for (int ii = 0; ii < 10; ii++) {
works.add(new Work()));
}
masterActor.tell(works);
List<Future<String>> futures = works.getFutures();
…
for (Future future : futures) {
writer.write( future.get() );
}
…
}
7
8. SO WHAT’S IN AN ACTOR 1?
public class MasterActor extends UntypedActor {
private ActorRef router;
static class LoadBalancer extends UntypedLoadBalancer {
private final InfiniteIterator<ActorRef> workers;
public LoadBalancer(ActorRef[] workers) {
this.workers =
new CyclicIterator<ActorRef>(asList(workers));
}
public InfiniteIterator<ActorRef> seq() {
return workers;
}
}
8
9. SO WHAT’S IN AN ACTOR 2?
public MasterActor(final int numWorkers, final RemoteClient
remoteClient) {
// create an array of started workers
final ActorRef[] workers = new ActorRef[numWorkers];
for (int i = 0; i < numWorkers; i++) {
workers[i] = actorOf(
new UntypedActorFactory() {
public UntypedActor create() {
return new WorkerActor(remoteClient);
}
})
.start();
}
// wrap all the workers with a load-balancing router
router = actorOf(new UntypedActorFactory() {
public UntypedActor create() {
return new LoadBalancer(workers);
}
}).start();
}
9
10. SO WHAT’S IN AN ACTOR 3?
// message handler in MasterActor
public void onReceive(Object message) {
if (message instanceof Works) {
for (Work work : ((Works) message).getAll()) {
router.tell(work);
}
}
}
10
11. SO WHAT’S IN AN ACTOR 4?
public class WorkerActor extends UntypedActor {
RemoteClient remoteClient;
public WorkerActor(RemoteClient remoteClient) {
this.remoteClient = remoteClient;
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof Work) {
Work work = (Work) message;
remoteClient.execute(work);
}
}
}
11
12. SO WHAT’S IN OUR REMOTECLIENT?
public void execute(final Work work) throws IOException {
ContentExchange exchange = new ContentExchange() {
protected void onResponseComplete() throws IOException {
super.onResponseComplete();
String responseContent = this.getResponseContent();
work.setFutureResponse(responseContent);
}
};
//set the method and the url
exchange.setMethod("GET");
exchange.setURL(work.getUrl());
// start the exchange
httpClient.send(exchange);
}
12
13. SO WHY USE AKKA/ACTORS
• More resource efficient for certain jobs
• Easier for mere mortals to reason with
• Nested supervisors can restart failed actors
• Supports STM if required
13