Vertx-web: Routes established by 2 different Verticles result in failure due to apparent round robin routing.

Created on 22 Jun 2015  Â·  25Comments  Â·  Source: vert-x3/vertx-web

Let's say I have the following situation:

I have 2 Verticles, each of which wants to establish a different route under the same web server address. They can each create the HttpServer at that address and Vertx will transparently share a single server between them. However, the same is not true for any Routers they may create. They may chose to either:

  1. Each create their own Router and define routes independently.
  2. Use the Vertx shared data map to create a single Router which they share and create their routes under that.

Option 1 doesn't work since when a request is received by the HttpServer it appears to choose between the 2 routers in a round robin fashion. This means that 1/2 the time instead of having the request processed you will instead get a response of <html><body><h1>Resource not found</h1></body></html>.

Option 2 doesn't work unless you are willing to ensure that the verticles will never use an isolating classloader (a complete discussion of this can be found here -- https://groups.google.com/forum/#!topic/vertx/PVf_734rr-w -- the most recent postings starting around June 7th). Also as @purplefox observes this approach isn't very "vertxy".

I haven't been around here long enough to know for sure, but to me option 1 is the most "vertxy" and is consistent with the way vertx handles the sharing of the Http server by multiple verticles. If that is shared then it seems intutive that the routers should be as well. If full sharing isn't possible, perhaps another solution would be to have the server continue to look for a router that can handle the request before giving up.

Anyway, at some level I'm trying to figure out how we're supposed to handle this. I want to have independent verticles that can contribute to the overall route tree and have the ability to dynamically add them without having to maintain some central definition of the routers.

Let me know if you need a gist to demonstrate the issue.

Most helpful comment

I know this is closed but I just want to comment that this behavior should be much better documented. I just spent at least 30 minutes puzzling over why my first two verticles seemed to be randomly 404'ing and other odd behavior when both of them were near identical copies of the example code provided.

And for the record, I agree with @sfitts - being able to code up a new verticle and deploy it into a server without any modification of existing code would be a big win. To a "newbie" seems to be the general spirit and philosophy of Vert.x, and one of its attractions. For example, one reason I'm exploring Vert.x is that I want people who write code in different languages to work on the same application. Now it seems like they're all going to have to work on code in the same language for at least this one small part. So it's quite odd to me to find this thread with experienced people in it saying that what they like to do is lump all the routing for their Verticles together in one class, when it seems to run counter some of the things that most make me want to use Vert.x!

All 25 comments

I'd recommend that you don't have different verticles with different
routes, instead have a single verticle class which has all the routes,
then scale it by creating more instances of that verticle.

On 22/06/15 05:25, Sean Fitts wrote:

Let's say I have the following situation:

I have 2 Verticles, each of which wants to establish a different route
under the same web server address. They can each create the HttpServer
at that address and Vertx will transparently share a single server
between them. However, the same is not true for any Routers they may
create. They may chose to either:

  1. Each create their own Router and define routes independently.
  2. Use the Vertx shared data map to create a single Router which they
    share and create their routes under that.

Option #1 https://github.com/vert-x3/vertx-web/pull/1 doesn't work
since when a request is received by the HttpServer it appears to
choose between the 2 routers in a round robin fashion. This means that
1/2 the time instead of having the request processed you will instead
get a response of |

Resource not found

|.

Option #2 https://github.com/vert-x3/vertx-web/issues/2 doesn't work
unless you are willing to ensure that the verticles will never use an
isolating classloader (a complete discussion of this can be found here
-- https://groups.google.com/forum/#!topic/vertx/PVf_734rr-w
https://groups.google.com/forum/#%21topic/vertx/PVf_734rr-w -- the
most recent postings starting around June 7th). Also as @purplefox
https://github.com/purplefox observes this approach isn't very "vertxy".

I haven't been around here long enough to know for sure, but to me
option #1 https://github.com/vert-x3/vertx-web/pull/1 is the most
"vertxy" and is consistent with the way vertx handles the sharing of
the Http server by multiple verticles. If that is shared then it seems
intutive that the routers should be as well. If full sharing isn't
possible, perhaps another solution would be to have the server
continue to look for a router that can handle the request before
giving up.

Anyway, at some level I'm trying to figure out how we're supposed to
handle this. I want to have independent verticles that can contribute
to the overall route tree and have the ability to dynamically add them
without having to maintain some central definition of the routers.

Let me know if you need a gist to demonstrate the issue.

—
Reply to this email directly or view it on GitHub
https://github.com/vert-x3/vertx-web/issues/140.

While I understand that position, it is unfortunate. To me one of the great strengths of Vertx is the ability to support dynamic loading and scaling of new Verticles and we hoped to leverage this to support the ability to expand/augment the API of our product. Need a new set of features -- create a verticle that provides those and the API to access them and then load that verticle into the system. The approach you're suggesting would require that we maintain a single, monolithic, centralized view of our REST API which runs directly counter to the modular, decentralized nature of Vertx overall.

We have actually done this by creating a router singleton that each verticle uses so they can be run independently or together. Are there going to be unexpected issues with that?

As long as all the verticles are loaded with the same classloader that loaded the singleton, you'll be fine. However, if you want to use something like the Maven verticle factory then it won't work. That factory uses an isolating classloader (so that verticles from multiple sources don't have their dependencies collide) and as a result you'll get different copies of the Router class (and everything in vertx-web).

Vertx core works around this for HttpServer by explicitly excluding its classes from isolation, but the same does not happen for vertx-web (or any of the non-core modules). That's why sharing of the HttpServer works as expected. That would be another solution (suggested in the referenced email thread) -- add vertx-web (and perhaps all vertx modules) to the exclusion list. You aren't going to want to run verticles based on different versions of Vertx, so the fact that you can now actually seems bad (since it will likely lead to behavioral problems).

Note that I haven't checked recently so it is possible this has already changed, but that was the behavior a few weeks ago.

Hmmm, ok, that could end up being an issue. They are all currently loaded by the same classloader but, I can see us wanting to use the verticle factory in the future. Thanks.

Don't forget Vert.x is unopinionated.

If you prefer to have different verticles managing different routes and sharing a router than you can do that.

Personally I would have a single verticle which had all routes, as I think it just makes things easier to scale (you can simply increase number of instances of that verticles). But that's just what I prefer, doesn't mean it's better.

BTW... having a single verticle doesn't mean it's monolithic. Remember you can still put your different routes in different classes and packages if you want as you would in any sane, well modularised application. Don't confuse how you modularise your code with how you choose your deployment units.

Also.. the maven verticle factory isolating classloader issue doesn't exist any more in master.

I'm going to close this as it's not really an issue, it's expected behaviour.

I know this is closed but I just want to comment that this behavior should be much better documented. I just spent at least 30 minutes puzzling over why my first two verticles seemed to be randomly 404'ing and other odd behavior when both of them were near identical copies of the example code provided.

And for the record, I agree with @sfitts - being able to code up a new verticle and deploy it into a server without any modification of existing code would be a big win. To a "newbie" seems to be the general spirit and philosophy of Vert.x, and one of its attractions. For example, one reason I'm exploring Vert.x is that I want people who write code in different languages to work on the same application. Now it seems like they're all going to have to work on code in the same language for at least this one small part. So it's quite odd to me to find this thread with experienced people in it saying that what they like to do is lump all the routing for their Verticles together in one class, when it seems to run counter some of the things that most make me want to use Vert.x!

@ssadedin Glad to hear I'm not the only one who thinks so ;).

Fortunately Vert.x is flexible enough that you can make it work. So after the experts decided it wasn't what they wanted I just went ahead and did it anyway (we have exactly the goal you do -- we want folks building independently without having to coordinate in one chunk of code).

We've learned a few things along the way that may help, so feel free to contact me directly if you'd like more details on our approach.

Thanks for the followup @sfitts - at the moment I'm perpetrating the egregious hack of putting this code into my main startup verticle (Groovy code):

 System.properties.put("vertx.router",router)

Then my other verticles just get it out of there so they can all wire up to the same router. I'm sure there's something horrifically wrong with this, but it's working for now. I straight away ran into trouble with classloaders when I tried to use singleton instances of classes, static properties or Vertx shared data maps, hence the hack of putting into the System properties. Hoping perhaps this helps others, and that perhaps if there are huge flaws in what I am doing, people can educate me about it.

You might want to look at using a local map from vertx.sharedData() to share the router (a bit less hacky since it is tied to the vertx runtime). You will have to create a wrapper to hold the router in this case, but that's simple enough (we created a class which let's us do this generically when we need).

@sfitts @ssadedin

You guys need to give those of use heading down this road some more info on these approaches :)

I just spent a weekend smacking my head against this problem, wondering why when I deploy multiple routers I get weird url behaviors.

I want to deploy multiple API surfaces, each in a different server with a different port. Can anyone comment on the best approach?

I have to agree that this type of thing is a key reason we started looking at vert.x in the first place.

@ssadedin

So it's quite odd to me to find this thread with experienced people in it saying that what they like to do is lump all the routing for their Verticles together in one class

I think you misunderstand what I said. No-one is saying you should put all your routes in one class. You can put your routes in any combination of classes that you want. You can have one team developing one set of routes and another team developing another set.

But when you come to deploying your verticles you can deploy them all in one verticle.

The number of verticle classes does not have a one-one correspondence with the number of classes that you develop your routes in.

I think this is where the confusion arises. People are using that the class they write their routes in has to be a verticle, it doesn't.

@MTyson If you want to deploy different services on different ports there shouldn't be an issue. Just create an http server for that host/port and create a router on that.

@sfitts >>> The approach you're suggesting would require that we maintain a single, monolithic, centralized view of our REST API which runs directly counter to the modular, decentralized nature of Vertx overall.

That doesn't follow. Just because you want to deploy all your routes in a single verticle doesn't mean they can't be maintained in multiple separate projects by different teams.

If you want all the routes hosted on the same web server than at packaging time you can pull in those projects as dependencies and add them to the router of your verticle.

The verticle is meant as a unit of deployment and scalability, it is not designed as a unit for code encapsulation.

@purplefox
Yes, actually, my problems were config related. After more refinement, spinning up multiple HttpServers, with distinct ports and their own api definitions works fine.

@purplefox
Thanks for the information so far.
But let me ask one more question.
You say:

That doesn't follow. Just because you want to deploy all your routes in a single verticle doesn't mean they can't be maintained in multiple separate projects by different teams.

The verticle is meant as a unit of deployment and scalability, it is not designed as a unit for code encapsulation.

But what if I want to update a service behind a route but I don't want to have a downtime for the other services?

what should I do? I have the same trouble with this issue.

Just my own personal view but if I had to change the logic behind a route,
I'd name this a new version of the app :)

I would put a load balancer in front of my Vert.x servers and redirect
traffic to the nodes with the new version.

2017-05-09 18:08 GMT+02:00 MerlinFeng notifications@github.com:

what should I do? I have the same trouble with this issue.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/vert-x3/vertx-web/issues/140#issuecomment-300214718,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABblti-IkaKFh87Ens50zmoAFUL_B1zRks5r4I9lgaJpZM4FImSy
.

I think it is a common use-case. every verticle focus on its business logic and share one server endpoint. putting a load balancer in front of servers is a very heavy solution.

Hi, I am new to Vertx and microservices in general. While doing my research I ran into this problem and I still dont understand the best practice for a very common usecase.

Say my application manages users and products, and I have a verticle for each. Now, i want a route to handle a call to get all users, and a route to handle a call to get all products.

Originally I did something like this:

public class UsersVerticle extends AbstractVerticle{
    public void start(){
        HttpServer server = vertx.createHttpServer();
        Router router = Router.router(vertx);
        router.route("/users").handler(this::getAllUsers);

        server.requestHandler(router::accept);
        server.listen(8080, result -> {...}
    }
}

public class ProductsVerticle extends AbstractVerticle{
    public void start(){
        HttpServer server = vertx.createHttpServer();
        Router router = Router.router(vertx);
        router.route("/products").handler(this::getAllProducts);

        server.requestHandler(router::accept);
        server.listen(8080, result -> {...}
    }
}

Of course this doesn't work, and I could solve this with a static router but that doesn't seem to be the best practice, and also could possibly create problems when deploying on, let's say, dockers.

My question is, should I somehow share the router instance between verticles, an if so - how?
Or am I supposed to have a single verticle accepting http requests and then using the event bus to get response from the appropriate verticle and send it back to the client?
Seems weird as it creates a lot of messages on the event bus, and I will have to update that 'route verticle' every time i want to add a handler to any of the verticles.

We do essentially what you started to gravitate towards. We have a Verticle that is responsible for deploying the HttpServer and configuring the routes. If/when we deploy multiple copies of that Verticle we ensure that all of them are configured identically. The one difference is that we configure the routes dynamically so we don't have a centralized "here are all my routes" method/file. It would be nice if Vert.x provided a bit more support for this pattern, but it isn't too hard to layer on top of what they do provide, so that's what we did.

Thanks for the reply.
I also looked at the blueprints (I missed those during my research somehow), and that cleared things up a bit.

Hello everyone,
I know that the subject is closed but the people who intervened are experienced with the creator of vert.x and I need your opinion.
in fact, I have a verticle in which I defined all the roads. And I have simple java classes that contain methods that I call in my verticle depending on the route. For example, my downloadFile() method is in the MyFile class like this:

 public class MyFile {

        public final void downloadFile(RoutingContext rc, Vertx vertx) {
            final HttpServerResponse response = rc.response();
            response.putHeader("Content-Type", "text/html");
            response.setChunked(true);

            rc.fileUploads().forEach(file -> {

                final String fileNameWithoutExtension = file.uploadedFileName();

                final JsonObject jsonObjectWithFileName = new JsonObject();
                 response.setStatusCode(200); 
                 response.end(jsonObjectWithFileName.put("fileName", fileNameWithoutExtension).encodePrettily());
            });
        }

       public final void saveFile(RoutingContext rc, Vertx vertx) {
                 //TODO
       }
    }

And I use this class in my verticle like this:

    public class MyVerticle extends AbstractVerticle{

            private static final MyFile myFile = new MyFile(); //my colleague tells me not to do that

            @Override
            public void start(Future<Void> startFuture) {
                final Router router = Router.router(vertx);
                final EventBus eventBus = vertx.eventBus();

                router.route("/getFile").handler(routingContext -> {
                    myFile.downloadFile(routingContext, vertx);
                });

        router.route("/saveFile").handler(routingContext -> {
                    myFile.saveFile(routingContext, vertx);
                });
            }
    }

To my surprise my friend tells me that we do not instantiate a class in a verticle and when I asked him why, he replied that it was becoming a stateful and I doubt what he tells me because I do not see how but as he is more experienced than me, I did not want to argue with him. As a verticle works with an event loop managing a queue of input queries, I want to say that I win even in performance since I declared my variable "static final" so I use the same instance for each request incoming instead of creating a new instance.

Please tell me if my friend is right or not. And please explain to me why you should not instantiate a class in a verticle ?

In addition I would like to know what is the interest of using 2 verticles for a treatment that only one verticle can do?

For example I have :

public class MyVerticle1 extends AbstractVerticle{

    public void start(Future<Void> startFuture) {

        connection.query("select * from file", result -> {

            if (result.succeeded()) {
                List<JsonArray> rowsSelected = result.result().getResults();
                eventBus.send("adress", rowsSelected, res -> {
                    if (res.succeded()) {
                        routinContext.response().end(res.result().encodePrettily());
                    }
                });
            } else {
                LOGGER.error(result.cause().toString());
            }

        });

    }
}


public class MyVerticle2 extends AbstractVerticle{

    public void start(Future<Void> startFuture) {
        JsonArray resultOfSelect = new JsonArray();

        eventBus.consumer("adress", message -> {
            List<JsonArray> rowsSelected = (List<JsonArray>) message.body();
            rowsSelected.forEach(jsa -> {
                JsonObject row = new JsonObject();
                row.put("id", jsa.getInteger(0));
                row.put("name", jsa.getString(1));
                resultOfSelect.add(row);
            });

            message.reply(resultOfSelect);
        });
    }
}

I really do not see the point of making 2 verticles since I can use the result of my query in the first verticle without using the second verticle.

For me, EventBus is important for transmitting information to verticles for parallel processing.

Help me understand please.

I created a new topic here if you prefer.

Thank you in advance !

HI Team,
This is closed ticket, but we are facing 404 error randomly for one verticle.
our deployment strategy is, one pod having multiple http service verticle and all are in cluster mode with Hazelcast.
for one verticle which have added we are getting 404 most of the time.
Can this issue still be relevant?
we are using Vertx 3.8.0

Was this page helpful?
0 / 5 - 0 ratings