Servidor Jetty incorporado em Java

Servidor Jetty incorporado em Java

1. Visão geral

Neste artigo, examinaremos a bibliotecaJetty. Jetty fornece um servidor web que pode ser executado como um contêiner embutido e se integra facilmente com a bibliotecajavax.servlet.

2. Dependências do Maven

Para começar, adicionaremos dependências Maven às bibliotecasjetty-serverejetty-servlet:


    org.eclipse.jetty
    jetty-server
    9.4.3.v20170317


    org.eclipse.jetty
    jetty-servlet
    9.4.3.v20170317

3. Iniciando Jetty Server com Servlet

Iniciar o contêiner incorporado do Jetty é simples. Precisamos instanciar um novo objetoServer e configurá-lo para iniciar em uma determinada 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});
}

Digamos que queremos criar um endpoint que responderá com o código de status HTTP 200 se tudo correr bem e uma carga útil JSON simples.

Vamos criar uma classe que estende a classeHttpServlet para lidar com essa solicitação; esta classe terá thread único e será bloqueada até a conclusão:

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

Em seguida, precisamos registrar a classeBlockingServlet no objetoServletHandler usando o métodoaddServletWithMapping() e iniciar o servidor:

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

Se quisermos testar nossa lógica de Servlet, precisamos iniciar nosso servidor usando a classeJettyServer criada anteriormente que é um wrapper da instância real do servidor Jetty dentro da configuração de teste:

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

Uma vez iniciado, enviaremos uma solicitação HTTP de teste para o endpoint/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 sem bloqueio

O Jetty tem um bom suporte para o processamento de solicitações assíncronas.

Digamos que temos um recurso enorme de I / O intenso, demorando muito para carregar, bloqueando o thread em execução por um período substancial de tempo. É melhor se esse encadeamento puder ser liberado para manipular outros pedidos enquanto isso, em vez de aguardar algum recurso de E / S.

Para fornecer essa lógica com o Jetty, podemos criar um servlet que usará a classeAsyncContext chamando o métodostartAsync() noHttpServletRequest.. Este código não bloqueará o thread em execução, mas executará o Operação de E / S em thread separada, retornando o resultado quando estiver pronto usando o métodoAsyncContext.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();
            }
        });
    }
}

Estamos escrevendoByteBuffer emOutputStream, e uma vez que todo o buffer é escrito, estamos sinalizando que o resultado está pronto para retornar ao cliente invocando o métodocomplete().

Em seguida, precisamos adicionarAsyncServlet como um mapeamento de servlet Jetty:

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

Agora podemos enviar uma solicitação para o endpoint/heavy/async - essa solicitação será tratada pelo Jetty de maneira assíncrona:

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

Quando nosso aplicativo está manipulando solicitações de maneira assíncrona, devemos configurar o pool de threads explicitamente. Na próxima seção, configuraremosJetty para usar um pool de threads customizado.

5. Configuração Jetty

Quando executamos nosso aplicativo Web em produção, queremos ajustar o modo como o servidor Jetty processa solicitações. Isso é feito definindo o pool de threads e aplicando-o ao nosso servidor Jetty.

Para fazer isso, temos três definições de configuração que podemos definir:

  • maxThreads - para especificar o número máximo de threads que o Jetty pode criar e usar no pool

  • minThreads – Para definir o número inicial de threads no pool que o Jetty usará

  • idleTimeout - Este valor em milissegundos define quanto tempo um thread pode ficar inativo antes de ser interrompido e removido do pool de threads. O número de threads restantes no pool nunca ficará abaixo da configuração deminThreads

Com eles, podemos configurar o servidorJetty incorporado de maneira programática, passando o pool de threads configurado para o construtorServer:

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

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

server = new Server(threadPool);

Então, quando iniciarmos nosso servidor, ele estará usando threads de um pool de threads específico.

6. Conclusão

Neste tutorial rápido, vimos como integrar servidores incorporados ao Jetty e testamos nosso aplicativo da web.

Como sempre, o código está disponívelover on GitHub.