4 回答

TA貢獻1804條經驗 獲得超7個贊
對于 servlet 3.1,您可以使用 Reactive Streams 橋支持非阻塞 I/O
Servlet 3.1+ 容器
要作為 WAR 部署到任何 Servlet 3.1+ 容器,您可以在 WAR 中擴展和包含 {api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[AbstractReactiveWebInitializer]。該類使用 ServletHttpHandlerAdapter 包裝 HttpHandler 并將其注冊為 Servlet。
所以你應該擴展AbstractReactiveWebInitializer來添加異步支持
registration.setAsyncSupported(true);
以及ServletHttpHandlerAdapter中的支持
AsyncContext asyncContext = request.startAsync();

TA貢獻2036條經驗 獲得超8個贊
追查一些例子不應該太難。我在WASdev/sample.javaee7.servlet.nonblocking找到了來自 IBM 的一個。在 Spring 或 Spring Boot 中使用javax.servletAPI 只是要求 Spring 注入HttpServletRequest或HttpServletResponse. 所以,一個簡單的例子可能是:
@SpringBootApplication
@Controller
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequestMapping(path = "")
public void writeStream(HttpServletRequest request, HttpServletResponse response) throws IOException {
ServletOutputStream output = response.getOutputStream();
AsyncContext context = request.startAsync();
output.setWriteListener(new WriteListener() {
@Override
public void onWritePossible() throws IOException {
if ( output.isReady() ) {
output.println("WriteListener:onWritePossible() called to send response data on thread : " + Thread.currentThread().getName());
}
context.complete();
}
@Override
public void onError(Throwable t) {
context.complete();
}
});
}
}
這只是創建一個WriteListener并將其附加到請求輸出流,然后返回。沒有什么花哨。
編輯:重點是 servlet 容器(例如 Tomcat)onWritePossible在可以無阻塞地寫入數據時調用。有關更多信息,請參見使用 Servlet 3.1 的非阻塞 I/O:使用 Java EE 7 的可擴展應用程序(TOTD #188)。.
偵聽器(和編寫器)具有回調方法,當內容可供讀取或可以無阻塞寫入時調用這些回調方法。
因此因此onWritePossible只有在out.println可以無阻塞地調用時才被調用。
調用 setXXXListener 方法表示使用非阻塞 I/O 而不是傳統的 I/O。
大概你必須做什么檢查output.isReady才能知道你是否可以繼續寫字節。似乎您必須與發送方/接收方就塊大小達成某種隱式協議。我從來沒有使用過它,所以我不知道,但是你在 Spring 框架中要求了一個這樣的例子,這就是所提供的。
因此 onWritePossible 僅在可以無阻塞地調用 out.println 時調用。 這聽起來是正確的,但我如何理解可以寫入多少字節?我應該如何控制它?
編輯 2:這是一個非常好的問題,我無法給你一個確切的答案。我假設onWritePossible當服務器在與主 servlet 分開的(異步)線程中執行代碼時調用它。從您檢查的示例中,input.isReady()或者output.isReady()我假設它會阻塞您的線程,直到發送方/接收方準備好接收更多消息。由于這是異步完成的,服務器本身不會被阻塞并且可以處理其他請求。我從來沒有用過這個,所以我不是專家。
當我說發送方/接收方將就塊大小達成某種隱式協議時,這意味著如果接收方能夠接受 1024 字節的塊,那么您將在為真時寫入該數量output.isReady。您必須通過閱讀文檔來了解這一點,而 api 中沒有關于它的內容。否則你可以寫單個字節,但 oracle 的示例使用 1024 字節塊。1024 字節塊是流式 I/O 的相當標準的塊大小。上面的示例必須擴展為在while循環中寫入字節,就像在 oracle 示例中顯示的那樣。這是留給讀者的練習。
Project reactor 和 Spring Webflux 的概念backpressure可能會更仔細地解決這個問題。那將是一個單獨的問題,我沒有仔細研究發送者和接收者是如何結合的(反之亦然)。

TA貢獻1851條經驗 獲得超3個贊
Servlet 3.0 - 解耦容器線程和處理線程。返回 DeferredResult 或 CompletableFuture。所以控制器處理可以在與服務器請求處理線程不同的線程中。服務器線程池可以自由處理更多傳入請求。
但是,IO 仍然處于阻塞狀態。讀取和寫入輸入和輸出流(接收和發送對慢速客戶端的響應)。
Servlet 3.1 - 一路非阻塞,包括 IO。直接在 Spring 上使用 Servlet 3.1 意味著你必須使用ReadListener和WriteListener接口,太麻煩了。此外,您必須避免使用同步和阻塞的 Servlet API,例如 Servlet 和 Filter。使用 Spring Webflux,它使用 Reactive Streams API 并支持 Servlet 3.1。

TA貢獻1880條經驗 獲得超4個贊
如果您正在尋找 Spring/Servlet 3.1 非阻塞 HTTP API 聲明的示例,請嘗試以下操作:
@GetMapping(value = "/asyncNonBlockingRequestProcessing")
public CompletableFuture<String> asyncNonBlockingRequestProcessing(){
ListenableFuture<String> listenableFuture = getRequest.execute(new AsyncCompletionHandler<String>() {
@Override
public String onCompleted(Response response) throws Exception {
logger.debug("Async Non Blocking Request processing completed");
return "Async Non blocking...";
}
});
return listenableFuture.toCompletableFuture();
}
需要在 Servlet 容器級別支持 Spring Web 5.0+ 和 Servlet 3.1(Tomcat 8.5+、Jetty 9.4+、WildFly 10+)
添加回答
舉報