Embedded Jetty Server in Java

Eingebetteter Jetty Server in Java

1. Überblick

In diesem Artikel werden wir uns die BibliothekJettyansehen. Jetty bietet einen Webserver, der als eingebetteter Container ausgeführt werden kann und sich problemlos in diejavax.servlet-Bibliothek integrieren lässt.

2. Maven-Abhängigkeiten

Zu Beginn fügen wir Maven-Abhängigkeiten zu den Bibliothekenjetty-server undjetty-servlethinzu:


    org.eclipse.jetty
    jetty-server
    9.4.3.v20170317


    org.eclipse.jetty
    jetty-servlet
    9.4.3.v20170317

3. Jetty Server mit Servlet starten

Das Starten des eingebetteten Jetty-Containers ist einfach. Wir müssen ein neuesServer-Objekt instanziieren und so einstellen, dass es an einem bestimmten Port startet:

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

Angenommen, wir möchten einen Endpunkt erstellen, der mit dem HTTP-Statuscode 200 antwortet, wenn alles gut läuft, und eine einfache JSON-Nutzlast.

Wir erstellen eine Klasse, die die KlasseHttpServleterweitert, um eine solche Anforderung zu verarbeiten. Diese Klasse ist Single-Threaded und blockiert bis zum Abschluss:

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

Als nächstes müssen wir die KlasseBlockingServlet im ObjektServletHandler mithilfe der MethodeaddServletWithMapping() registrieren und den Server starten:

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

Wenn wir unsere Servlet-Logik testen möchten, müssen wir unseren Server mithilfe der zuvor erstelltenJettyServer-Klasse starten, die ein Wrapper der tatsächlichen Jetty-Serverinstanz innerhalb des Test-Setups ist:

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

Nach dem Start senden wir eine HTTP-Testanforderung an den Endpunkt von/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. Nicht blockierende Servlets

Jetty bietet eine gute Unterstützung für die asynchrone Anforderungsverarbeitung.

Nehmen wir an, wir haben eine enorme Ressource, die E / A-intensiv ist und lange Zeit benötigt, um den ausführenden Thread für einen beträchtlichen Zeitraum zu blockieren. Es ist besser, wenn dieser Thread in der Zwischenzeit für andere Anforderungen freigegeben werden kann, anstatt auf eine E / A-Ressource zu warten.

Um eine solche Logik mit Jetty bereitzustellen, können wir ein Servlet erstellen, das dieAsyncContext-Klasse verwendet, indem wir diestartAsync()-Methode fürHttpServletRequest. aufrufen. Dieser Code blockiert nicht den ausführenden Thread, sondern führt das aus E / A-Operation in einem separaten Thread, der das Ergebnis zurückgibt, wenn er mit der MethodeAsyncContext.complete() bereit ist:

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

Wir schreiben dieByteBuffer in dieOutputStream, und sobald der gesamte Puffer geschrieben ist, signalisieren wir, dass das Ergebnis bereit ist, durch Aufrufen dercomplete()-Methode zum Client zurückzukehren.

Als nächstes müssen wirAsyncServlet als Jetty-Servlet-Mapping hinzufügen:

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

Wir können jetzt eine Anfrage an den Endpunkt von/heavy/asyncenden - diese Anfrage wird vom Jetty asynchron verarbeitet:

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

Wenn unsere Anwendung Anforderungen asynchron verarbeitet, sollten wir den Thread-Pool explizit konfigurieren. Im nächsten Abschnitt konfigurieren wirJetty für die Verwendung eines benutzerdefinierten Thread-Pools.

5. Anlegestellenkonfiguration

Wenn wir unsere Webanwendung in der Produktion ausführen, möchten wir möglicherweise optimieren, wie der Jetty-Server Anforderungen verarbeitet. Dazu definieren Sie den Thread-Pool und wenden ihn auf unseren Jetty-Server an.

Dazu haben wir drei Konfigurationseinstellungen, die wir festlegen können:

  • maxThreads - Gibt die maximale Anzahl von Threads an, die Jetty im Pool erstellen und verwenden kann

  • minThreads – Zum Festlegen der anfänglichen Anzahl von Threads im Pool, die Jetty verwenden wird

  • idleTimeout - Dieser Wert in Millisekunden definiert, wie lange ein Thread inaktiv sein kann, bevor er gestoppt und aus dem Thread-Pool entfernt wird. Die Anzahl der verbleibenden Threads im Pool wird niemals die EinstellungminThreadsunterschreiten

Mit diesen können wir den eingebettetenJetty-Server programmgesteuert konfigurieren, indem wir den konfigurierten Thread-Pool an denServer-Konstruktor übergeben:

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

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

server = new Server(threadPool);

Wenn wir dann unseren Server starten, verwendet er Threads aus einem bestimmten Thread-Pool.

6. Fazit

In diesem kurzen Tutorial haben wir gesehen, wie man eingebettete Server in Jetty integriert und unsere Webanwendung getestet.

Wie immer ist der Codeover on GitHub verfügbar.