Search code examples
jettynettyservlet-3.0http2grpc

What is the best way to implement an HTTP2 servlet in Jetty?


I understand that HTTP2 in Jetty is mostly at the Connector, Transport and Channel levels.

I'm trying to decide which combination would be the best to transport binary data between client and server:

  1. Jetty HTTP2 server with async servlet + Jetty HTTP2 client
  2. Jetty HTTP2 server with sync servlet + Jetty HTTP2 client
  3. Jetty HTTP2 server with async servlet + Netty HTTP2 client
  4. GRPC client and server (both are default Netty based)

Details:

I would like to send binary data to my client and I would like the connections to be non-blocking/async. The number of concurrent client requests can be high and the server could take a few seconds (some times) to respond to some requests.

Each response is small chunk of binary data. I would've liked it if I could send Netty's ByteBufs directly as the response instead of copying to byte[] or ByteBuffer but that is not directly related to this particular question.

Method #4 is not my favorite because of the ProtoBuf wrapping (link) limitation (link).

Jetty references:


Solution

  • Disclaimer, I am the Jetty HTTP/2 maintainer.

    Given that you have a large number of clients and that processing could take seconds, I would recommend to go with option 1 - async servlet and Jetty HTTP/2 client.

    With "async servlet" you have 2 flavors: 1) async processing with blocking I/O, or 2) async processing + async I/O.

    Servlet async processing is triggered by the use of HttpServletRequest.startAsync().

    Servlet async I/O is triggered by using ReadListener and WriteListener respectively with the ServletInputStream and ServletOutputStream.

    You definitely want async processing because you have a large number of clients and processing in the order of seconds - this will optimize the use of server threads.

    Whether or not to use async I/O should probably be measured, since you have small binary responses. Blocking I/O is much much easier to code and debug, while async I/O is definitely more complicated to code and debug. Async I/O really shines when you have large contents and slow clients that may congest the TCP connection.

    If you want to be fully async, go with async I/O. If you can tolerate a bit of blocking in exchange of simpler code, stay on blocking I/O. Worth repeating, in both cases - either async I/O or blocking I/O - you want to use async processing.

    Regarding the issue of copying data, if you are willing to cast down to Jetty classes, you can avoid the copy by directly writing a ByteBuffer to the ServletOutputStream subclass, see this example.

    Finally, with the Jetty client you can use the high-level HttpClient with the HTTP/2 transport as detailed here. The benefit would be a high-level API that only deals with HTTP concepts, rather than using the low-level HTTP2Client that deals with HTTP/2 frames, streams and such.

    Report back what you end up choosing, and how it goes for you !