Spring WebFlux 中 WebSocket 使用 DataBuffer 的注意事項

以下是修改后的完整文檔,包含在多個多線程環境中使用 retain()release() 方法的示例,且確保在 finally 塊中調用 release()


在 Spring WebFlux 中,WebSocketMessage 主要用于表示 WebSocket 的消息載體,其中 getPayload() 方法返回 DataBuffer,用于處理二進制數據流。在使用 DataBuffer 時,需要注意其一次性讀取特性,以及潛在的內存管理問題。本文將介紹如何正確使用 DataBuffer,避免重復讀取和內存泄漏。

1. 避免重復讀取 DataBuffer

DataBuffer 設計為一次性讀取流數據,因此,一旦被消費,后續讀取將無法獲取數據。例如:

String firstRead = webSocketMessage.getPayload().toString(StandardCharsets.UTF_8);
String secondRead = webSocketMessage.getPayload().toString(StandardCharsets.UTF_8); // 此處讀取會失敗

解決方案

如果需要多次使用 DataBuffer 的數據,可以在第一次讀取時緩存:

DataBuffer dataBuffer = webSocketMessage.getPayload();
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String payload = new String(bytes, StandardCharsets.UTF_8);

這樣,后續可以安全地使用 payload 變量,而不會影響 DataBuffer


2. 避免阻塞操作

Spring WebFlux 是基于響應式編程的,WebSocket 處理也應保持非阻塞。如果在 DataBuffer 處理中引入了阻塞操作(如同步 I/O 或 Thread.sleep()),可能會導致 Reactor 線程阻塞,影響整體吞吐量。

解決方案

使用 Flux/Mono 進行異步處理,例如:

session.receive().map(WebSocketMessage::getPayloadAsText)  // 避免直接操作 DataBuffer.flatMap(payload -> processMessage(payload)).subscribe();

3. 處理 DataBuffer 可能帶來的內存泄漏

Spring WebFlux 采用 Netty 作為默認底層引擎,而 Netty 的 ByteBuf 需要手動釋放,否則可能導致內存泄漏。Spring 提供了 DataBufferUtils.release() 方法來避免 DataBuffer 占用資源不被回收。

正確的釋放方式

session.receive().doOnNext(message -> {try {String data = message.getPayloadAsText();System.out.println("Received: " + data);} finally {DataBufferUtils.release(message.getPayload());}}).subscribe();

DataBufferUtils.release() 僅在手動管理 DataBuffer 生命周期時才需要,如果直接通過 WebSocketMessage.getPayloadAsText() 處理字符串,不必顯式釋放。


4. 在 Flux/Mono 組合操作時避免數據丟失

如果 DataBuffermap() 操作多次消費,可能導致數據丟失或 DataBuffer 為空。例如:

session.receive().map(message -> {DataBuffer payload = message.getPayload();DataBufferUtils.release(payload); // 這里釋放后,后續的 map() 操作會讀取不到數據return payload;}).map(buffer -> buffer.toString(StandardCharsets.UTF_8)) // 這里可能會失敗.subscribe();

正確的方式

  • 確保 DataBuffer 只在最終消費時釋放。
  • 處理 DataBuffer 時,轉換為 byte[] 以避免流式數據的重復讀取。
session.receive().map(WebSocketMessage::getPayload).map(dataBuffer -> {byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer);  // 讀取完畢后釋放return new String(bytes, StandardCharsets.UTF_8);}).subscribe(System.out::println);

5. retain()release() 方法的補充

Spring WebFlux 中,WebSocketMessage 還提供了 retain()release() 方法,用于管理 DataBuffer 的引用計數和釋放資源。下面介紹如何在多線程環境中正確使用這些方法。

retain() 方法

retain() 方法確保 DataBuffer 的引用計數增加,以便在需要時能夠安全使用:

public WebSocketMessage retain() {if (reactorNetty2Present) {return ReactorNetty2Helper.retain(this);}DataBufferUtils.retain(this.payload);return this;
}

retain() 方法會增加 DataBuffer 的引用計數,防止在處理過程中被提前釋放。這對于需要多個組件共享同一 DataBuffer 實例的情況非常重要。

release() 方法

release() 方法用于釋放 DataBuffer,減少引用計數,釋放底層資源,防止內存泄漏:

public void release() {DataBufferUtils.release(this.payload);
}

release() 方法通常在處理完成后調用,確保底層的 DataBuffer 被正確釋放。

使用示例:在多線程環境中使用 retain() 和 release()

在 WebSocket 消息處理時,確保在多線程環境中正確管理 DataBuffer 的生命周期。示例如下,使用 retain() 保證資源被正確引用,并在 finally 塊中調用 release() 確保即使出現異常時也會釋放資源:

session.receive().doOnNext(message -> {// 在多線程環境中保留引用message.retain();try {String data = message.getPayloadAsText();System.out.println("Received: " + data);// 模擬處理過程,可能會涉及多線程操作// 例如:通過某個線程池處理消息processMessageAsync(data);} finally {// 確保釋放資源message.release();  // 釋放資源}}).subscribe();

在上面的示例中,retain() 確保了 DataBuffer 在多個線程中可以安全訪問,直到最終的 release() 被調用來釋放資源。無論操作成功與否,finally 塊中的 release() 都會被執行,確保不會發生內存泄漏。


6. 總結

在 Spring WebFlux 中使用 WebSocketMessageDataBuffer 需要注意以下幾點:

  1. 避免重復讀取 DataBuffer,建議在讀取后緩存數據。
  2. 避免阻塞操作,盡量使用 Flux/Mono 進行異步處理。
  3. 防止內存泄漏,在手動管理 DataBuffer 生命周期時使用 DataBufferUtils.release() 釋放資源。
  4. 確保 DataBuffer 只在最終消費時釋放,避免 Flux 流程中數據丟失。
  5. 使用 retain()release() 方法 來管理 DataBuffer 的引用計數,確保資源的正確釋放,特別是在多線程環境中,確保在 finally 中釋放資源。

通過遵循這些實踐,可以有效地管理 WebSocket 消息的內存使用,并提高應用的性能和可靠性。


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

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

相關文章

【CSS】Tailwind CSS 與傳統 CSS:設計理念與使用場景對比

1. 開發方式 1.1 傳統 CSS 手寫 CSS:你需要手動編寫 CSS 規則,定義類名、ID 或元素選擇器,并為每個元素編寫樣式。 分離式開發:HTML 和 CSS 通常是分離的,HTML 中通過類名或 ID 引用 CSS 文件中的樣式。 示例&#…

2025華為OD機試真題E卷 - 螺旋數字矩陣【Java】

題目描述 疫情期間,小明隔離在家,百無聊賴,在紙上寫數字玩。他發明了一種寫法:給出數字個數 n (0 < n ≤ 999)和行數 m(0 < m ≤ 999),從左上角的 1 開始,按照順時針螺旋向內寫方式,依次寫出2,3,…,n,最終形成一個 m 行矩陣。小明對這個矩陣有些要求: 1、…

地下井室可燃氣體監測裝置:守護地下安全,防患于未“燃”!

在城市的地下&#xff0c;隱藏著無數的燃氣管道和井室&#xff0c;它們是城市基礎設施建設的重要部分&#xff0c;燃氣的使用&#xff0c;給大家的生活提供了極大的便利。在便利生活的背后&#xff0c;也存在潛在的城市安全隱患。 近年來&#xff0c;地下井室可燃氣體泄漏事故…

【使用hexo模板創建個人博客網站】

使用hexo模板創建個人博客網站 環境準備node安裝hexo安裝ssh配置 使用hexo命令搭建個人博客網站hexo命令 部署到github創建倉庫修改_config.yml文件 編寫博客主題擴展 環境準備 node安裝 進入node官網安裝node.js 使用node -v檢查是否安裝成功 安裝成功后應該出現如上界面 …

C# OPC DA獲取DCS數據(提前配置DCOM)

OPC DA配置操作手冊 配置完成后&#xff0c;訪問遠程ip&#xff0c;就能獲取到服務 C#使用Interop.OPCAutomation采集OPC DA數據&#xff0c;支持訂閱&#xff08;數據變化&#xff09;、單個讀取、單個寫入、斷線重連

發行思考:全球熱銷榜的頻繁變動

幾點雜感&#xff1a; 1、單機游戲銷量與在線人數的衰退是劇烈的&#xff0c;有明顯的周期性&#xff0c;而在線游戲則穩定很多。 如去年的某明星游戲&#xff0c;最高200多萬在線&#xff0c;如今在線人數是48名&#xff0c;3萬多。 而近期熱門的是MH&#xff0c;在線人數8…

Unity自定義區域UI滑動事件

自定義區域UI滑動事件 介紹制作1.創建一個Image2.創建腳本 總結 介紹 一提到滑動事件聯想到有太多的插件了比如EastTouchBundle&#xff0c;今天想單純通過UI去做一個滑動事件而不是基于Box2d或者Box去做滑動事件。 制作 1.創建一個Image 2.創建腳本 using UnityEngine; us…

taosd 寫入與查詢場景下壓縮解壓及加密解密的 CPU 占用分析

在當今大數據時代&#xff0c;時序數據庫的應用越來越廣泛&#xff0c;尤其是在物聯網、工業監控、金融分析等領域。TDengine 作為一款高性能的時序數據庫&#xff0c;憑借獨特的存儲架構和高效的壓縮算法&#xff0c;在存儲和查詢效率上表現出色。然而&#xff0c;隨著數據規模…

《UE5_C++多人TPS完整教程》學習筆記34 ——《P35 網絡角色(Network Role)》

本文為B站系列教學視頻 《UE5_C多人TPS完整教程》 —— 《P35 網絡角色&#xff08;Network Role&#xff09;》 的學習筆記&#xff0c;該系列教學視頻為計算機工程師、程序員、游戲開發者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephe…

K8s 1.27.1 實戰系列(七)Deployment

一、Deployment介紹 Deployment負責創建和更新應用程序的實例,使Pod擁有多副本,自愈,擴縮容等能力。創建Deployment后,Kubernetes Master 將應用程序實例調度到集群中的各個節點上。如果托管實例的節點關閉或被刪除,Deployment控制器會將該實例替換為群集中另一個節點上的…

Linux(Centos 7.6)命令詳解:vim

1.命令作用 vi/vim 是Linux 系統內置不可或缺的文本編輯命令&#xff0c;vim 是vi 的加強版本&#xff0c;兼容vi 的所有指令&#xff0c;不僅能編輯文本&#xff0c;而且還具有shell 程序編輯的功能&#xff0c;可以不同顏色的字體來辨別語法的正確性。 2.命令語法 usage: …

微信小程序引入vant-weapp組件教程

本章教程,介紹如何在微信小程序中引入vant-weapp。 vant-weapp文檔:https://vant-ui.github.io/vant-weapp/#/button 一、新建一個小程序 二、npm初始化 npm init三、安裝 Vant Weapp‘ npm i @vant/weapp -

C++ 作業 DAY5

作業 代碼 Widtget.h class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();private:Ui::Widget *ui;/************************ 起始終止坐標 ************************/QPoint end;QPoint start;QVector<QPoint> per_start_lis…

Selenium 中 ActionChains 支持的鼠標和鍵盤操作設置及最佳實踐

Selenium 中 ActionChains 支持的鼠標和鍵盤操作設置及最佳實踐 一、引言 在使用 Selenium 進行自動化測試時&#xff0c;ActionChains 類提供了強大的功能&#xff0c;用于模擬鼠標和鍵盤的各種操作。通過 ActionChains&#xff0c;可以實現復雜的用戶交互&#xff0c;如鼠標…

前端面試技術性場景題

87.場景面試之大數運算&#xff1a;超過js中number最大值的數怎么處理 在 JavaScript 中&#xff0c;Number.MAX_SAFE_INTEGER&#xff08;即 2^53 - 1&#xff0c;即 9007199254740991&#xff09;是能被安全表示的最大整數。超過此值時&#xff0c;普通的 Number 類型會出現…

【js逆向】iwencai國內某金融網站實戰

地址&#xff1a;aHR0cHM6Ly93d3cuaXdlbmNhaS5jb20vdW5pZmllZHdhcC9ob21lL2luZGV4 在搜索框中隨便輸入關鍵詞 查看請求標頭&#xff0c;請求頭中有一個特殊的 Hexin-V,它是加密過的&#xff1b;響應數據包中全是明文。搞清楚Hexin-V的值是怎么生成的&#xff0c;這個值和cooki…

ES Module 的 import 導入和 import () 動態導入

ES Module 的 import 導入和 import () 動態導入介紹 一、ES Module 簡介 ES Module 是 JavaScript 官方提供的標準化模塊系統&#xff0c;它的出現解決了長期以來 JavaScript 在模塊管理方面的混亂局面。通過 ES Module&#xff0c;開發者可以更加方便地組織和復用代碼&…

使用Node.js從零搭建DeepSeek本地部署(Express框架、Ollama)

目錄 1.安裝Node.js和npm2.初始化項目3.安裝Ollama4.下載DeepSeek模型5.創建Node.js服務器6.運行服務器7.Web UI對話-Chrome插件-Page Assist 1.安裝Node.js和npm 首先確保我們機器上已經安裝了Node.js和npm。如果未安裝&#xff0c;可以通過以下鏈接下載并安裝適合我們操作系…

BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN學習

目錄 一、網頁功能探索 二、SSTI注入 三、方法一 四、方法二 使用PIN碼 &#xff08;1&#xff09;服務器運行flask登錄所需的用戶名 &#xff08;2&#xff09;modename &#xff08;3&#xff09;flask庫下app.py的絕對路徑 &#xff08;4&#xff09;當前網絡的mac地…

Java基礎關鍵_018_集合(二)

目 錄 一、泛型 ※ 1.說明 2.實例 3.擦除與補償 4.泛型的定義 &#xff08;1&#xff09;類定義 &#xff08;2&#xff09;靜態方法定義 &#xff08;3&#xff09;接口定義 5.通配符 &#xff08;1&#xff09;無限定 &#xff08;2&#xff09;上限 &#xff…