Embedded Jetty Server en Java

1. Información general

En este artículo, veremos la biblioteca Jetty . Jetty proporciona un servidor web que puede ejecutarse como un contenedor integrado y se integra fácilmente con la biblioteca javax.servlet .

2. Dependencias de Maven

Para comenzar, agregaremos dependencias de Maven a las bibliotecas jetty-server y jetty-servlet:

 org.eclipse.jetty jetty-server 9.4.3.v20170317   org.eclipse.jetty jetty-servlet 9.4.3.v20170317 

3. Iniciar Jetty Server con Servlet

Iniciar el contenedor empotrado Jetty es simple. Necesitamos crear una instancia de un nuevo objeto de servidor y configurarlo para que se inicie en un puerto determinado:

public class JettyServer { private Server server; public void start() throws Exception { server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(8090); server.setConnectors(new Connector[] {connector}); }

Digamos que queremos crear un punto final que responda con el código de estado HTTP 200 si todo va bien y una carga útil JSON simple.

Crearemos una clase que amplíe la clase HttpServlet para manejar dicha solicitud; esta clase será de un solo subproceso y se bloqueará hasta su finalización:

public class BlockingServlet extends HttpServlet { protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println("{ \"status\": \"ok\"}"); } }

A continuación, necesitamos registrar la clase BlockingServlet en el objeto ServletHandler usando el método addServletWithMapping () e iniciar el servidor:

servletHandler.addServletWithMapping(BlockingServlet.class, "/status"); server.start();

Si deseamos probar nuestra lógica de Servlet, necesitamos iniciar nuestro servidor usando la clase JettyServer creada previamente que es un contenedor de la instancia real del servidor Jetty dentro de la configuración de prueba:

@Before public void setup() throws Exception { jettyServer = new JettyServer(); jettyServer.start(); }

Una vez iniciado, enviaremos una solicitud HTTP de prueba al punto final / status :

String url = "//localhost:8090/status"; HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);

4. Servlets sin bloqueo

Jetty tiene un buen soporte para el procesamiento de solicitudes asincrónicas.

Digamos que tenemos un recurso enorme que es intenso en E / S y tarda mucho en cargar bloqueando el subproceso en ejecución durante una cantidad considerable de tiempo. Es mejor si ese hilo se puede liberar para manejar otras solicitudes mientras tanto, en lugar de esperar algún recurso de E / S.

Para proporcionar dicha lógica con Jetty, podemos crear un servlet que usará la clase AsyncContext llamando al método startAsync () en HttpServletRequest. Este código no bloqueará el hilo en ejecución, pero realizará la operación de E / S en un hilo separado y devolverá el resultado cuando esté listo usando el método AsyncContext.complete () :

public class AsyncServlet extends HttpServlet { private static String HEAVY_RESOURCE = "This is some heavy resource that will be served in an async way"; protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ByteBuffer content = ByteBuffer.wrap( HEAVY_RESOURCE.getBytes(StandardCharsets.UTF_8)); AsyncContext async = request.startAsync(); ServletOutputStream out = response.getOutputStream(); out.setWriteListener(new WriteListener() { @Override public void onWritePossible() throws IOException { while (out.isReady()) { if (!content.hasRemaining()) { response.setStatus(200); async.complete(); return; } out.write(content.get()); } } @Override public void onError(Throwable t) { getServletContext().log("Async Error", t); async.complete(); } }); } }

Estamos escribiendo el ByteBuffer en OutputStream , y una vez que se escribe todo el búfer, indicamos que el resultado está listo para regresar al cliente invocando el método complete () .

A continuación, necesitamos agregar AsyncServlet como un mapeo de servlet Jetty:

servletHandler.addServletWithMapping( AsyncServlet.class, "/heavy/async");

Ahora podemos enviar una solicitud al punto final / heavy / async ; esa solicitud será manejada por Jetty de forma asincrónica:

String url = "//localhost:8090/heavy/async"; HttpClient client = HttpClientBuilder.create().build(); HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); assertThat(response.getStatusLine().getStatusCode()) .isEqualTo(200); String responseContent = IOUtils.toString(r esponse.getEntity().getContent(), StandardCharsets.UTF_8); assertThat(responseContent).isEqualTo( "This is some heavy resource that will be served in an async way");

Cuando nuestra aplicación maneja solicitudes de forma asincrónica, debemos configurar el grupo de subprocesos explícitamente. En la siguiente sección, configuraremos Jetty para usar un grupo de subprocesos personalizado.

5. Configuración del embarcadero

Cuando ejecutamos nuestra aplicación web en producción, es posible que queramos ajustar la forma en que el servidor Jetty procesa las solicitudes. Esto se hace definiendo el grupo de subprocesos y aplicándolo a nuestro servidor Jetty.

Para hacer esto, tenemos tres opciones de configuración que podemos establecer:

  • maxThreads : para especificar el número máximo de subprocesos que Jetty puede crear y usar en el grupo
  • minThreads: para establecer el número inicial de subprocesos en el grupo que utilizará Jetty
  • idleTimeout : este valor en milisegundos define cuánto tiempo puede estar inactivo un hilo antes de que se detenga y se elimine del grupo de hilos. La cantidad de subprocesos restantes en el grupo nunca será inferior a la configuración de minThreads

Con estos podemos configurar el servidor Jetty integrado mediante programación pasando el grupo de subprocesos configurado al constructor del servidor :

int maxThreads = 100; int minThreads = 10; int idleTimeout = 120; QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, minThreads, idleTimeout); server = new Server(threadPool);

Luego, cuando iniciemos nuestro servidor, usará subprocesos de un grupo de subprocesos específico.

6. Conclusión

En este tutorial rápido, vimos cómo integrar servidores integrados con Jetty y probamos nuestra aplicación web.

Como siempre, el código está disponible en GitHub.