Netty 核心原理與實戰:從 DiscardServer 看透 Reactor 模式與組件協作

目錄

Netty 是什么?

Netty 的目標

Netty 實戰案例 DiscardServer

服務端程序 NettyDiscardServer

業務處理器 NettyDiscardHandler

配置類?NettyDemoConfig

回顧 Reactor 模式中的 IO 事件處理流程

Netty 中的 Channel

Netty 中的 Reactor

Netty 中的 Handler

Netty 中的 Pipeline


Netty 是什么?

????????Netty 是一個基于 Java NIO 的客戶端/服務器端通信框架,旨在簡化網絡編程的開發過程。它不僅支持高并發、高擴展性,還具備良好的可維護性,非常適合用于構建高性能的網絡應用程序。Netty 的核心實現基于 Reactor 模式,封裝了底層復雜的事件驅動機制,使開發者能夠專注于業務邏輯而無需處理底層通信細節。
????????通過使用 Netty,開發者可以輕松實現 TCP、UDP、HTTP 等常見通信協議的支持,從而大大簡化了底層通信的編程工作。無論是 TCP、UDP 這樣的低層傳輸協議,還是 FTP、SMTP、HTTP 等應用層協議,Netty 都能提供統一且高效的編程模型,幫助開發者快速搭建穩定的網絡服務端或客戶端應用。

Netty 的目標

????????Netty 的設計目標是幫助開發者快速而輕松地構建各類通信應用程序。它通過抽象底層復雜的網絡通信細節,提供了清晰且統一的編程接口,使得網絡程序的開發更加高效和簡潔。
????????在實現高性能通信方面,Netty 支持多種通信協議,能夠處理包括 TCP、UDP 在內的傳輸層協議,以及 HTTP、FTP 等應用層協議。同時,Netty 基于異步非阻塞的通信機制,結合事件驅動的架構,有效提升了系統在高并發場景下的響應能力和吞吐量。
????????為了實現靈活的通信控制,Netty 提供了一系列核心組件,包括用于數據傳輸的 Channel、用于處理通信邏輯的 Handler,以及用于執行 I/O 事件循環的 EventLoopGroup 等。這些組件相互協作,構成了 Netty 強大而靈活的通信框架核心。

Netty 實戰案例 DiscardServer

什么是 DiscardServer?
一個丟棄消息的服務器:接收客戶端消息后不做任何處理直接丟棄,相當于 Netty 的 “HelloWorld”

服務端程序 NettyDiscardServer

下面的代碼很多地方是新知識,先大體了解代碼功能,后續會進行詳細講解

服務端核心步驟
1. 創建反應器線程組(EventLoopGroup)
2.?配置 ServerBootstrap
3.?啟動服務器
????????bind().sync() 同步綁定。
????????closeFuture().sync() 同步關閉監聽。
????????shutdownGracefully() 優雅關閉線程組。

// 定義一個Netty丟棄服務器類,功能是接收數據并直接丟棄
public class NettyDiscardServer {// 服務器監聽的端口號private final int serverPort;// Netty的服務端啟動類,用于配置和啟動服務器ServerBootstrap b = new ServerBootstrap();// 構造函數,傳入服務器端口public NettyDiscardServer(int port) {this.serverPort = port;}// 運行服務器的方法public void runServer() {// 創建boss事件循環組,處理連接請求,參數1表示使用1個線程EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);// 創建worker事件循環組,處理IO操作,不指定參數則使用默認線程數EventLoopGroup workerLoopGroup = new NioEventLoopGroup();try {// 配置服務器引導類b.group(bossLoopGroup, workerLoopGroup); // 設置事件循環組b.channel(NioServerSocketChannel.class); // 指定使用NIO傳輸通道b.localAddress(serverPort); // 設置服務器監聽地址和端口b.option(ChannelOption.SO_KEEPALIVE, true); // 設置TCP保持連接選項// 配置子通道的處理器b.childHandler(new ChannelInitializer<SocketChannel>() {// 初始化通道時調用protected void initChannel(SocketChannel ch) {// 向管道添加自定義的處理器ch.pipeline().addLast(new NettyDiscardHandler());}});// 綁定服務器并同步等待綁定完成ChannelFuture channelFuture = b.bind().sync();// 打印服務器啟動成功信息System.out.println("服務器啟動成功,監聽端口: " +channelFuture.channel().localAddress());// 獲取通道關閉的FutureChannelFuture closeFuture = channelFuture.channel().closeFuture();// 同步等待服務器通道關閉closeFuture.sync();} catch (Exception e) {e.printStackTrace(); // 打印異常堆棧} finally {// 優雅關閉事件循環組workerLoopGroup.shutdownGracefully();bossLoopGroup.shutdownGracefully();}}// 主方法,程序入口public static void main(String[] args) {// 從配置類獲取端口號int port = NettyDemoConfig.SOCKET_SERVER_PORT;// 創建服務器實例并運行new NettyDiscardServer(port).runServer();}
}

關鍵步驟:

1. 創建反應器線程組

EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1); ?// 負責接收客戶端連接
EventLoopGroup workerLoopGroup = new NioEventLoopGroup(); // 負責處理 IO 讀寫

bossGroup:只負責 accept 新連接
workerGroup:負責處理每個連接的讀寫事件(比如數據收發)

2. 配置啟動器 ServerBootstrap

b.group(bossLoopGroup, workerLoopGroup);
b.channel(NioServerSocketChannel.class); // 使用 NIO 的 ServerSocketChannel
b.localAddress(serverPort); ? ? ? ? ? ? ?// 綁定監聽端口
b.option(ChannelOption.SO_KEEPALIVE, true); // 保持長連接

channel():指定使用 NIO 網絡模型
option():配置服務器通道參數,如是否保持連接

NioServerSocketChannel 是 Netty 中專門用于 NIO 模式下的 TCP 服務器端通道,對應底層的 ServerSocketChannel(Java NIO 中的 TCP 服務器套接字通道),專門處理 TCP 協議的連接請求

ChannelOption.SO_KEEPALIVE 是 TCP 協議的保活機制選項,用于設置是否開啟 TCP 連接的保活功能。

當該選項設置為 true 時:

如果客戶端和服務器長時間沒有數據交互(默認通常是 2 小時左右,具體時間取決于系統配置),TCP 協議會自動發送探測數據包,檢測對方是否還在線。
如果對方正常響應,說明連接仍然有效,繼續保持連接;
如果多次探測無響應,會判定連接已失效,自動關閉連接,釋放資源。

3. 設置子通道處理器

b.childHandler(new ChannelInitializer<SocketChannel>() {protected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new NettyDiscardHandler());}
});

每當有客戶端連接成功,就會初始化它的通道(SocketChannel)
向 pipeline 添加處理器 NettyDiscardHandler

業務處理器 NettyDiscardHandler

NettyDiscardHandler 是服務端通道的自定義處理器,用于讀取并丟棄客戶端發送的數據。

// 自定義的Netty通道處理器,繼承自ChannelInboundHandlerAdapter
public class NettyDiscardHandler extends ChannelInboundHandlerAdapter {// 當通道有數據可讀時調用@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 將消息轉換為ByteBuf類型ByteBuf in = (ByteBuf) msg;try {System.out.println("收到消息,丟棄如下:");// 循環讀取ByteBuf中的可讀字節while (in.isReadable()) {// 逐個字節讀取并打印為字符System.out.print((char) in.readByte());}System.out.println(); // 打印換行} finally {// 釋放消息資源,防止內存泄漏ReferenceCountUtil.release(msg);}}
}
步驟說明
channelRead()當通道收到客戶端數據時觸發
msg → ByteBuf將消息強制轉換為 ByteBuf 類型
while (in.isReadable())遍歷 ByteBuf?中所有可讀字節
readByte()逐字節讀取并打印內容
ReferenceCountUtil.release()釋放 ByteBuf 引用,防止內存泄漏

為什么需要手動釋放 ByteBuf?
Netty 使用引用計數機制管理內存,ByteBuf 在讀取后必須顯示釋放,否則會導致內存泄漏。

ReferenceCountUtil.release(msg) 等價于調用 ((ByteBuf) msg).release(),但更加通用安全。

在 Netty 中,基于引用計數機制(ReferenceCounted 接口)管理的對象(如 ByteBuf),需要開發者顯式調用釋放方法來回收資源,這就是手動釋放的含義 —— 不像 JVM 堆內存那樣由垃圾回收器自動回收,而是需要代碼中主動觸發釋放。

ReferenceCountUtil.release(msg) 的作用是:
1. 檢查 msg 是否是引用計數對象(實現了 ReferenceCounted 接口,比如 ByteBuf)
2. 如果是,則調用其 release() 方法減少引用計數,當計數減為 0 時,對象會被釋放(回收內存)
3. 如果不是引用計數對象,該方法會忽略操作,避免報錯。

配置類?NettyDemoConfig
// Netty演示配置類
public class NettyDemoConfig {// 定義服務器端口常量public static final int SOCKET_SERVER_PORT = 12345;
}

如果沒有客戶端發送數據,只啟動服務器無法驗證效果。
所以可以使用之前的 NioDiscardClient 客戶端,在下面鏈接有講
詳解 NIO Selector_java nio selectors 工作原理-CSDN博客
下面是完整的客戶端代碼,運行時需要先啟動服務端,再啟動客戶端

public class NioDiscardClient {// 靜態的 Logger 實例private static final Logger logger = LoggerFactory.getLogger(NioDiscardClient.class);public static void startClient() throws IOException {// 1. 配置服務器地址和端口InetSocketAddress address = new InetSocketAddress("127.0.0.1", 12345);// 2. 獲取 SocketChannel 并連接到服務器SocketChannel socketChannel = SocketChannel.open(address);// 3. 設置為非阻塞模式socketChannel.configureBlocking(false);// 4. 等待連接建立完成while (!socketChannel.finishConnect()) {// 自旋等待連接完成}logger.info("客戶端連接成功");// 5. 準備數據緩沖區ByteBuffer byteBuffer = ByteBuffer.allocate(1024);byteBuffer.put("hello world".getBytes());byteBuffer.flip();// 6. 將數據寫入通道(發送到服務端)socketChannel.write(byteBuffer);// 7. 關閉輸出流并斷開連接socketChannel.shutdownOutput();socketChannel.close();}public static void main(String[] args) throws IOException {startClient();}
}

如何驗證消息被丟棄?
啟動客戶端程序,向服務器發消息。
服務端打印接收到的內容,但不會返回任何響應。

回顧 Reactor 模式中的 IO 事件處理流程

單線程 Reactor 模式-CSDN博客
多線程 Reactor 模式-CSDN博客
如果不了解 Reactor 模式,可點擊鏈接跳轉學習

一個 IO 操作從內核產生到業務代碼執行,會經歷 4 個階段:

階段說明
第 1 步:注冊IO 事件通過 Channel 注冊到 Selector
第 2 步:查詢Reactor(反應器)通過線程輪詢 Selector,檢查是否有事件就緒
第 3 步:分發如果發現事件,交給綁定的 Handler 處理
第 4 步:處理真正的 IO 讀寫邏輯或業務邏輯由 Handler 完成

第 1、2 步由 Java NIO 提供機制,第 3、4 步為 Reactor 核心。

Netty 中的 Channel

????????在 Netty 中,Channel 是進行所有 IO 操作的核心載體。它是對 Java NIO 中 Channel 的封裝和擴展,提供了更加靈活和統一的接口,使得網絡通信編程變得更為簡單高效。Channel 表示一個網絡連接,既可以代表客戶端的連接通道,也可以代表服務端的監聽通道。
????????針對不同的通信協議,例如 TCP、UDP 和 SCTP,Netty 提供了對應的 Channel 實現類。同時,為了支持不同的 IO 模型,Netty 為每種協議都提供了基于 NIO(非阻塞 IO)和 OIO(阻塞 IO)的兩種實現版本。例如,針對 TCP 協議,Netty 提供了 NioSocketChannel 和 OioSocketChannel。
????????通過這種方式,Netty 不僅屏蔽了底層 IO 實現的復雜性,還為用戶提供了豐富且統一的通信編程模型。無論是同步通信還是異步通信,開發者都可以通過 Channel 接口輕松完成底層數據傳輸的操作。

常見 Channel 類型

協議類型Channel 實現
TCP 傳輸NioSocketChannel, OioSocketChannel
TCP 監聽NioServerSocketChannel, OioServerSocketChannel
UDP 傳輸NioDatagramChannel, OioDatagramChannel
SCTP 傳輸NioSctpChannel, OioSctpChannel
SCTP 監聽NioSctpServerChannel, OioSctpServerChannel

Netty 的 Channel 封裝關系

????????在 Netty 中,Channel 的實現并不是直接使用 Java NIO 的 Channel 接口,而是在其基礎上進行了多層封裝,以提供更高層次的抽象和更強的功能擴展。以常用的 NioSocketChannel 為例,它的繼承結構遵循了一個清晰的層級體系:NioSocketChannel 繼承自 AbstractNioByteChannel,而后者又繼承自 AbstractNioChannel,再向上是 AbstractChannel,最終實現了最頂層的 Channel 接口。
????????這種層層封裝的設計,使得 Netty 能夠在不同層級中分離出通用的邏輯與具體的實現細節,從而提升代碼的復用性與擴展性。同時,盡管 Netty 封裝了自己的 Channel 體系,但其最底層依然是依賴于 Java NIO 中的 SelectableChannel,也就是說,Netty 的所有 IO 操作最終都會落地到 NIO 提供的底層通道上執行。因此,Netty 在提升開發體驗的同時,也保留了 NIO 高性能的核心優勢。

頂層接口(Channel):定義通用行為,與具體實現無關
抽象類(AbstractChannel 等):封裝通用邏輯,沉淀復用代碼
具體實現類(NioSocketChannel 等):專注于特定協議和 IO 模型的細節

SelectableChannel 是 Java NIO 中的一個抽象類,是所有支持多路復用(Selector)通道(Channel)的父類。它的核心作用是允許通道被注冊到 Selector 上,通過 Selector 實現對多個通道的 IO 事件(讀就緒、寫就緒、連接就緒等)進行統一監聽和管理。

Netty 中的 Reactor

????????在 Netty 中,Reactor 模式的核心組件之一是 NioEventLoop,負責事件輪詢與處理調度。NioEventLoop 是 Netty 對 Java NIO 中事件驅動機制的封裝,其本質是一個反應器線程,用于不斷輪詢底層的 Selector,監聽是否有 IO 事件發生,并在事件就緒后將其分發給對應的處理器(Handler)進行處理。
????????每一個 NioEventLoop 都擁有一個獨立的線程,并且綁定了一個 Selector 實例。它們之間形成一一對應的關系,保證事件處理的線程安全性和高效性。此外,Netty 設計中允許多個 Channel 被注冊到同一個 NioEventLoop 上,從而實現一對多的高效事件管理機制。這種設計不僅充分利用了線程資源,還有效支持了高并發場景下的網絡通信。

NioEventLoop 的繼承結構:

????????在 Netty 的架構中,NioEventLoop 是實現事件驅動模型的核心類之一,它的繼承結構體現了 Netty 對事件處理流程的高度抽象與組織。具體來說,NioEventLoop 繼承自 SingleThreadEventLoop,而 SingleThreadEventLoop 又繼承自 SingleThreadEventExecutor,最終實現了頂層的 Executor 接口。
????????這一層層的繼承關系體現了職責的逐步細化:從通用的任務執行器(Executor),到專用于單線程執行的事件循環體(SingleThreadEventExecutor),再到真正綁定了 IO 事件處理能力的 NioEventLoop。通過這樣的設計,Netty 將事件處理的通用性與專業性有機結合,使得每一個 EventLoop 既具備強大的調度能力,又能專注于單線程下的高效執行。
????????在實現細節上,NioEventLoop 擁有兩個至關重要的成員屬性:其一是 Thread,即事件處理線程;其二是 Selector,用于監聽和輪詢注冊的 IO 事件。這兩個核心組件配合使用,使得 NioEventLoop 成為支撐 Netty 高性能異步 IO 的關鍵支點。

EventLoop 與 Channel 的關系

一個 EventLoop 可以管理成千上萬個 Channel。
屬于一對多的綁定結構。

Netty 中的 Handler

IO 事件類型:

類型對應 SelectionKey
讀就緒OP_READ
寫就緒OP_WRITE
連接完成OP_CONNECT
接收連接OP_ACCEPT

Handler 分類:

類型說明
ChannelInboundHandler入站事件處理器(如讀數據)
ChannelOutboundHandler出站事件處理器(如寫數據)

????????在 Netty 中,Handler 是承載業務邏輯處理的核心組件。它用于響應和處理各種 IO 事件,是連接數據與業務處理之間的橋梁。根據事件的方向不同,Handler 被分為兩類:入站處理器和出站處理器。
????????ChannelInboundHandler 是處理入站事件的處理器,負責響應例如客戶端連接建立、數據讀取等事件,典型方法如 channelRead()、channelActive()。而 ChannelOutboundHandler 則用于處理出站事件,主要負責將數據寫出到網絡或進行編碼轉換等操作,典型方法如 write()、flush()。
????????每個 Channel 在創建時,都會綁定一個 ChannelPipeline,這個 Pipeline 中可以包含多個 Handler。通過 Pipeline 的鏈式結構,Netty 能夠靈活地控制事件在多個 Handler 之間的流轉,從而實現復雜、可擴展的網絡業務處理邏輯。

Netty 中的 Pipeline

????????在 Netty 中,ChannelPipeline 是每個 Channel 所獨有的組件,它是一個雙向鏈表結構,專門用于管理和組織多個 Handler 的執行順序。可以理解為,Pipeline 就是一條事件處理的通道,所有與該 Channel 相關的 IO 事件都會按照這條通道中 Handler 的順序依次傳遞與處理。
????????通過 ChannelPipeline,Netty 實現了對事件處理器(Handler)的鏈式管理,不僅支持事件的入站處理(Inbound),也支持事件的出站處理(Outbound)。事件在 Pipeline 中的傳遞方向由事件類型決定:入站事件會按照添加順序正向傳遞,出站事件則會反向傳遞。
????????這種設計帶來了高度的靈活性,使開發者能夠按需在不同位置添加、替換或刪除 Handler,從而精細控制事件處理流程,實現清晰、模塊化的通信邏輯。簡而言之,ChannelPipeline 是 Netty 中連接 Channel 與業務處理器之間的橋梁,也是事件流動和處理的主通道。

普通 Reactor 模式是事件 → 單個處理器(內部包含所有步驟),而 Netty 是事件 → 流水線(多個獨立 Handler 按順序協作)

入站/出站執行順序
1. IO 事件進入 Pipeline。
2. 沿著 Handler 鏈處理:
????????入站事件按前向順序觸發 InboundHandler。
????????出站事件按后向順序觸發 OutboundHandler。


尚未完結

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

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

相關文章

關于“LoggerFactory is not a Logback LoggerContext but Logback is on ......“的解決方案

? ?重磅&#xff01;盹貓的個人小站正式上線啦&#xff5e;誠邀各位技術大佬前來探秘&#xff01;? 這里有&#xff1a; 硬核技術干貨&#xff1a;編程技巧、開發經驗、踩坑指南&#xff0c;帶你解鎖技術新姿勢&#xff01;趣味開發日常&#xff1a;代碼背后的腦洞故事、工具…

2025年6月電子學會青少年軟件編程(C語言)等級考試試卷(三級)

答案和更多內容請查看網站&#xff1a;【試卷中心 -----> 電子學會 ----> C/C ---->三級】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 編程題 第 1 題 打印城門 題目描述 給定一個正整數 n&#xff0c;輸出如下的星號城門。具體格式請見樣例。 輸入格…

跨平臺直播美顏SDK開發指南:兼顧性能與美型效果的最佳實踐

面對iOS、Android乃至Web等多端應用需求&#xff0c;如何開發一款真正跨平臺、兼顧性能與美型效果的美顏SDK&#xff0c;成為眾多開發團隊和產品經理的一道必答題。 今天筆者這篇文章&#xff0c;就從架構設計、性能優化、視覺效果調校三個關鍵維度&#xff0c;帶你深入解析跨平…

2025數字藏品安全保衛戰:高防CDN如何成為NFT應用的“隱形護甲”?

副標題&#xff1a; 從DDoS防御到全球加速&#xff0c;拆解數字資產平臺的生死防線&#x1f310; 引言&#xff1a;當數字藏品成為黑客的“頭號靶場”2025年全球數字藏品市場突破$1000億&#xff0c;但安全事件同步激增230%——某頭部NFT平臺因3.2Tbps DDoS攻擊癱瘓&#xff0c…

linux 執行sh腳本,提示$‘\r‘: command not found

1、在Linux下執行某個腳本文件卻提示$\r: command not found&#xff0c;如下圖:2、錯誤原因:a、 Windows 風格的換行符&#xff1a;Windows 系統使用 \r\n 作為行結束符&#xff0c;而 Linux 和 Unix 系統使用 \n。當你從 Windows 環境中復制文本到 Linux 環境時&#xff0c;可…

使用HaiSnap做了一款取件碼App(一鍵生成)

你是否懷揣著奇思妙想&#xff0c;卻因不懂代碼而對開發應用望而卻步&#xff1f;現在&#xff0c;有一個神奇AI Agent&#xff08;響指HaiSnap&#xff09;&#xff0c;一個響指就能實現&#xff0c;你說神奇不&#xff1f;只需要一句話就可以生成你想要的應用&#xff01;讓你…

容器與虛擬機的本質差異:從資源隔離到網絡存儲機制

目錄 專欄介紹 作者與平臺 您將學到什么&#xff1f; 學習特色 容器與虛擬機的本質差異&#xff1a;從資源隔離到網絡存儲機制 一、容器與虛擬機的本質區別 1.1 資源抽象層次差異 1.2 資源消耗與性能對比 1.3 隔離性深度差異 二、容器網絡基礎架構 2.1 Docker網絡模型…

ros2 launch文件編寫詳解

一個完整的簡單的launch文件配置過程1.編寫launch文件2.配置package.xml3.配置setup.py&#xff08;python包&#xff09;4.配置CMakeList(C包)5.編譯運行# 在 ROS 2 的 Python 啟動文件中&#xff0c;這些導入語句用于引入各類啟動模塊&#xff0c;以構建和配置節點啟動流程 f…

QT中QTableView+Model+Delegate實現一個demo

一、概述功能: 實現一個查詢學生信息的表格&#xff0c;有學號、性別、年齡、班級和分數共5列&#xff0c;針對最后一列分數實現委托代理&#xff0c;要求能編輯和查看該分數列。QTableView實現視圖展示uiModel負責數據的構造Delegate是委托&#xff0c;可針對某列數據做自定義…

用latex+vscode寫論文

文章目錄 前言 一、下載texlive安裝包 二、安裝texlive 1.安裝 2.配置環境變量 3.檢查是否安裝成功 三、安裝vscode 四、vscode中安裝latex workshop插件 五、創建latex文檔 六、撰寫+編譯+預覽 七、latex workshop常用設置 1.打開設置頁面 2.設置自動保存代碼 3.設置自動編譯代…

監測預警系統:讓園區更高效、更安全、更智能

隨著城市化進程的加快和產業集聚效應的凸顯&#xff0c;園區作為經濟發展的重要載體&#xff0c;其規模不斷擴大&#xff0c;功能日益復雜。在這一背景下&#xff0c;傳統的園區管理模式已難以滿足現代園區高效、安全、智能的運營需求。園區監測預警系統作為一種集成了物聯網、…

分享一個AutoOff定時動作軟件

我們平時在使用電腦的時候有很多需求的功能&#xff0c;比如定時打開程序、定時關閉程序、定時休眠、定時關機等等。如果你也有這樣的需求&#xff0c;那么就需要今天這款軟件。AutoOff定時動作軟件AutoOff這個軟件是一款定時的軟件&#xff0c;軟件大小只有1.1M&#xff0c;而…

RPA軟件推薦:提升企業自動化效率

在數字化轉型浪潮中&#xff0c;機器人流程自動化&#xff08;RPA&#xff09;已成為企業降本增效的核心工具。它通過模擬人類操作&#xff0c;自動化重復性任務&#xff0c;如數據錄入、報表生成和系統集成&#xff0c;顯著提升運營效率。面對眾多RPA軟件&#xff0c;如何選擇…

【Qt】QTime::toString(“hh:mm:ss.zzz“) 顯示亂碼的原因與解決方案

在使用 Qt 編寫計時器程序時&#xff0c;我遇到一個很奇怪的問題&#xff1a;使用 QTime::toString("hh:mm:ss.zzz") 格式化時間后&#xff0c;顯示出來的是一串亂碼&#xff0c;如下所示&#xff1a;本來應該是&#xff1a;但卻顯示了一堆“〇”或奇怪的符號。問題表…

MSVC編譯KDChart過程

MSVC編譯KDChart過程 一、下載KDChart源文件 GitHub源文件 查看文件夾里的INSTALL.txt&#xff0c;可以看到需要CMake編譯。 中文版 以下是使用 CMake 構建系統安裝 KD Chart 的說明。 需要 CMake 3.3 或更高版本。Qt 版本支持&#xff1a;* KD Chart 2.5.0 及以下版本需要…

AI Agent管理后臺原型設計全拆解(附3套可復用素材)

最近在做AI方向的產品&#xff0c;越來越能感受到“智能體”這個概念正在從技術圈走向應用層。無論是內部探索項目&#xff0c;還是外部合作需求&#xff0c;很多場景都會提到Agent、助手、知識庫這些關鍵詞。我們經常討論如何打造一個有用的AI Agent&#xff0c;但實際上&…

ABP VNext + Elastic APM:微服務性能監控

ABP VNext Elastic APM&#xff1a;微服務性能監控 &#x1f680; &#x1f4da;目錄ABP VNext Elastic APM&#xff1a;微服務性能監控 &#x1f680;一、引言 ?架構全景圖 &#x1f3d7;?二、環境與依賴 &#x1f4e6;三、APM 服務器與 Kibana 快速部署 &#x1f433;Doc…

單片機學習筆記.AD/DA(略含有SPI,用的是普中開發板上的XPT2046芯片)

AD/DA基礎知識 硬件電路&#xff1a; 模電運放知識回顧&#xff1a; 虛短&#xff08;Virtual Short&#xff09;定義&#xff1a;運放同相輸入端&#xff08;&#xff09;和反相輸入端&#xff08;-&#xff09;的電位近似相等&#xff0c;即V V-&#xff0c;仿佛兩個輸入端短…

避坑指南:VMware安裝CentOS常見錯誤及完美解決方案

1. 引言 虛擬機允許在同一臺機子上有不同的操作系統&#xff0c;還可以用于搭建實驗環境、軟件測試和兼容性測試等。我是主攻大數據方向的會用到Linux操作系統&#xff0c;所以虛擬機對我來說是很有必要的。我把之前的筆記和安裝包整理了一下&#xff0c;就有了現在這個教程。…

Python爬蟲04_Requests豆瓣電影爬取

一、 爬取豆瓣電影排行榜數據 import requests import json url ‘https://movie.douban.com/j/chart/top_list’ param { ‘type’:‘24’, ‘interval_id’:‘100:90’, ‘action’:‘’, ‘start’:‘0’, #從庫中的第幾部電影去取 ‘limit’:‘20’, } headers { ‘Use…