【问题标题】:Java HttpsServer multi threadedJava HttpsServer 多线程
【发布时间】:2017-09-02 07:26:56
【问题描述】:

我已经用 Java 设置了一个 HttpsServer。我所有的沟通都完美无缺。我设置了多个上下文,加载自签名证书,甚至基于外部配置文件启动。

我现在的问题是让多个客户端能够访问我的安全服务器。为此,我想以某种方式对来自 HttpsServer 的请求进行多线程处理,但不知道该怎么做。下面是我的基本 HttpsConfiguration。

  HttpsServer server = HttpsServer.create(new InetSocketAddress(secureConnection.getPort()), 0);
  SSLContext sslContext = SSLContext.getInstance("TLS");

  sslContext.init(secureConnection.getKeyManager().getKeyManagers(), secureConnection.getTrustManager().getTrustManagers(), null);

  server.setHttpsConfigurator(new SecureServerConfiguration(sslContext));
  server.createContext("/", new RootHandler());
  server.createContext("/test", new TestHandler());
  server.setExecutor(Executors.newCachedThreadPool());
  server.start();

其中secureConnection 是一个包含服务器设置和证书信息的自定义类。

我试图将执行程序设置为Executors.newCachedThreadPool() 和其他几个。但是,它们都产生了相同的结果。每个都以不同的方式管理线程,但第一个请求必须在第二个请求处理之前完成。

我也尝试编写自己的 Executor

public class AsyncExecutor extends ThreadPoolExecutor implements Executor
{
   public static Executor create()
   {
      return new AsyncExecutor();
   }

   public AsyncExecutor()
   {
      super(5, 10, 10000, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(12));
   }

   @Override
   public void execute(Runnable process)
   {
      System.out.println("New Process");

      Thread newProcess = new Thread(process);
      newProcess.setDaemon(false);

      newProcess.start();

      System.out.println("Thread created");
   }
}

不幸的是,结果与其他执行者相同。

为了进行测试,我使用 Postman 来访问 /Test 端点,该端点通过执行 Thread.sleep(10000) 来模拟长时间运行的任务。当它运行时,我正在使用我的 Chrome 浏览器来访问根端点。直到 10 秒睡眠结束后,才会加载根页面。

关于如何处理对 HTTPS 服务器的多个并发请求有什么想法吗?

为了便于测试,我使用标准 HttpServer 复制了我的场景,并将所有内容压缩到一个 java 程序中。

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class Example
{
   private final static int PORT = 80;
   private final static int BACKLOG = 10;

   /**
    * To test hit:
    * <p><b>http://localhost/test</b></p>
    * <p>This will hit the endoint with the thread sleep<br>
    * Then hit:</p>
    * <p><b>http://localhost</b></p>
    * <p>I would expect this to come back right away. However, it does not come back until the
    * first request finishes. This can be tested with only a basic browser.</p>
    * @param args
    * @throws Exception
    */
   public static void main(String[] args) throws Exception
   {
      new Example().start();
   }

   private void start() throws Exception
   {
      HttpServer server = HttpServer.create(new InetSocketAddress(PORT), BACKLOG);

      server.createContext("/", new RootHandler());
      server.createContext("/test", new TestHandler());
      server.setExecutor(Executors.newCachedThreadPool());
      server.start();

      System.out.println("Server Started on " + PORT);
   }

   class RootHandler implements HttpHandler
   {
      @Override
      public void handle(HttpExchange httpExchange) throws IOException
      {
         String body = "<html>Hello World</html>";

         httpExchange.sendResponseHeaders(200, body.length());
         OutputStream outputStream = httpExchange.getResponseBody();

         outputStream.write(body.getBytes("UTF-8"));
         outputStream.close();
      }
   }

   class TestHandler implements HttpHandler
   {
      @Override
      public void handle(HttpExchange httpExchange) throws IOException
      {
         try
         {
            Thread.sleep(10000);
         }
         catch (InterruptedException e)
         {
            e.printStackTrace();
         }

         String body = "<html>Test Handled</html>";

         httpExchange.sendResponseHeaders(200, body.length());
         OutputStream outputStream = httpExchange.getResponseBody();

         outputStream.write(body.getBytes("UTF-8"));
         outputStream.close();
      }
   }
}

【问题讨论】:

  • 您可能需要添加一些源代码来展示如何设置端口处理并实际处理 http 服务器中的各个请求:据我们所知,您一次只接受一个套接字。跨度>
  • 如何更改 HttpsServer 以接受多个套接字?
  • 它已经做到了。您如何处理接受的套接字?
  • 我自己没有对套接字进行任何操作。我以为 HttpsServer 已经为我处理好了。
  • 我仍然无法解决这个问题。有没有人有其他想法?

标签: java multithreading http https threadpoolexecutor


【解决方案1】:

TL;DR:没关系,用两个不同的浏览器,或者专门的工具测试一下就行了。

您的原始实现没问题,它按预期工作,不需要自定义执行器。对于每个请求,它都会执行“共享”处理程序类实例的方法。它总是从池中提取空闲线程,因此每个方法调用都在不同的线程中执行。

问题似乎是,当您使用同一浏览器的多个窗口来测试此行为时......由于某种原因,请求以序列化方式执行(当时只有一个)。使用最新的 Firefox、Chrome、Edge 和 Postman 测试。 Edge 和 Postman 按预期工作。 Firefox 和 Chrome 的匿名模式也有帮助。

从两个 Chrome 窗口同时打开相同的本地 URL。首先在 5 秒后加载的页面,我得到了 Thread.sleep(5000) 所以没关系。第二个窗口加载响应时间为 8.71 秒,因此存在 3.71 秒的未知延迟。

我的猜测?可能是一些浏览器内部优化或故障保护机制。

【讨论】:

    【解决方案2】:

    尝试指定一个非零的最大积压(create() 的第二个参数):

    HttpsServer server = HttpsServer.create(new InetSocketAddress(secureConnection.getPort()), 10);
    

    【讨论】:

    • 我也玩过这个。这与与服务器关联的积压有关。我什至在这个领域测试了多达 1000 个。我仍然得到相同的结果。
    【解决方案3】:

    我做了一些实验,对我有用的是:

    public void handler(HttpExchange exchange) {
        executor.submit(new SomeOtherHandler());
    }
    
    public class SomeOtherHandler implements Runnable {
    
    }
    

    其中执行器是您创建为线程池的那个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多