Jetty Server incorporato in Java

1. Panoramica

In questo articolo, esamineremo la libreria Jetty . Jetty fornisce un server web che può essere eseguito come contenitore incorporato e si integra facilmente con la libreria javax.servlet .

2. Dipendenze di Maven

Per iniziare, aggiungeremo le dipendenze Maven alle librerie jetty-server e jetty-servlet:

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

3. Avvio di Jetty Server con servlet

L'avvio del contenitore incorporato Jetty è semplice. Dobbiamo istanziare un nuovo oggetto Server e impostarlo per l'avvio su una determinata porta:

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}); }

Diciamo che vogliamo creare un endpoint che risponderà con il codice di stato HTTP di 200 se tutto va bene e un semplice payload JSON.

Creeremo una classe che estende la classe HttpServlet per gestire tale richiesta; questa classe sarà a thread singolo e verrà bloccata fino al completamento:

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\"}"); } }

Successivamente, dobbiamo registrare la classe BlockingServlet nell'oggetto ServletHandler utilizzando il metodo addServletWithMapping () e avviare il server:

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

Se desideriamo testare la nostra logica Servlet, dobbiamo avviare il nostro server utilizzando la classe JettyServer creata in precedenza che è un wrapper dell'istanza del server Jetty effettiva all'interno della configurazione di test:

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

Una volta avviato, invieremo una richiesta HTTP di prova all'endpoint / 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. Servlet non bloccanti

Jetty ha un buon supporto per l'elaborazione delle richieste asincrone.

Supponiamo di avere un'enorme risorsa che richiede molto tempo per caricare I / O e blocca il thread in esecuzione per una notevole quantità di tempo. È meglio se quel thread può essere liberato per gestire altre richieste nel frattempo, invece di aspettare qualche risorsa di I / O.

Per fornire tale logica con Jetty, possiamo creare un servlet che utilizzerà la classe AsyncContext chiamando il metodo startAsync () su HttpServletRequest. Questo codice non bloccherà il thread in esecuzione ma eseguirà l'operazione di I / O in thread separati restituendo il risultato quando sarà pronto utilizzando il metodo 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(); } }); } }

Stiamo scrivendo ByteBuffer in OutputStream e una volta che l'intero buffer è stato scritto, segnaliamo che il risultato è pronto per essere restituito al client invocando il metodo complete () .

Successivamente, dobbiamo aggiungere AsyncServlet come mappatura servlet Jetty:

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

Ora possiamo inviare una richiesta all'endpoint / heavy / async : tale richiesta verrà gestita da Jetty in modo asincrono:

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");

Quando la nostra applicazione gestisce le richieste in modo asincrono, dobbiamo configurare esplicitamente il pool di thread. Nella sezione successiva, configureremo Jetty per utilizzare un pool di thread personalizzato.

5. Configurazione molo

Quando eseguiamo la nostra applicazione web in produzione, potremmo voler regolare il modo in cui il server Jetty elabora le richieste. Questo viene fatto definendo il pool di thread e applicandolo al nostro server Jetty.

Per fare ciò, abbiamo tre impostazioni di configurazione che possiamo impostare:

  • maxThreads - Per specificare il numero massimo di thread che Jetty può creare e utilizzare nel pool
  • minThreads - Per impostare il numero iniziale di thread nel pool che Jetty utilizzerà
  • idleTimeout : questo valore in millisecondi definisce per quanto tempo un thread può essere inattivo prima di essere arrestato e rimosso dal pool di thread. Il numero di thread rimanenti nel pool non sarà mai inferiore all'impostazione minThreads

Con questi possiamo configurare il server Jetty incorporato a livello di programmazione passando il pool di thread configurato al costruttore del server :

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

Quindi, quando avviamo il nostro server, utilizzerà i thread da un pool di thread specifico.

6. Conclusione

In questo breve tutorial, abbiamo visto come integrare i server embedded con Jetty e testato la nostra applicazione web.

Come sempre, il codice è disponibile su GitHub.