米哈游一面:Netty 采用了哪些經典的設計模式?
Netty 是一個優秀的、高性能、異步的事件驅動網絡應用框架,它內部使用了許多經典的設計模式。這篇文章,我們來詳細分析 Netty到底使用了哪些優秀的設計模式,并且結合 Netty 的具體實現來探討這些模式的應用。
1. 責任鏈模式
(1) 概念:
責任鏈模式(Chain of Responsibility)用于將請求沿著處理鏈傳播,每個對象都有機會處理請求或將其傳遞給下一個對象。
(2) Netty 中的應用:
Netty 的 ChannelPipeline 和 ChannelHandler 正是責任鏈模式的經典實現。ChannelPipeline 是一組互相連接的 ChannelHandler 對象,每個 ChannelHandler 執行對數據流的處理。
(3) 實現案例:
- 在 Netty 中,ChannelPipeline 提供了一組按順序工作的 ChannelHandler,可分為入站(inbound)和出站(outbound)。
- 當接收到數據時,它會沿入站處理鏈傳播,各個入站 ChannelHandler 依次處理該數據(如解碼、業務邏輯處理等)。
- 當發送數據時,它會沿出站處理鏈傳播,各個出站 ChannelHandler 依次處理該數據(如編碼、壓縮等)。
(4) 代碼示例:
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new DecoderHandler());
pipeline.addLast(new BusinessLogicHandler());
pipeline.addLast(new EncoderHandler());
每個處理器都會處理其關心的部分,并將其余的事情交給鏈內的下一個處理器。
2. 觀察者模式
(1) 概念:
觀察者模式(Observer)定義了對象之間的一對多依賴關系,當目標對象狀態發生改變時,其依賴者(觀察者)會收到通知并自動更新。
(2) Netty 中的應用:
Netty 的事件驅動模型通過觀察者模式實現。當 Selector 檢測到特定事件(如 read 或 write 準備完成)后會通知對應的 Channel。Channel 會觸發事件并將任務提交到合適的處理器執行。
(3) 實現案例:
- ChannelFuture 是 Netty 中觀察者模式的典型應用,例如,當你向服務器發送數據時,可以通過 ChannelFuture 注冊監聽器,來監控數據發送是否完成。
- 當操作完成時,監聽器會被通知從而執行用戶定義的回調邏輯。
(4) 代碼示例:
ChannelFuture future = channel.writeAndFlush(message);
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("Write successful!");
} else {
System.out.println("Write failed: " + future.cause());
}
}
});
ChannelFutureListener 是典型的觀察者,當 ChannelFuture 的狀態變化時會收到通知。
3. 工廠模式
(1) 概念:
工廠模式(Factory)用于創建對象的實例,屏蔽了對象創建的復雜性。
(2) Netty 中的應用:
Netty 使用工廠模式隱藏了創建復雜對象的細節,常見的是 EventLoopGroup 和 Bootstrap 等組件。
(3) 實現案例:
- EventLoopGroup 是執行事件循環的關鍵組件,Netty 提供了多種實現(如 NIO 的 NioEventLoopGroup 和 Epoll 的 EpollEventLoopGroup),用戶可以通過抽象工廠模式指定自己需要的實現。
- Bootstrap 和 ServerBootstrap 也是工廠模式的經典應用,它們用于構造客戶端和服務端配置。
(4) 代碼示例:
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyHandler());
}
});
用戶只需要調用 Bootstrap 的方法即可完成工廠的配置,隱藏了復雜的配置邏輯。
4. 適配器模式
(1) 概念:
適配器模式(Adapter)用來將一個類的接口轉換為另一個接口,以實現接口之間的兼容。
(2) Netty 中的應用:
Netty 的 ChannelHandlerAdapter 和 ChannelInboundHandlerAdapter 是典型的適配器模式應用,它們簡化了 ChannelHandler 的實現。
(3) 實現案例:
Netty 的 ChannelHandler 提供了很多接口方法,但用戶可能只需要實現一小部分邏輯。在這種情況下,用戶無需全部實現所有方法,可以繼承 ChannelInboundHandlerAdapter 或 ChannelOutboundHandlerAdapter 來簡化代碼。
(4) 代碼示例:
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Message received: " + msg);
ctx.fireChannelRead(msg);
}
}
通過適配器,用戶不需要實現 ChannelHandler 的所有方法,同時保留了靈活性。
5. 策略模式
(1) 概念:
策略模式(Strategy)將一組算法封裝起來,使得它們可以互換,同時將算法的選擇獨立于使用這些算法的客戶。
(2) Netty 中的應用:
Netty 在其 EventLoopGroup 和處理 IO 的任務分配中采用了策略模式。通過抽象的 EventLoop,Netty 支持多種不同的多路復用機制(如 NIO、Epoll 等)。
(3) 實現案例:
- EventLoopGroup 支持多種實現,并根據運行環境動態選擇策略,例如在 Linux 平臺優先選擇 Epoll。
- Netty 的序列化與解碼器也使用了策略模式,不同的序列化方式可以互相替換(如 protobuf、JSON 等)。
(4) 代碼示例:
EventLoopGroup group = new EpollEventLoopGroup(); // Linux 平臺下的高性能實現
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(EpollSocketChannel.class);
用戶可以靈活替換實現以適配特定需求。
6. 原型模式
(1) 概念:
原型模式 (Prototype)通過克隆的方式創建對象,而不是直接實例化。
(2) Netty 中的應用:
Netty 的緩沖區分配(ByteBufAllocator)中使用了原型模式。為了減少內存分配和回收的開銷,Netty 提供了池化的緩沖區,通過克隆和回收來重復利用緩沖區。
(3) 實現案例:
- PooledByteBufAllocator 負責提供緩沖區,其內部維護了一系列固定大小的內存塊,用于內存分配和回收。
- 使用原型模式減少了頻繁的內存分配成本。
7. 單例模式
(1) 概念:
單例模式(Singleton)保證一個類只存在一個實例,并提供全局訪問點。
(2) Netty 中的應用:
Netty 中某些共享的組件采用單例模式,例如 Unpooled 類和一些內部工具類。
(3) 實現案例:
Unpooled 是非池化緩沖區的工廠類,它使用單例模式提供緩沖區操作的統一入口。
(4) 代碼示例:
ByteBuf buf = Unpooled.buffer(256);
8. 模板方法模式
(1) 概念:
模板方法模式(Template Method)允許在基類定義操作的框架,而將具體實現延遲到子類。
(2) Netty 中的應用:
Netty 的很多組件都提供了模板方法模式的實現,例如 ChannelInitializer 用于設置 ChannelPipeline。
(3) 實現案例:
用戶通過繼承 ChannelInitializer 定義自己的邏輯,而底層框架負責調用和執行。
(4) 代碼示例:
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyHandler());
}
}
9. 總結
這篇文章,我們詳細地分析了 Netty 包含的經典設計模式,并結合 Netty 的具體實現來探討這些模式的應用。因為篇幅有限,我們只分析了 8種有代表性的模型,但是 Netty的設計模式絕不僅僅只有這些,它們都是經典的設計模式。作為Java領域一款經典的網絡通信工具,Netty絕對值得我們花時間去琢磨。