puniverse / comsat

Fibers and actors for web development
docs.paralleluniverse.co/comsat
Other
598 stars 103 forks source link

WebActor delegate to one or more actors and wait for response #35

Closed drkarl closed 9 years ago

drkarl commented 9 years ago

I have done a PoC for a project I'm considering using comsat. I have tried to use the Actors system, but there's not enough examples or documentation so I find myself trying to figure things up looking at existing classes, the comsat and quasar code and the examples.

Sorry but I'm new to the Actors model paradigm (I knew about actors before, since I've programmed in Scala and I've read about Akka actors a while ago but I haven't really used them. I know they're inspired by Erlang Actors).

Also I've read most of your blog posts in blog.paralleluniverse.co

This is the issue:

I have a WebActor, it receives HttpRequest messages fine and in the examples it uses this pattern

HttpRequest msg = (HttpRequest) message;
msg.getFrom().send(ok(self(), msg, body).build())

this works fine, of course.

The problem is that my WebActor delegates to one or many other actors (looking at the classes in Comsat I thought the appropriate in this case was that the other actors should be BehaviourActor, is that right?) which receive the WebDataMessage fine, perform the operations they need to perform and then use the msg.getFrom() technique to reply to the original WebActor.

All that is fine, the WebActor receives the messages from the BehaviorActors, but then it needs to send a message with the HttpResponse to the virtual actor ServletActor. How can I do this if I've lost the original HttpRequest to do a .getFrom()? I've done it storing a reference to the HttpRequest in the WebActor like this

private HttpRequest request;

and then doing

if (message instanceof WebDataMessage) {
    request.getFrom().send(ok(self(), request, ((WebDataMessage) message).getStringBody()).build());
}

but I don't think that is a good pattern, is it? There must be a better way...

Also, without actors, if my servlet depends on multiple async calls I can always use java Futures to wait for all the async calls to fulfill and then return a combined response, but with actors, after sending a message to several BehaviorActors how can I send a combined response with the message from all of the BehaviorActors? What's the proper way to do it?

circlespainter commented 9 years ago

In addition to the blog and the docs (they were enough for me to build quasar-stocks which you probably have already found through the comparison with Akka), there are examples here, here and here as well as the comsat-actors-servlet test actor.

In addition all of them should be up-to-date (Comsat 0.4.0). What additional docs and examples do you think could be helpful to get started with Web Actors?

As for behavioural actors: the choice is up to you. Even just basic behaviour functionality is quite convenient, so I don't see many reasons to do without it.

As for actor state management: a single actor's state is private to the actor and is never accessed concurrently so of course you can use it to store information there without much trouble; since by default a new web actor instance is associated with each client session (unless you decide otherwise) there's even little risk of mismatching requests and responses. But since you can just block your fiber in a receive from the mailbox after having decoded the web request and sent messages around, with the request and its "from" still in scope (or being able to pass them to auxiliary methods anyway), why can't you just do this? Why do you lose the original request object?

As for combining responses coming from several actors, you can simply perform a blocking receive and then do whatever you want with the messages once you got them all (or progressively while you receive them). If it's more convenient for you, you can also perform selective receives.

Quasar/Comsat are just this simple: just use normal blocking calls and regular control flow constructs with the peace of mind of knowing that they are being run in fibers, which are much cheaper than regular Java threads. Simply use the tools you already know from regular imperative programming, no need for additional constructs and concepts like in Akka and other async frameworks.

drkarl commented 9 years ago

Thanks @circlespainter, with your suggestions I've been able to remove the reference to the HttpRequest in the WebActor (although I'm relieved to know that an instance of a webactor is tied to a session, and so it would be private for the instance). I've changed it to do use a second receive in a loop to receive all the messages result from the actros to which I delegated the job, using a MessageProcessor to only process my type of message (I subclassed WebDataMessage) and not get any HttpRequest in the middle if there are more requests before I complete this one.

I still have many classes and patterns to understand though, but now it makes more sense. Also, if there are many consecutive requests from the same client it would be the same http session, right? In that case, if I am expecting WebDataMessages (or some other kind of message) from other actors, is there a change for mismatch and receive WebDataMessages from other requests or they would be handled by another webactor instance? If it's the same instance, would it be a good idea to subclass WebDataMessage and store in it an identifier for that particular request so that I know all those messages belong to the same request and so need to be included in the same response?

circlespainter commented 9 years ago

Java web sessions are typically identified through the JSESSIONID cookie value, then it's up to the client to manage cookies appropriately. For browsers I expect that at least requests from the same tab will be considered part of the same session but I can remember that different browsers used to have different behaviours w.r.t. e.g. multiple windows. Non-browser clients may behave again differently. If you write the client logic then you can decide how you want to manage them (have a look at the webactor SSE test to see how the session cookie is explicitly managed with AHC).

Since (by default) web actors are associated to sessions, If a client performs multiple HTTP requests within the same session then the corresponding WebDataMessage will be received by the same web actor instance. Why do you need an extra identifier? A single WebDataMessage's Java object identity is uniquely associated with the corresponding request already. You can just pass it around and/or use it as a key in data structures.

circlespainter commented 9 years ago

@drkarl Were my comments useful to you?

circlespainter commented 9 years ago

@drkarl closing for inactivity, you can always reopen.