websocket深入-webflux+websocket

文章目錄

  • 背景
  • 版本約定
  • 配置文件
  • 代碼
    • 使用webflux
    • 使用websocket
      • 配置文件
      • handler基類
      • 實現類
      • 注冊路由

背景

基于更復雜的情況和更高的開發要求,我們可能會遇到必須同時要使用webflux和websocket的情況。

版本約定

  • JDK21
  • Springboot 3.2.0
  • Fastjson2
  • lombok

配置文件

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version>
</parent>
<properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><!-- Spring Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.54</version></dependency>
</dependencies>

代碼

只要引入webflux,就同時引入了websocket,不需要再次引入websocket

使用webflux

@RestController
@RequestMapping("/user")
public class UserFlux {@Autowiredprivate UserService userService;@GetMapping("/get")public Mono<Result<User>> get() {return Mono.just(Result.httpSuccess(userService.getUser()));}/*** 服務器推送** @return 由服務器決定推送多少次多少數據,推送結束前不會斷開連接** @apiNote (SSE - > Server Send Event)*/@GetMapping(value = "/flux", produces = MediaType.APPLICATION_JSON_VALUE)public Flux<String> flux() {return Flux.fromStream(IntStream.range(1, 5).mapToObj(i -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException ignored) {}JSONObject obj = new JSONObject();obj.put("data", "hello,flux" + i);return obj.toJSONString();}));}
}

這里比較值得注意的是Flux返回值,這個返回值從性質上說有點像會自動close的websocket。我們看下這個/flux的返回值:


{"data": "hello,flux1"
}{"data": "hello,flux2"
}{"data": "hello,flux3"
}{"data": "hello,flux4"
}

注意這不是我拼接的,是調試結果就是這樣。也就是說,/flux是分幀輸出,具有流式的特性。

使用websocket

這里選擇使用手動注冊websocket而非Endpoint自動注解,主要是因為我想對handler做規范化

配置文件

@Configuration(proxyBeanMethods = false)
public class ReactiveWebSocketConfiguration {@Beanpublic WebSocketHandlerAdapter webSocketHandlerAdapter() {return new WebSocketHandlerAdapter();}
}

handler基類

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
// 強制規定基類必須有泛型約束入參出參。強制規定必須進行參數校驗
@Slf4j
public abstract class BaseSocketHandler<T, R> implements WebSocketHandler {@Override@NonNullpublic Mono<Void> handle(WebSocketSession session) {String sessionId = session.getId();log.info("與sessionId:【{}】 建立連接", sessionId);Flux<WebSocketMessage> receive = session.receive();Flux<R> fluxHandled = receive.flatMap(webSocketMessage -> {String payloadAsText = webSocketMessage.getPayloadAsText();if (!JSON.isValid(payloadAsText)) {log.error("收取參數不合法:{}", payloadAsText);session.close();throw new IllegalArgumentException("參數不合法");}TypeReference<T> reference = getTypeReference();if (!check(payloadAsText, reference)) {log.error("參數校驗不通過:{}", payloadAsText);session.close();throw new IllegalArgumentException("參數校驗不通過");}return handler(payloadAsText, reference);}).onErrorResume(throwable -> {log.error("連接異常,即將關閉", throwable);session.close();return Mono.error(throwable);});return session.send(Mono.from(fluxHandled).map(payload -> session.textMessage(JSON.toJSONString(payload))));}public abstract boolean check(String payloadObject, TypeReference<T> typeReference);public abstract Mono<R> handler(String payload, TypeReference<T> typeReference);protected abstract TypeReference<T> getTypeReference();
}

實現類

// 這樣繼承基類的handler使用時非常簡單不說,由于上層做了處置,還會更安全更好做日志
public class NoticeHandler extends BaseSocketHandler<User, UserInfo> {@Overridepublic boolean check(String payloadObject, TypeReference<User> userTypeReference) {User user = JSON.parseObject(payloadObject, userTypeReference);return !Objects.isNull(user.getId()) && user.getId() > 0;}@Overridepublic Mono<UserInfo> handler(String payload, TypeReference<User> typeReference) {User user = JSON.parseObject(payload, typeReference);UserInfo userInfo = new UserInfo();BeanUtils.copyProperties(user, userInfo);return Mono.just(userInfo);}@Overrideprotected TypeReference<User> getTypeReference() {return new TypeReference<>() {};}
}

注冊路由

import com.xu.socket.NoticeHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;import java.util.HashMap;
import java.util.Map;@Component
public class ReactiveWebSocketServerHandlerMapping extends SimpleUrlHandlerMapping {public ReactiveWebSocketServerHandlerMapping() {Map<String, WebSocketHandler> map = new HashMap<>();map.put("/ws/notice", new NoticeHandler());setUrlMap(map);setOrder(100);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76347.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76347.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76347.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

致遠OA —— 表單數據獲取(前端)

文章目錄 :apple: 業務需求描述 &#x1f34e; 業務需求描述 測試案例&#xff1a; https://pan.quark.cn/s/3f58972f0a27 官網地址&#xff1a; 需求描述&#xff1a; 點擊獲取數據接口&#xff0c;調用后臺&#xff0c;將從后臺查詢到的數據回寫到表單的內容中。 如下…

51c嵌入式~繼電器~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/13775821 一、繼電器應用細節 繼電器的應用&#xff0c;相信大家都知道&#xff0c;在電路中只要給它供電、斷電也就可以工作了。本文討論它的應用細節。 現在流行的接法 圖中&#xff0c;繼電器的線圈經過Q1作為開關&am…

前端性能優化核彈級方案:CSS分層渲染+Wasm,首屏提速300%!

前端性能優化核彈級方案&#xff1a;CSS分層渲染Wasm實現首屏提速300%的終極指南 在當今Web應用日益復雜的背景下&#xff0c;性能優化已成為前端開發的核心競爭力。本文將深入剖析兩種革命性的前端性能優化技術——CSS分層渲染與WebAssembly(Wasm)的協同應用&#xff0c;揭示…

初識Redis · 簡單理解Redis

目錄 前言&#xff1a; 分布式系統 開源節流 認識Redis 負載均衡 緩存 微服務 前言&#xff1a; 本文只是作為Redis的一篇雜談&#xff0c;簡單理解一下Redis為什么要存在&#xff0c;以及它能做到和它不能做到的事兒&#xff0c;簡單提及一下它對應的優勢有什么&#…

網絡通訊協議UDP轉發TCP工具_UdpToTcpRelay_雙向版

UDP/TCP網絡轉發器程序說明書 1. 程序概述 本程序是一個高性能網絡數據轉發工具&#xff0c;支持UDP和TCP協議之間的雙向數據轉發&#xff0c;并具備以下核心功能&#xff1a; 協議轉換&#xff1a;實現UDP?TCP協議轉換數據轉換&#xff1a;支持十六進制/ASCII格式的數據轉…

MCP 服務搭建與配置學習資源部分匯總

MCP 服務搭建與配置學習資源匯總 目錄 圖文教程GitHub 示例項目視頻課程不同開發語言實現案例 圖文教程 Cherry Studio 配置 MCP 服務教程 – 介紹如何在 Cherry Studio 客戶端中配置 MCP 服務器&#xff0c;讓 AI 模型能夠自主調用本地/網絡工具來完成任務&#xff0c;提升…

Selenium中`driver.get(htmlfile)`方法可能出現的超時問題

針對Selenium中driver.get(htmlfile)方法可能出現的超時問題&#xff0c;以下是幾種改進方案及具體實現方法&#xff1a; 1. 設置頁面加載超時時間 通過set_page_load_timeout()方法直接控制頁面加載的最大等待時間。若超時&#xff0c;會拋出TimeoutException異常&#xff0c…

20分鐘了解 MMAction2 框架設計

步驟3&#xff1a;構建一個識別器 # 修改此處 predictions[0].pred_score -> predictions[0].pred_scores.item print(Scores of Sample[0], predictions[0].pred_scores.item)步驟4&#xff1a;構建一個評估指標 # 修改此處 data_sample[pred_score].cpu().numpy() ->…

單軌小車懸掛輸送機安全規程

導語 大家好&#xff0c;我是社長&#xff0c;老K。專注分享智能制造和智能倉儲物流等內容。歡迎大家使用我們的倉儲物流技術AI智能體。 新書《智能物流系統構成與技術實踐》 新書《智能倉儲項目出海-英語手冊&#xff0c;必備&#xff01;》 完整版文件和更多學習資料&#xf…

C++之多態

文章目錄 一、多態的概念 多態的定義與類型 二、多態的實現 三、虛函數 虛函數的概念 虛函數的重寫/覆蓋 協變 析構函數的重寫/覆蓋 override,final關鍵字 override final 純虛函數與抽象類 三個概念辨析 四、多態實現的原理 虛函數表指針 動態綁定與靜態綁定 …

深入理解 HTML5 Audio:網頁音頻播放的新時代

在網頁開發領域,音頻的嵌入和播放一直是一個重要且不斷演進的話題。HTML5 的出現,為網頁音頻播放帶來了標準化的解決方案,極大地改善了開發者和用戶的體驗。 一、HTML5 之前的音頻播放狀況 在 HTML5 誕生之前,互聯網上缺乏統一的網頁音頻播放標準。當時,大多數音頻播放依…

重載和重寫的區別

重載 在同一個類中定義多個同名方法&#xff0c; 但參數列表不同&#xff08;參數類型、參數個數或參數順序不同&#xff09;返回值類型不同。 public class MathOperations {int add(int a, int b) {return a b;}double add(double a, double b) {return a b;} }重寫 子…

機器視覺+深度學習,讓電子零部件表面缺陷檢測效率大幅提升

在精密加工的3C電子行業中&#xff0c;一抹0.1毫米的油漬&#xff0c;一粒肉眼難辨的灰塵或將引發整機性能隱患。當制造業邁入微米級品質競爭時代&#xff0c;產品表面看似微不足道的臟污缺陷&#xff0c;正成為制約企業高質量發展的隱形枷鎖。分布無規律的污漬斑點、形態各異的…

Dart逆向之函數調用

我們從Blutter恢復的部分IL中可以看到Dart調用函數的邏輯 // 0x180490: r16 <int> // 0x180490: ldr x16, [PP, #0x8a0] ; [pp0x8a0] TypeArguments: <int> // 0x180494: r30 Instance_MethodChannel // 0x180494: ldr lr, [P…

如何白嫖Grok3 API? 如何使用Grok3 API調用實例?怎么使用Grok3模型?

前段時間&#xff0c;Grok3&#xff08;想要體驗Grok3的童鞋可以參考本文&#xff1a;Grok 上線角色扮演功能&#xff0c;教你課后作業手到擒來&#xff0c;Grok3使用次數限制&#xff1f;如何使用Grok3? Grok3國內支付手段如何訂閱升級Premium - AI is all your need!&#x…

《超短心法》速讀筆記

文章目錄 書籍信息概覽主線行業篇戰法一 人氣漲停戰法戰法二 四維主線戰法 主線龍頭篇戰法三 龍頭起爆戰法戰法四 六合強莊控盤戰法戰法五 籌碼戰法之七星連珠 趨勢牛股篇戰法六 趨勢擒龍之暴漲形態戰法七 趨勢破位起爆戰法戰法八 強中選強多頭戰法 漲停晉級篇戰法九 強勢漲停狙…

git倉庫遷移包括提交記錄日志

網上找了很多資料都不好用&#xff0c;直到看到一個親測有效后&#xff0c;整理如下&#xff1a; 1、進入倉庫目錄下&#xff0c;并且切換到要遷移的分支上 前提是你本地已有舊倉庫的代碼&#xff1b;如果沒有的話&#xff0c;先拉取。 2、更改倉庫地址 git remote set-url …

powerDesign 逆向 mysql 生成 物理模型,并用VBS腳本整理comment

學習自&#xff1a;https://www.cnblogs.com/xmyjcs/p/8536233.html 文章目錄 Reverse Engineer格式化模型執行 VBS 腳本 Reverse Engineer 下面 DBMS 可以通過 ODBC&#xff08;Open Database Connectivity&#xff0c;開放數據庫連接&#xff09;連接&#xff0c; 需要自己先…

Qt文件讀寫

Qt文件讀寫&#xff08;Stream流形式&#xff09; 文件讀寫相關類 1. QFile類 QFile主要用于文件的打開、關閉等功能&#xff1b; [override virtual] bool QFile::open(QIODevice::OpenMode mode);Reimplements: QIODevice::open(QIODevice::OpenMode mode). Opens the fi…

[特殊字符]【高并發實戰】Java Socket + 線程池實現高性能文件上傳服務器(附完整源碼)[特殊字符]

大家好&#xff01;今天給大家分享一個 Java Socket 線程池 實現的高性能文件上傳服務器&#xff0c;支持 多客戶端并發上傳&#xff0c;代碼可直接運行&#xff0c;適合 面試、項目實戰、性能優化 學習&#xff01; &#x1f4cc; 本文亮點&#xff1a; ? 完整可運行代碼&a…