Embedded Jetty Server in Java – 在Java中嵌入Jetty服务器

最后修改: 2017年 4月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we will be looking at the Jetty library. Jetty provides a web server that can run as an embedded container and integrates easily with the javax.servlet library.

在这篇文章中,我们将研究Jetty库。Jetty提供了一个可以作为嵌入式容器运行的Web服务器,并与javax.servlet库轻松集成。

2. Maven Dependencies

2.Maven的依赖性

To get started we’ll add Maven dependencies to jetty-server and jetty-servlet libraries:

为了开始工作,我们将为jetty-serverjetty-servlet库添加Maven依赖项。

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.4.3.v20170317</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
    <version>9.4.3.v20170317</version>
</dependency>

3. Starting Jetty Server With Servlet

3.用Servlet启动Jetty服务器

Starting the Jetty embedded container is simple. We need to instantiate a new Server object and set it to start on a given port:

启动Jetty嵌入式容器很简单。我们需要实例化一个新的服务器对象,并将其设置为在一个给定的端口启动。

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

Let’s say that we want to create an endpoint that will respond with the HTTP status code of 200 if everything goes well and a simple JSON payload.

比方说,我们想创建一个端点,如果一切顺利,它将以HTTP状态代码200和一个简单的JSON有效载荷来响应。

We’ll create a class that extends the HttpServlet class to handle such request; this class will be single threaded and block until completion:

我们将创建一个扩展了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\"}");
    }
}

Next, we need to register the BlockingServlet class in the ServletHandler object by using the addServletWithMapping() method and start the server:

接下来,我们需要通过使用addServletWithMapping()方法在ServletHandler对象中注册BlockingServlet类,并启动服务器。

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

If we wish to test our Servlet logic, we need to start our server by using the previously created JettyServer class that is a wrapper of the actual Jetty server instance within the test setup:

如果我们想测试我们的Servlet逻辑,我们需要通过使用先前创建的 JettyServer 类来启动我们的服务器,该类是测试设置中实际的Jetty服务器实例的封装。

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

Once started, we will send a test HTTP request to the /status endpoint:

一旦启动,我们将向/status端点发送一个测试的HTTP请求。

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. Non-Blocking Servlets

4.无阻塞的Servlets

Jetty has good support for asynchronous request processing.

Jetty对异步请求处理有很好的支持。

Let’s say that we have an enormous resource that is I/O intense taking a long time to load blocking the executing thread for a substantial amount of time. It is better if that thread can be liberated to handle other requests in the meantime, instead of waiting for some I/O resource.

比方说,我们有一个巨大的资源,它是I/O密集型的,需要很长的时间来加载,阻塞了执行线程的大量时间。如果该线程能被解放出来处理其他请求,而不是等待一些I/O资源,那就更好了。

To provide such logic with Jetty, we can create a servlet that will use the AsyncContext class by calling the startAsync() method on the HttpServletRequest. This code will not block the executing thread but will perform the I/O operation in separate thread returning the result when ready using the AsyncContext.complete() method:

为了用Jetty提供这样的逻辑,我们可以创建一个Servlet,它将使用AsyncContext类,在HttpServletRequest上调用startAsync() 方法。这段代码不会阻塞执行线程,但会在单独的线程中执行I/O操作,并在准备好后使用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();
            }
        });
    }
}

We are writing the ByteBuffer to the OutputStream, and once the whole buffer is written we are signaling that result is ready to return to the client by invoking the complete() method.

我们正在将ByteBuffer写入OutputStream,一旦整个缓冲区被写入,我们就会通过调用complete()方法来示意结果可以返回给客户端。

Next, we need to add the AsyncServlet as a Jetty servlet mapping:

接下来,我们需要添加AsyncServlet作为Jetty servlet的映射。

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

We can now send a request to the /heavy/async endpoint – that request will be handled by the Jetty in an asynchronous way:

我们现在可以向/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");

When our application is handling requests in an asynchronous way, we should configure thread pool explicitly. In the next section, we will configure Jetty to use a custom thread pool.

当我们的应用程序以异步的方式处理请求时,我们应该明确配置线程池。在下一节,我们将配置Jetty使用一个自定义的线程池。

5. Jetty Configuration

5.Jetty配置

When we run our web application on production, we want might want to tune how the Jetty server processes requests. This is done by defining thread pool and applying it to our Jetty server.

当我们在生产中运行我们的Web应用程序时,我们可能希望调整Jetty服务器处理请求的方式。这可以通过定义线程池并将其应用于Jetty服务器来实现。

To do this, we have three configuration settings that we can set:

要做到这一点,我们有三个可以设置的配置。

  • maxThreads – To specify the maximum number of threads that Jetty can create and use in the pool
  • minThreads – To set the initial number of threads in the pool that Jetty will use
  • idleTimeout – This value in milliseconds defines how long a thread can be idle before it is stopped and removed from the thread pool. The number of remaining threads in the pool will never go below the minThreads setting

With these we can configure the embedded Jetty server programmatically by passing the configured thread pool to the Server constructor:

有了这些,我们可以通过将配置的线程池传递给服务器构造器,以编程方式配置嵌入式Jetty服务器。

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

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

server = new Server(threadPool);

Then, when we start our server it will be using threads from a specific thread pool.

然后,当我们启动服务器时,它将使用特定线程池的线程。

6. Conclusion

6.结论

In this quick tutorial, we saw how to integrate embedded servers with Jetty and tested our web application.

在这个快速教程中,我们看到了如何将嵌入式服务器与Jetty集成,并测试了我们的Web应用程序。

As always, the code is available over on GitHub.

一如既往,代码可在GitHub上获得。。