Difference between Routing context and eventbus.send("address")

1,254 views
Skip to first unread message

Pratap Modi Nataraj

unread,
Jul 10, 2015, 1:06:40 PM7/10/15
to ve...@googlegroups.com
Hi,

Had a question about the Routing context ... is it a good practice to pass the routing context between the verticals using the eventbus.send()? What i am trying to achieve is a flow which uses the vertx.web routing context when the request comes in to the server and then it triggers events to other verticals through the eventbus using event.send(). This flow will eventually call many other verticals and then finally last vertical which will write out the output in to the http response and close the response.

So my question is, 
is this the right way to do  ?
 Does a all the verticals for a http request run only on a single thread attached to a single core?
 When I send the  Routing context object through the event bus does it clone it just use the reference ?
 If I have multiple vertical instances of some module running in different event loops(i mean core), how will all the instances use the same Routing context. If one instance is storing some data in the routing context and another instance is reading it how do i ensure thread safety?

Regards
Pratap

Tim Fox

unread,
Jul 11, 2015, 1:46:04 AM7/11/15
to ve...@googlegroups.com
I wouldn't recommend this.

RoutingContext is designed to be used in the verticle that created it. And Vert.x objects in general are optimised to be used from the same event loop where they were provided.

The verticle system provides an "actor-like" concurrency model - where we give you certain threading guarantees so you can write your code as single threaded and not have to worry about many problems of multi-threaded concurrency. Concurrent access to shared state is avoided.

What you're suggesting is very un-actor-like and very un-Vert.x-y.

If you want a flow where you chain handlers and the last one ends the response, I recommend you look at vertx-web which does exactly that.

hth
--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Pratap Nataraj

unread,
Jul 11, 2015, 5:23:26 AM7/11/15
to ve...@googlegroups.com
Thanks for the reply Tim.

Here is the main use case I am trying to solve, can you let me know if using vertx-web will help me solve it.

1. use case:
(A)----(B)

one verticle sending msg to another. I guess this is straightforward

2. dynamic forking of events: Here the number of forked events generated is determined at run time, In the below example. Verticle (A) will determine at run time how many events to send out in parallel and to which other verticles

(A) ------- (1)
      ------- (2)
        .
        .
      ------- (n)


3 Joining of events(Tricky one): Here I want to join outputs of many verticles in to one single verticle. In the below example verticles (1) to (n) will invoke same event (A). (A) should somehow know that all  it has received all the possible events back and only then trigger (B)

(1) ------- (A)-----(B)
(2) -------
        .
        .
(n) -------


Please help me suggest a good mechanism to achieve this.

Thanks & Regards
Pratap

Tim Fox

unread,
Jul 11, 2015, 5:33:51 AM7/11/15
to ve...@googlegroups.com
On 11/07/15 10:23, Pratap Nataraj wrote:
Thanks for the reply Tim.

Here is the main use case I am trying to solve, can you let me know if using vertx-web

Nothing in your post mentions web at all, you only refer to sending messages between verticles, so I don't see any relationship between what you describe and web interactions.


will help me solve it.

1. use case:
(A)----(B)

one verticle sending msg to another. I guess this is straightforward

Yes, looks like point to point messaging. The event bus supports point to point messaging.



2. dynamic forking of events: Here the number of forked events generated is determined at run time, In the below example. Verticle (A) will determine at run time how many events to send out in parallel and to which other verticles

(A) ------- (1)
      ------- (2)
        .
        .
      ------- (n)

Looks like pub/sub messaging. The event bus supports this too.




3 Joining of events(Tricky one): Here I want to join outputs of many verticles in to one single verticle. In the below example verticles (1) to (n) will invoke same event (A). (A) should somehow know that all  it has received all the possible events back and only then trigger (B)

(1) ------- (A)-----(B)
(2) -------

You could count events manually, or if you want to do more complex operations, Rx should help you here/

Pratap Nataraj

unread,
Jul 11, 2015, 7:52:25 AM7/11/15
to ve...@googlegroups.com
Hi Tim


Sorry I was not clear in giving the big picture. Here is a better problem definition. I do have http requests. All the cases I mentioned is happening together per http request. My code is like this

HttpServer server = vertx.createHttpServer();
Router router = router(vertx);
server.requestHandler(router::accept).listen(4080);

// This handler will be called for every request
router.route("/someurl").handler(routingContext -> {
    EventBus eb = vertx.eventBus();

    ARoutingContext aRoutingContext = new ARoutingContext(routingContext);
    eb.send(Events.A, aRoutingContext<// This object contains data and routing context>);
    
    // Lets say i will need same flow executed again. Just to simulate more complex flows cases
    eb.send(Events.A, aRoutingContext<// This object contains data and routing context>);
}

Note: I have multiple instances of all below mentioned verticles so 

Lets say verticle v(A) handles event A. 

This will now trigger events in this flow 
(v(A)    to    v(B)) 
{ v(B)           to         v(1), v(2) ..... v(N) }  - This is the forking case
{ v(1), v(2) .... v(N)     to             v(C)  } - This the joining case

The way I have implemented now is v(C) will have the routing context so it will write data and close by doing response.done() when all data for that req is flushed out. I need routing context object in v(C) cos I need to share data between different instances of v(C) and also need a way to track if all data for the request is flushed out or not only then close response object.  

One more way of implementing this flow was by using message.reply() mechanism. but in my flow since there are many forks and joins, I will end up getting same output multiple times in the route handler so I opted to passing routing context along the verticles.

Please suggest the best way to achieve this flow per request.

Thanks & Regards
Pratap

Jez P

unread,
Jul 11, 2015, 8:19:19 AM7/11/15
to ve...@googlegroups.com
A better approach would be for v(C) to notify the handler of what data to write out. The more sensible approach in my view is for handler to send a message to an instance of v(C) giving it enough information from the routing context to trigger the other verticles. v(C) should call the other verticles whose replies should give it information to know what to write out. v(C) compiles the replies from the other verticles into a unified response to send back to the handler. The handler then translates this into the actions it needs to do (write out the actual http response and end it). The only thing to write to the HttpResponse (or do anything which would change the state of the routing context) should be the handler itself, not one of the verticles whose processing it triggers.

I'm not sure if I've explained this clearly enough, the primary difference with your model is that v(C) should really act as a "supervisor" for the other processing - it triggers the other verticles and compiles the responses from them, and then sends a piece of data back to the handler which should make appropriate use of that data. 

What you're attempting to do looks like the road to hell, especially if you're sharing mutable state between multiple instances of v(C). I can't see that working out well at all, you're essentially throwing away all the benefits of the vert.x approach for no actual gain.

Pratap Nataraj

unread,
Jul 11, 2015, 8:44:55 AM7/11/15
to ve...@googlegroups.com
Hi,

Thanks for the suggestions. I agree, sharing mutable object across verticle instance is bad idea, I am trying to find a good solution. 

(v(A)    to    v(B)) 
{ v(B)           to         v(1), v(2) ..... v(N) }  - This is the forking case
{ v(1), v(2) .... v(N)     to             v(C)  } - This the joining case


In the above flow diagram I put up.  I can stop pass the context object and just change all verticles to do message reply.  Then v(A) is sort of like the supervisor that u mentioned. But the problem i see here is cos of the fork at v(B) and then join by at v(C) nature of the flow (like diamond structure flow) I think if v(C) will reply for every fork created by v(B). Finally v(A) the supervisor might receive duplicate data right ? 

Note: There can be multiple forks and joins in the flow I have taken a simple use case here.

Thanks & Regards
Pratap

Pratap Nataraj

unread,
Jul 11, 2015, 9:17:05 AM7/11/15
to ve...@googlegroups.com
Hi All

The join case is giving me a lot of trouble. 
{ v(1), v(2) .... v(N)     to             v(C), v(C)  } - This the joining case

If the v(C) verticle has many instances then the join per request is scattered among all the v(C) verticles.  How can I aggregate the data scattered across multiple instances of v(C) in to one single verticle with out using a shared mutable object ?


Regards
Pratap

Jez P

unread,
Jul 11, 2015, 10:02:19 AM7/11/15
to ve...@googlegroups.com
If v(c) distributes the processing via send to the other verticles, then if they reply the replies will all be received by the v(c) verticle instance which sent the messages out in the first place. This is what I meant by a "supervisor" verticle.

So your handler sends the originating data (which it determines from the routing context) to an address in a single message, with the v(c) verticles listening on that address. Only one of your v(c) verticles will receive that message (if you used send, rather than publish, to send it). That verticle instance then determines what other verticles it needs to involve and uses a send to the relevant addresses, with a reply handler whose responsibility is to aggregate the data. It will receive the replies, so can use the reply handling to aggregate. No other v(c) instance will receive those replies. 

In effect your joining verticle should also be your forking verticle. Then you don't have the problem you're describing. Why do you feel the need to make them different verticles?

In your scenario why not have v(B) join them as well as fork, by using the send/reply mechanism? 

Pratap Nataraj

unread,
Jul 11, 2015, 3:26:13 PM7/11/15
to ve...@googlegroups.com
Hi Jez

Thanks for the suggestion. I had actually built the system before exactly how u said, but later I kinda felt the call back code was getting a bit complicated and each call back needs to get data out from itself and update a common data. So was trying out other option. But now I am clear on what to do thank you. You guys are very helpful :)

Thanks & Regards
Pratap

Jez P

unread,
Jul 11, 2015, 3:32:25 PM7/11/15
to ve...@googlegroups.com
You might find using rx simplifies the callback stuff, depending on the details of your usecase (you can use flatMap on the observable to translate the returned data into the format you actually want to use for updating, then subscribe to the observable returned by flatMap - just a thought). 
Reply all
Reply to author
Forward
0 new messages