So I spent an hour with the debugger
looking into the inner workings of Netty to give you a more
detailed explanation of what is going on... (You could do the same
with a debugger if you are interested)
You are deploying N instances of a verticle which all start an
HTTP server on port 8080. Under the cover Vert.x only creates a
single HTTP server which actually listens on that address and
round robins between the different instances.
This single server has a single ServerSocket which listens on port
8080. When this is first created it is assigned an event loop,
call this EventLoop0 - at this point there is only one event loop
as its the first server that gets deployed that gets to actually
listen.
When netty chooses an event loop it uses round robin, but there is
only one event loop to choose from so the EventLoop0 is chosen,
the position is incremented and it gets set back to zero as there
is only one event loop.
The other verticles now deploy and register their event loops, and
now there are N event loops to choose from but the pointer is
still at zero.
Now the verticles are deployed and the server is listening.
We now make a request from a browser to
http://localhost:8080/foo
The ServerSocket accepts the connection on EventLoop0 (that's the
acceptor loop), it now needs to find an event loop that's going to
be associated to the connection, so it calls next() on
NioEventLoopGroup - the pointer is at zero, so EventLoop0 gets
chosen again - this is the event loop which will forever more
handle any data for that connection.
Now the foo handler gets called, on EventLoop0.
foo handler uses jodd to make a blocking HTTP request to
http://localhost:8080/foo
This is the same address as before so the same and we a single
ServerSocket listening on that address - but that ServerSocket is
assigned EventLoop0. But that's the one we are blocking, so the
connection will never get accepted! So you have a deadlock.
I hope that is clearer now :)
I'll say it again - bottom line is - don't block that event loop.
If you follow that simple rule you will be ok.
There is no reason for people to "break their heads" over this.
The rule is simple and Vert.x will tell you *exactly* where you
are blocking if you do this.
fwiw - you won't get any such niceness in Node.js or other async
systems which will just hang in this situation (e.g. pure Netty or
Node.js)
Here's your stack showing the exact line of code, as I believe you
missed this before:
WARNING: Thread Thread[vert.x-eventloop-thread-6,5,main] has been
blocked for 6779 ms, time limit is 2000
io.vertx.core.VertxException: Thread blocked
at java.net.SocketInputStream.socketRead0(Native Method)
at
java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at jodd.http.HttpResponse.readFrom(HttpResponse.java:156)
at jodd.http.HttpRequest.send(HttpRequest.java:657)
at vertx.FooHandler.handle(FooHandler.java:18) <!--------
THIS IS WHERE YOUR FOOHANDLER SENDS THE JODD REQUEST
at vertx.FooHandler.handle(FooHandler.java:9)
at
io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:213)
at
io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:67)
at
io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:96)
at
vertx.ServerVerticle.lambda$setupRouter$2(ServerVerticle.java:56)
<snip>
Imho it doesn't get much more userfriendly than that :)