Thank you very much for this exhaustive reply Christopher.

2012/12/11 Christopher Schultz <ch...@christopherschultz.net>

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA256
>
> Julien,
>
> Warning: this is long. Like, André-or-Mark-Eggers long.
>
> On 12/11/12 7:30 AM, Julien Martin wrote:
> > I am in reference to the following blog entry: Blog
> > entry<
> http://blog.springsource.org/2012/05/06/spring-mvc-3-2-preview-introducing-servlet-3-async-support
> >
> >
> >
> about Spring MVC 3.2 asynchronous support.
> >
> > I understand Tomcat uses a thread pool in order to serve http/web
> > requests. Furthermore, the above article seems to indicate that
> > Spring MVC asynchronous support relieves Tomcat's thread pool and
> > allows for better concurrency in the webapp by using background
> > threads for "heavy-lift" operations.
>
> I believe you are misinterpreting what that post has to say. It's not
> that a "background" thread itself is more efficient, it's that
> processing that does not need to communicate with the client can be
> de-coupled from the request-processing thread-pool that exists for
> that purpose.
>
> An example - right from the blog post - will make much more sense than
> what I wrote above. Let's take the example of sending an email
> message. First, some assumptions:
>
> 1. Sending an email takes a long time (say, 5 seconds)
> 2. The client does not need confirmation that the email has been sent
>
> If your were to write a "classic" servlet, it would look something
> like this:
>
> doPost() {
>   validateOrder();
>
>   queueOrder();
>
>   sendOrderConfirmation(); // This is the email
>
>   response.sendRedirect("/order_complete.jsp");
> }
>
> Let's say that validation takes 500ms, queuing takes 800ms, and
> emailing (as above) takes 5000ms. That means that the request, from
> the client perspective, takes 6300ms (6.3 sec). That's a noticeable delay.
>
> Also, during that whole time, a single request-processing thread (from
> Tomcat's thread pool) is tied-up, meaning that no other requests can
> be processed by that same thread.
>
> If you have a thread pool of size=1 (foolish, yet instructive), it
> means you can only process a single transaction every 6.3 seconds.
>
> Lets re-write the servlet with a background thread -- no
> "asynchronous" stuff from the servlet API, but just with a simple
> background thread:
>
> doPost() {
>   validateOrder();
>
>   queueOrder();
>
>   (new Thread() {
>     public void run() {
>       sendOrderConfirmation();
>     }
>   }).start();
>
>   response.sendRedirect("order_complete.jsp");
> }
>
> So, now the email is being sent by a background thread: the response
> returns to the client after 1.3 seconds which is a significant
> improvement. Now, we can handle a request once every 1.3 seconds with
> a request-processing thread-pool of size=1.
>
> Note that a better implementation of the above would be to use a
> thread pool for this sort of thing instead of creating a new thread
> for every request. This is what Spring provides. It's not that Spring
> can do a better job of thread management, it's that Tomcat's thread
> pool is special: it's the only one that can actually dispatch client
> requests. Off-loading onto another thread pool for background
> processing means more client requests can be handled with a smaller
> (or same-sized) pool.
>
> Looking above, you might notice that the validateOrder() and
> queueOrder() processes still take some time (1.3 seconds) to complete,
> and there is no interaction with the client during that time -- the
> client is just sitting there waiting for a response. Work is still
> getting done on the server, of course, but there's no real reason that
> the request-processing thread has to be the one doing that work: we
> can delegate the entire thing to a background thread so the
> request-processor thread can get back to dispatching new requests.
>
> This is where servlet 3.0 async comes into play.
>
> Let's re-write the servlet as an asynchronous one. I've never actually
> written one, so I'm sure the details are going to be wrong, but the
> idea is the same. This time, we'll do everything asynchronously.
>
> doPost() {
>   final AsyncContext ctx = request.startAsync();
>
>   (new Thread() {
>     public void run() {
>       validateOrder();
>       queueOrder();
>       sendOrderConfirmation();
>
>       ctx.getResponse().sendRedirect("/order_complete.jsp");
>
>       ctx.complete();
>     }
>   }).start();
> }
>
> So, how what happens? When startAsync is called, an AsyncContext is
> created and basically the request and response are packaged-up for
> later use. The doPost method creates a new thread and starts it (or it
> may start a few ms later), then returns from doPost. At this point,
> the request-processor thread has only spent a few ms (let's say 5ms)
> setting up the request and then it goes back into Tomcat's thread-pool
> and can accept another request. Meanwhile, the "background" thread
> will process the actual transaction.
>
> Let's assume that nothing in the run() method above interacts in any
> way with the client. In the first example (no async), the client waits
> the whole time for a response from the server, and the
> request-processing thread does all the work. So, the client waits 6.3
> seconds and the request-processing thread is "working" for 6.3 seconds.
>
> In the async example, the client will probably still wait 6.3 seconds,
> but the request-processing thread is back and ready for more client
> requests after a tiny amount of time. Of course, the transaction is
> not complete, yet.
>
> The background thread will run and process the transaction, including
> the 5-second email process. Once the email confirmation has been sent,
> the background thread "sends" a redirect and completes the async
> request. I'm not sure of the exact details, here, but either the
> background thread itself (via getRequest().sendRedirect()) pushes the
> response back to the client, or Tomcat fetches a request-processing
> thread from the pool and uses that to do the same thing. I can't see
> why the background-thread wouldn't do that itself, but it's up to the
> container to determine who does what.
>
> The point is that, when using asynchronous requests, fewer
> request-processing threads can handle a whole lot of load. In the
> async example, still with a thread-pool of size=1 and an async-setup
> time of 5ms, that means that you can handle one client transaction
> every 5ms. That's much better than every 6.3 seconds, don't you think?
>
> (Note that this isn't magic: if your background threads are either
> limited or your system can't handle the number of transactions you are
> trying to asynchronously-process, eventually you'll still have
> everyone waiting 6.3 seconds no matter what).
>
> So, a recap of throughput (req/sec) of the above 3 implementations:
>
> Standard:      .15873
> Background:    .76923
> Async:      200.00000
>
> Using asynchronous dispatching can improve our throughput a huge
> number of times.
>
> It's worth repeating what I said earlier: if your server can't
> actually handle this much load (200 emails per second, let's say),
> then using asych isn't going to change anything. Honestly, this trick
> only works when you have a lot of heterogeneous requests. For example,
> maybe 10% of your traffic is handling orders as implemented above,
> while the rest of your traffic is for much smaller sub-500ms-requests.
> There's probably no reason to convert those short-running requests
> into asynchronous operations. Only long-running processes with no
> client interaction make any sense for this. If only 10% of your
> requests are orders, that means that maybe you can process 20 orders
> and 190 "small" requests per second. That's much better than, say,
> waiting 6.3 seconds for a single order and then processing a single
> short request, then another order and so on.
>
> Just remember that once all request-processing threads are tied-up
> doing something, everyone else waits in line. Asynchronous request
> dispatching aims to run through the line as quickly as possible. It
> does *not* improve the processing time of any one transaction.
>
> - -chris
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
> Comment: GPGTools - http://gpgtools.org
> Comment: Using GnuPG with undefined - http://www.enigmail.net/
>
> iEYEAREIAAYFAlDHTdkACgkQ9CaO5/Lv0PDrNACgsaeHmBzr9RMSFuZX9ksX3g9d
> bKYAniJzbqRjGBAjwxIYihvcyJYV5rIl
> =l+ie
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
>
>

Reply via email to