Embedded Jetty Server en Java

Serveur Jetty intégré en Java

1. Vue d'ensemble

Dans cet article, nous examinerons la bibliothèqueJetty. Jetty fournit un serveur Web qui peut fonctionner comme un conteneur intégré et s'intègre facilement à la bibliothèquejavax.servlet.

2. Dépendances Maven

Pour commencer, nous allons ajouter des dépendances Maven aux bibliothèquesjetty-server etjetty-servlet:


    org.eclipse.jetty
    jetty-server
    9.4.3.v20170317


    org.eclipse.jetty
    jetty-servlet
    9.4.3.v20170317

3. Démarrer Jetty Server avec Servlet

Démarrer le conteneur intégré Jetty est simple. Nous devons instancier un nouvel objetServer et le définir 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 classeHttpServlet 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 classeBlockingServlet dans l'objetServletHandler en utilisant la méthodeaddServletWithMapping() 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 classeJettyServer 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 = "http://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 demandes asynchrones.

Supposons que nous ayons une ressource énorme dont les E / S intenses prennent 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 demandes entre-temps, au lieu d'attendre certaines ressources d'E / S.

Pour fournir une telle logique avec Jetty, nous pouvons créer un servlet qui utilisera la classeAsyncContext en appelant la méthodestartAsync() sur leHttpServletRequest. Ce code ne bloquera pas le thread en cours d'exécution mais exécutera le Opération d'E / S dans un thread séparé renvoyant le résultat une fois prêt à l'aide de la méthodeAsyncContext.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 lesByteBuffer dans lesOutputStream, 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éthodecomplete().

Ensuite, nous devons ajouter lesAsyncServlet 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 = "http://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 traite les demandes de manière asynchrone, nous devons configurer le pool de threads explicitement. Dans la section suivante, nous allons configurerJetty 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 ajuster la manière dont le serveur Jetty traite les demandes. Ceci est 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ètreminThreads

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

int maxThreads = 100;
int minThreads = 10;
int idleTimeout = 120;

QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, minThreads, idleTimeout);

server = new Server(threadPool);

Ensuite, lorsque nous démarrons notre serveur, il utilisera des threads provenant d’un pool de threads spécifique.

6. Conclusion

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

Comme toujours, le code est disponibleover on GitHub.