Netty是由JBoss開發,基於Java NIO的一個高效能通訊框架。之前幾篇文章介紹了Java NIO的一些基本的概念和API。但在實際的網路開發中,其實很少使用Java NIO原生的API。主要有以下原因:
- 原生API使用單執行緒模型,不能很好利用多核優勢,如果自己去寫多執行緒結合起來比較麻煩;
- 原生API是直接使用的IO資料,沒有做任何封裝處理,對資料的編解碼、TCP的粘包和拆包、客戶端斷連、網路的可靠性和安全性方面沒有做處理;
在《Netty權威指南》這本書裡提到一個真實的故事,兩個專案團隊都要做基於NIO非阻塞特性的一個高效能、非同步和高可靠性的底層通訊框架,但一個團隊選擇了基於Java NIO API從頭開發,另一個團隊選擇了基於Netty開發。最終,從頭開發的團隊遇到了各種各樣的問題,導致專案延遲,而基於Netty開發的團隊則進展順利。
其實網路開發是一個比較複雜的事情,因為網路的不穩定性,通常會遇到各種各樣的問題,比如前面提到的客戶端突然斷連、TCP的拆包和沾包等等。
幸運的是,網路上已經有了這麼一個成熟的框架幫我們處理了這些事情,這個框架就是Netty。Netty主要應用於以下領域:
- 高效能的RPC框架:常用於微服務之間的高效能遠端呼叫(如Dubbo)
- 遊戲行業:Netty可以很輕鬆地定製和開發一個私有協議棧,
- 即時通訊:Netty基於Java NIO,並且做了一些優化,支援高效能的即時通訊
這裡有一個Netty的功能特性的圖:
Netty Core提供了基本功能,包括檔案零拷貝、基本的API、可擴充套件的基於事件的模型(下文詳細介紹)。
Netty支援非常多的協議,比如HTTP、WebSocket等。當然,Netty也可以自定義協議。常見協議的示例程式碼可以參考netty原始碼裡面的example
包。
Netty同時支援Java的BIO和NIO兩種方式。且很容易與Spring等主流框架進行整合。
首先介紹處理事件的兩種方式:
- 輪詢方式:執行緒不斷輪詢訪問相關事件發生源有沒有發生事件,有發生事件就呼叫事件處理邏輯。Java 原生的NIO就是使用的輪詢方式。
- 事件驅動方式,發生事件,主執行緒把事件放入事件佇列,在另外執行緒不斷迴圈消費事件列表中的事件,呼叫事件對應的處理邏輯處理事件。事件驅動方式也被稱為訊息通知方式,其實是設計模式中觀察者模式的思路。
Reactor是反應堆的意思。Reactor執行緒模型是指通過一個或多個輸入,同時傳遞給服務處理器的服務請求的事件驅動處理模式。
Reactor模式主要工作原理如下圖:
Reactor 有一個專門負責監聽和分發事件的執行緒,如圖中的Service Handler,所有請求進來後,被它分發到具體的處理執行緒,如圖中的Event Handler去處理。
Reactor可能有多個,而Netty正是使用了多Reactor的執行緒模型。
Netty裡面有兩個Group,分別是Boss Group和Worker Group。其中,Boss Group用於處於連線,Worker Group用於處理實際的讀寫IO。
Boss Group裡面的NioEventLoop會輪詢accept事件,遇到有新的連線,就生成NioSocketChannel,並把這個Channel註冊到Worker Group的Selector上。
Worker Group輪詢read和write事件,在可讀或者可寫條件滿足時,就進行處理。
Worker Group和Boss Group都是通過裡面的NioEventLoop
來操作的。NioEventLoop中維護了一個執行緒和任務佇列,支援非同步提交執行任務,執行緒啟動時會呼叫NioEventLoop的run
方法。
最後都會執行一個runAllTasks
方法,它用於處理任務佇列中的任務。任務佇列中的任務包括使用者呼叫 eventloop.execute或schedule執行的任務,或者其他執行緒提交到該eventloop的任務。
以下是使用Netty建立一個Server的示例程式碼。Client也可以使用Netty來建立,也可以使用之前文章裡面的NIO示例程式碼。這裡就不展示基於Netty的Client端的程式碼,只展示Server端的程式碼了。
public class Server {
private final static int PORT = 8080;
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
ChannelFuture f = b.bind(PORT).sync();
System.out.println(Thread.currentThread().getName() +
",伺服器開始監聽埠,等待客戶端連線.........");
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ServerHandler());
}
}
static class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] reg = new byte[buf.readableBytes()];
buf.readBytes(reg);
String body = new String(reg, StandardCharsets.UTF_8);
System.out.println(Thread.currentThread().getName() +
",The server receive order : " + body);
String respMsg = "I am Server,訊息接收 success!";
ByteBuf respByteBuf = Unpooled.copiedBuffer(respMsg.getBytes());
ctx.write(respByteBuf);
}
}
}
預覽
作者:公眾號_xy的技術圈
連結:www.imooc.com/article/289251
來源:慕課網
本作品採用《CC 協議》,轉載必須註明作者和本文連結