[英]Java HttpsServer multi threaded
我已經在 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");
}
}
不幸的是,結果與其他 Executor 相同。
為了測試,我使用 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();
}
}
}
TL;DR:沒關系,只需使用兩種不同的瀏覽器,或專門的工具來測試它。
您最初的實現是可以的,它按預期工作,不需要自定義 Executor。 對於每個請求,它執行“共享”處理程序類實例的方法。 它總是從池中獲取空閑線程,因此每個方法調用都在不同的線程中執行。
問題似乎是,當您使用同一瀏覽器的多個窗口來測試此行為時……出於某種原因,請求以序列化方式執行(當時只有一個)。 使用最新的 Firefox、Chrome、Edge 和 Postman 進行測試。 Edge 和 Postman 按預期工作。 Firefox 和 Chrome 的匿名模式也有幫助。
從兩個 Chrome 窗口同時打開相同的本地 URL。 首先頁面在 5 秒后加載,我得到了 Thread.sleep(5000) 所以沒關系。 第二個窗口在 8.71 秒內加載響應,因此有 3.71 秒的未知來源延遲。
我猜? 可能是一些瀏覽器內部優化或故障保護機制。
嘗試指定一個非零的最大積壓( create()
的第二個參數):
HttpsServer server = HttpsServer.create(new InetSocketAddress(secureConnection.getPort()), 10);
我做了一些實驗,對我有用的是:
public void handler(HttpExchange exchange) {
executor.submit(new SomeOtherHandler());
}
public class SomeOtherHandler implements Runnable {
}
其中 executor 是您創建為線程池的那個。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.