4_webflux服務端開發
一 初識Web Flux
- 概念
Web Flux是Spring5.0提出的新的開發web的技術棧,它是一種非阻塞的開發模式,它執行在Netty或者Servlet 3.1的容器上邊,支援非常高的併發量,也就是說我們現在開發Web應用又有了一種新的開發模式,可以使用之前的mvc開發模式也可以使用web Flux開發模式;
- Web Flux 與 MVC的區別
2.1 開發方式的不同:Web Flux是非同步非阻塞的開發模式,MVC同步的阻塞的I/O開發模式;
2.2 執行環境的不同:MVC的執行環境是基於Servlet API的,所以它必須執行在Servlet容器上邊,而Web Flux的開發模式是基於Reactive Stream流的方式,它可以執行在Netty或者Servlet 3.1及以後版本的容器上邊;其實Spring預設的容器就是Netty;
2.3 資料庫型別的不同:關係型資料庫(mysql等)暫時不支援響應式的開發模式;
- Web Flux的優勢
支援非常高的併發量;
二 非同步Servlet
- 為什麼要使用非同步Servlet?
因為使用非同步Servlet不會阻塞Tomcat的Servlet執行緒,所以非同步Servlet可以達到非常高的吞吐量,可以處理非常高的併發;
- 同步Servlet阻塞了什麼?
2.1 其實阻塞的是Tomcat容器的Servlet執行緒,當有網路請求傳送到Tomcat以後,Tomcat會為每一個請求分配一個執行緒去處理,執行緒會呼叫對應的Servlet容器去處理,在這個處理的過程中,相關的業務程式碼所花費的時間就是Servlet執行緒等待的時間,這就是同步Servlet的阻塞;
程式碼演示:
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class SyncServlet
*/
@WebServlet("/SyncServlet")
public class SyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SyncServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
long t1 = System.currentTimeMillis();
// 執行業務程式碼
doSomeThing(request, response);
System.out.println("sync use:" + (System.currentTimeMillis() - t1));
}
private void doSomeThing(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// 模擬耗時操作
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
response.getWriter().append("done");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 非同步Servlet是怎麼工作的?
因為使用非同步Servlet不會阻塞Tomcat的Servlet執行緒,它會將請求放在獨立的執行緒池中(特別是對於比較耗時的操作),結果會立馬被返回,結果返回後就接著處理下一個請求,所以非同步Servlet可以達到非常高的吞吐量;
程式碼演示:
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class AsyncServlet
*/
@WebServlet(asyncSupported = true, urlPatterns = { "/AsyncServlet" })
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public AsyncServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
long t1 = System.currentTimeMillis();
// 開啟非同步
AsyncContext asyncContext = request.startAsync();
// ִ執行業務程式碼
CompletableFuture.runAsync(() -> doSomeThing(asyncContext,
asyncContext.getRequest(), asyncContext.getResponse()));
System.out.println("async use:" + (System.currentTimeMillis() - t1));
}
private void doSomeThing(AsyncContext asyncContext,
ServletRequest servletRequest, ServletResponse servletResponse) {
// 模擬耗時操作
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
//
try {
servletResponse.getWriter().append("done");
} catch (IOException e) {
e.printStackTrace();
}
// 業務程式碼處理完畢,通知結束
asyncContext.complete();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
三 Web Fulx開發效率對比
package com.imooc;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@Slf4j
public class TestController {
@GetMapping("/1")
private String get1() {
log.info("get1 start");
String result = createStr();
log.info("get1 end.");
return result;
}
@GetMapping("/2")
private Mono<String> get2() {
log.info("get2 start");
Mono<String> result = Mono.fromSupplier(() -> createStr());
log.info("get2 end.");
return result;
}
/**
* Flux : 返回0-n個元素
*
* @return
*/
@GetMapping(value = "/3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
private Flux<String> flux() {
Flux<String> result = Flux
.fromStream(IntStream.range(1, 5).mapToObj(i -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
return "flux data--" + I;
}));
return result;
}
private String createStr() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
return "some string";
}
}
從上邊可以看出,get1(正常模式)執行時間為5秒,get2(Web Flux模式)執行基本沒有時間損耗,效能更高;關於原因已經在上邊進行了闡述這裡就不在進行說明了;
四 SSE(server-sent events)
- SSE的本質
伺服器向客戶端宣告,接下來要傳送的是流資訊(streaming),也就是說,傳送的不是一次性的資料包,而是一個資料流,會連續不斷地傳送過來。這時,客戶端不會關閉連線,會一直等著伺服器發過來的新的資料流,視訊播放就是這樣的例子。
- SSE的服務端實現
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class SSE
*/
@WebServlet("/SSE")
public class SSE extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SSE() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 實現SSE必須要設定的內容
response.setContentType("text/event-stream");
response.setCharacterEncoding("utf-8");
for (int i = 0; i < 5; i++) {
// 指定事件標識
response.getWriter().write("event:me\n");
// 格式: data: + 資料 + 兩個回車
response.getWriter().write("data:" + i + "\n\n");
response.getWriter().flush();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- SSE的前端實現(基於H5)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
// 初始化, 引數為url
// 依賴H5
var sse = new EventSource("SSE");
sse.onmessage = function(e) {
console.log("message", e.data, e);
}
// 監聽指定事件, (就不會進入onmessage了)
sse.addEventListener("me", function(e) {
console.log("me event", e.data);
// 如果不關閉,會自動重連
if (e.data == 3) {
sse.close();
}
});
</script>
</body>
</html>
- 效果展示
我們可以自定義我們的訊息返回,請看圖示但是此時我們發現一個問題,就是如果不主動關閉的話,瀏覽器會一直的接受這個流,所以我們需要手動關閉,當執行到4的時候又從頭開始接受了,所以我們限制一下讓他接受到3的時候就關閉這個動作
- 更多關於SSE的問題請參考阮一峰的網路日誌
相關文章
- Kotlin + SpringBoot + JPA 服務端開發KotlinSpring Boot服務端
- go語言遊戲服務端開發(三)——服務機制Go遊戲服務端
- 最右app——服務端開發工程師(go)APP服務端工程師Go
- 【深圳shopee】Go 服務端開發工程師Go服務端工程師
- 網路開發基礎服務端001服務端
- 那些需要自己開發的安全需求(服務端)服務端
- Netty服務端開發及效能最佳化Netty服務端
- Java服務端和客戶端開發輔助工具UtilsJava服務端客戶端
- 5分鐘搞定 服務端 本地開發 遠端執行服務端
- web學習:服務端開發的業務需求-路由解析Web服務端路由
- [翻譯]微服務設計模式 - 5. 服務發現 - 服務端服務發現微服務設計模式服務端
- TCP程式設計之服務端和客戶端的開發TCP程式設計服務端客戶端
- 跨端開發SAAS級服務助力研發降本增效跨端
- 華為帳號服務學習筆記(四):Authorization Code模式服務端開發筆記模式服務端
- 服務端測試開發必備技能:Mock測試服務端Mock
- 【新加坡】Shopee: Golang服務端或者平臺開發內推Golang服務端
- Flutter 全棧開發體驗——爬蟲與服務端Flutter全棧爬蟲服務端
- 使用.NET開發搭建OpenAI模型的中間服務端OpenAI模型服務端
- 【.NET6】gRPC服務端和客戶端開發案例,以及minimal API服務、gRPC服務和傳統webapi服務的訪問效率大對決RPC服務端客戶端APIWeb
- 怎樣開啟mongodb服務端?MongoDB服務端
- win10 如何開啟遠端服務_win10如何開啟遠端連線服務Win10
- go語言遊戲服務端開發(四)——RPC機制Go遊戲服務端RPC
- 教你使用rest雲服務介面,自己完成前後端開發REST後端
- Docker下Java檔案上傳服務三部曲之二:服務端開發DockerJava服務端
- 服務端渲染到前端渲染,再到“服務端渲染”服務端前端
- nuxt.js實現服務端渲染ssr (一)(環境配置、 多環境開發、程式守護、服務端映象)UXJS服務端
- 服務端,客戶端服務端客戶端
- 客戶端,服務端客戶端服務端
- TCP服務端TCP服務端
- YApi 服務端測試新增 globalCookie ,相容自動化觸發服務端測試功能API服務端Cookie
- 服務端開發學習路徑圖,心疼小哥哥們服務端
- [新加坡] Shopee Golang 服務端或者平臺開發工程師內推Golang服務端工程師
- 網頁開發方式-從靜態頁面到服務端渲染網頁服務端
- Sentry 開發者貢獻指南 - 後端服務(Python/Go/Rust/NodeJS)後端PythonGoRustNodeJS
- C#開發BIMFACE系列43 服務端API之圖紙拆分C#服務端API
- C#開發BIMFACE系列41 服務端API之模型對比C#服務端API模型
- go語言遊戲服務端開發(二)——網路通訊Go遊戲服務端
- HarmonyOS 位置服務開發指南