[undertow-dev] Unifying blocking and non-blocking handlers, Part 2

Stuart Douglas sdouglas at redhat.com
Mon Mar 11 04:24:26 EDT 2013


Folks,

I have been giving some more thought to how best to unify blocking and 
non-blocking handlers, and I have come up with a new approach.

At the moment what we have is a mess. We have two types of handlers, 
confusingly called blocking and non-blocking. This terminology is not in 
any way accurate, it is quite possible for a non-blocking handler to 
dispatch to a worker thread and perform a blocking operation (such as 
looking up a user from the IDM). It is also possible for blocking 
handlers to perform non-blocking IO, such as when using the Servlet 3.1 
async IO API.

These two different types of handlers make it very difficult to write 
general purpose handlers that work in all situations. e.g. at the moment 
we have a requirement for the security handlers to sit in the middle of 
the servlet handler chain, however due to servlet handlers being 
'blocking' and security being 'non-blocking' this is not really possible 
without some form of adaptor.

I am proposing that we make the following changes:

1) Change the signature of HttpHandler#handleRequest to

void handleRequest(HttpServerExchange exchange) throws Exception;

This will allow exceptions to propagate up the exchange to the root 
handler. If an exception propagates all the way the exchange will be 
ended with a 500 error code.

2) Change the default behavior so that when the handler call stack 
returns the exchange is ended. This is consistent with how blocking 
handlers work at the moment. If an async handler wants to either 
dispatch to a worker or perform async IO it has to either call 
HttpServerExchange.dispatch() or resume reads/writes. In either case the 
call stack will return, and then the requested action will be 
dispatched. This means that there will only ever be a single thread 
active in the exchange.

3) Add the methods isInIoThread() to the exchange. This means that 
handlers that need to perform blocking actions will do the following:

if (exchange.isInIoThread()) {
   exchange.dispatch(this);
   return;
}

Rather than the current approach using WorkerDispatcher, that requires a 
thread local read and an object creation.

4) Add the isBlocking() and startBlocking() method to the exchange 
(actually these are already there). The startBlocking() method starts 
blocking IO, and enables the use of get(Input/Output)Stream on the 
exchange.

I think these changes have a number of advantages. In particular:

- It will be much less likely to 'loose' a request due to a buggy 
handler. At the moment if an async handler returns without ending the 
exchange or dispatching then the request just disappears, which can be 
quite difficult to debug.

- Handlers that do not perform IO will work will all types of requests, 
and can be placed anywhere in the handler chain.

- The threading model should become simpler for blocking requests that 
need to do async work (such as servlet 3.1), as the new model means that 
only one thread will ever be active in an exchange.

I have made a start of these changes here if anyone is interested, it 
compiles and the tests pass, however there are probably still some bugs:

https://github.com/stuartwdouglas/undertow/compare/remove-blocking

Stuart







More information about the undertow-dev mailing list