Serveur Jetty intégré à Java

1. Vue d'ensemble

Dans cet article, nous examinerons la bibliothèque Jetty . Jetty fournit un serveur Web qui peut s'exécuter en tant que conteneur intégré et s'intègre facilement à la bibliothèque javax.servlet .

2. Dépendances de Maven

Pour commencer, nous ajouterons des dépendances Maven aux bibliothèques jetty-server et jetty-servlet:

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

3. Démarrage de Jetty Server avec un servlet

Le démarrage du conteneur intégré Jetty est simple. Nous devons instancier un nouvel objet Serveur et le configurer pour qu'il démarre sur un port donné:

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

Disons que nous voulons créer un point de terminaison qui répondra avec le code d'état HTTP de 200 si tout se passe bien et une simple charge utile JSON.

Nous allons créer une classe qui étend la classe HttpServlet pour gérer une telle demande; cette classe sera à thread unique et bloquera jusqu'à la fin:

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

Ensuite, nous devons enregistrer la classe BlockingServlet dans l' objet ServletHandler à l'aide de la méthode addServletWithMapping () et démarrer le serveur:

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

Si nous souhaitons tester notre logique Servlet, nous devons démarrer notre serveur en utilisant la classe JettyServer précédemment créée qui est un wrapper de l'instance de serveur Jetty réelle dans la configuration de test:

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

Une fois démarré, nous enverrons une requête HTTP de test au point de terminaison / 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 non bloquants

Jetty prend en charge le traitement des requêtes asynchrones.

Disons que nous avons une ressource énorme qui est intense en E / S prenant beaucoup de temps à charger, bloquant le thread en cours d'exécution pendant une durée considérable. Il est préférable que ce thread puisse être libéré pour gérer d'autres requêtes entre-temps, au lieu d'attendre une ressource d'E / S.

Pour fournir une telle logique avec Jetty, nous pouvons créer un servlet qui utilisera la classe AsyncContext en appelant la méthode startAsync () sur HttpServletRequest. Ce code ne bloquera pas le thread en cours d'exécution mais effectuera l'opération d'E / S dans un thread séparé retournant le résultat lorsqu'il est prêt à l'aide de la méthode 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(); } }); } }

Nous écrivons le ByteBuffer dans OutputStream , et une fois que tout le tampon est écrit, nous signalons que le résultat est prêt à retourner au client en invoquant la méthode complete () .

Ensuite, nous devons ajouter l' AsyncServlet en tant que mappage de servlet Jetty:

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

Nous pouvons maintenant envoyer une requête au point de terminaison / heavy / async - cette requête sera gérée par la Jetty de manière asynchrone:

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

Lorsque notre application gère les requêtes de manière asynchrone, nous devons configurer explicitement le pool de threads. Dans la section suivante, nous allons configurer Jetty pour utiliser un pool de threads personnalisé.

5. Configuration de la jetée

Lorsque nous exécutons notre application Web en production, nous souhaitons peut-être régler la façon dont le serveur Jetty traite les demandes. Cela se fait en définissant le pool de threads et en l'appliquant à notre serveur Jetty.

Pour ce faire, nous avons trois paramètres de configuration que nous pouvons définir:

  • maxThreads - Pour spécifier le nombre maximum de threads que Jetty peut créer et utiliser dans le pool
  • minThreads - Pour définir le nombre initial de threads dans le pool que Jetty utilisera
  • idleTimeout - Cette valeur en millisecondes définit la durée pendant laquelle un thread peut être inactif avant d'être arrêté et supprimé du pool de threads. Le nombre de threads restants dans le pool ne descendra jamais en dessous du paramètre minThreads

Avec ceux-ci, nous pouvons configurer le serveur Jetty intégré par programme en passant le pool de threads configuré au constructeur du serveur :

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

Ensuite, lorsque nous démarrerons notre serveur, il utilisera des threads d'un pool de threads spécifique.

6. Conclusion

Dans ce rapide tutoriel, nous avons vu comment intégrer des serveurs embarqués à Jetty et testé notre application Web.

Comme toujours, le code est disponible sur sur GitHub.