Встроенный Jetty Server в Java

Встроенный Jetty Server в Java

1. обзор

В этой статье мы рассмотрим библиотекуJetty. Jetty предоставляет веб-сервер, который может работать как встроенный контейнер и легко интегрируется с библиотекойjavax.servlet.

2. Maven Зависимости

Для начала добавим зависимости Maven в библиотекиjetty-server иjetty-servlet:


    org.eclipse.jetty
    jetty-server
    9.4.3.v20170317


    org.eclipse.jetty
    jetty-servlet
    9.4.3.v20170317

3. Запуск Jetty Server с сервлетом

Запуск встроенного контейнера Jetty прост. Нам нужно создать экземпляр нового объектаServer и настроить его запуск на заданном порту:

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

Допустим, мы хотим создать конечную точку, которая будет отвечать кодом состояния HTTP 200, если все будет хорошо, и простой полезной нагрузкой JSON.

Мы создадим класс, который расширяет классHttpServlet для обработки такого запроса; этот класс будет однопоточным и будет блокироваться до завершения:

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

Затем нам нужно зарегистрировать классBlockingServlet в объектеServletHandler с помощью методаaddServletWithMapping() и запустить сервер:

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

Если мы хотим протестировать нашу логику сервлета, нам нужно запустить наш сервер, используя ранее созданный классJettyServer, который является оболочкой фактического экземпляра сервера Jetty в тестовой настройке:

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

После запуска мы отправим тестовый HTTP-запрос в конечную точку/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. Неблокирующие сервлеты

Jetty имеет хорошую поддержку для асинхронной обработки запросов.

Предположим, у нас есть огромный ресурс, который требует большого количества операций ввода-вывода и требует много времени для загрузки, блокируя выполняющийся поток в течение значительного количества времени. Лучше, если этот поток может быть освобожден для обработки других запросов в то же время, вместо ожидания некоторого ресурса ввода-вывода.

Чтобы обеспечить такую ​​логику с помощью Jetty, мы можем создать сервлет, который будет использовать классAsyncContext, вызвав методstartAsync() дляHttpServletRequest.. Этот код не будет блокировать выполняющийся поток, но будет выполнять Операция ввода-вывода в отдельном потоке, возвращающая результат по готовности с использованием метода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();
            }
        });
    }
}

Мы записываемByteBuffer вOutputStream, и как только весь буфер записан, мы сигнализируем, что результат готов к возврату клиенту, вызывая методcomplete().

Затем нам нужно добавитьAsyncServlet как отображение сервлета Jetty:

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

Теперь мы можем отправить запрос в конечную точку/heavy/async - этот запрос будет обрабатываться Jetty асинхронным способом:

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

Когда наше приложение обрабатывает запросы асинхронно, мы должны явно настроить пул потоков. В следующем разделе мы настроимJetty для использования настраиваемого пула потоков.

5. Конфигурация причала

Когда мы запускаем наше веб-приложение на производстве, мы можем захотеть настроить то, как сервер Jetty обрабатывает запросы. Это делается путем определения пула потоков и применения его к нашему серверу Jetty.

Для этого у нас есть три параметра конфигурации, которые мы можем установить:

  • maxThreads - чтобы указать максимальное количество потоков, которые Jetty может создать и использовать в пуле.

  • minThreads – Чтобы установить начальное количество потоков в пуле, которое Jetty будет использовать

  • idleTimeout - это значение в миллисекундах определяет, как долго поток может простаивать, прежде чем он будет остановлен и удален из пула потоков. Количество оставшихся потоков в пуле никогда не будет ниже значенияminThreads.

С их помощью мы можем программно настроить встроенный серверJetty, передав настроенный пул потоков конструкторуServer:

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

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

server = new Server(threadPool);

Затем, когда мы запустим наш сервер, он будет использовать потоки из определенного пула потоков.

6. Заключение

В этом кратком руководстве мы увидели, как интегрировать встроенные серверы с Jetty, и протестировали наше веб-приложение.

Как всегда доступен кодover on GitHub.