Netty是快速開發高效能,高擴充套件性的網路伺服器和客戶端提供的非同步框架。目標有:
-
快速輕鬆開發,不僅能快速輕鬆開發tcp,udp程式,還能開發ftp,http外的其他應用層協議。
-
高效能,高擴充套件,基於Java的NIO設計了優秀的Reactor模式實現。
這裡選擇Netty4版本。依賴如下:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.113.Final</version>
</dependency>
以一個簡單的服務開始Netty的學習之旅。用Netty實現一個Discard服務:丟棄客戶端傳送的資料,也不向客戶端傳送資料。
public static void startServer(int port) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 監聽客戶端並接受連線輪詢組
NioEventLoopGroup bossEventLoppGroup = new NioEventLoopGroup();
//工作輪詢組
NioEventLoopGroup workEventLoppGroup = new NioEventLoopGroup();
try {
// 1、設定輪詢組
ChannelFuture channelFuture = serverBootstrap.group(bossEventLoppGroup, workEventLoppGroup)
// 2、設定NIO型別通道
.channel(NioServerSocketChannel.class)
// 3、設定監聽埠
.localAddress(new InetSocketAddress("localhost",port))
// 4、設定通道引數
.childOption(ChannelOption.SO_KEEPALIVE, true)
// 5、裝配子通道流水線
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new DiscardHandle());
}
})
// 6、繫結伺服器
.bind();
ChannelFuture sync = channelFuture.sync();
System.out.println("伺服器啟動成功,繫結埠"+sync.channel().localAddress());
// 7、等待通道非同步關閉
sync.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8 、釋放資源
bossEventLoppGroup.shutdownGracefully();
workEventLoppGroup.shutdownGracefully();
}
}
//ChannelInboundHandlerAdapter是ChannelInboundHandler的預設實現
public class DiscardHandle extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("收到訊息如下");
try {
while (byteBuf.isReadable()) {
System.out.print((char) byteBuf.readByte());
}
System.out.println();
} finally {
ReferenceCountUtil.release(byteBuf);
}
}
}
ServerBootstrap負責Netty元件的組裝,配置,Netty伺服器或客戶端的啟動。也可以不用ServerBootstrap,而由自己負責這些工作。
1、第一步建立了兩個反映器輪詢組。回顧下Reactor模式,這裡的輪詢組實現了Reactor模式中Reactor執行緒的工作。第一個輪詢組叫作boss輪詢組,負責監聽並連線客戶端,也叫做“包工頭”,第二個輪詢組叫做工作輪詢組,負責資料的傳輸及處理。也可以直接用一個輪詢組。反映器輪詢組與Reactor模式中的Reactor執行緒不同,一個輪詢組中有一個執行緒池,裡面的每個執行緒相當於Reactor模式中的Reactor執行緒。
2、設定通道的型別。Netty不止支援NIO,也支援BIO,還可以配置為OioServerSocketChannel。
3、設定監聽埠。
4、設定通道的引數。注意第4步,第5步的方法名,有child
。為什麼要這麼命名?因為有父子通道的概念。在Linux系統中,NioServerSocketChannel包裝了Linux系統中的檔案描述符,這個檔案描述符負責監聽客戶端的連線。NioSocketChannel也包裝了Linux系統中的檔案描述符,負責資料傳輸及處理。NioServerSocketChannel叫做NioSocketChannel的父通道,NioSocketChannel叫NioServerSocketChannel的子通道。注意第4步,第5步的方法名設定的都是子通道中的屬性。所以要有child。
這些引數一般都是與TCP協議相關的。
SO_KEEPALIVE:是否開啟TCP的心跳機制,true為連線保持心跳,false為關閉。
SO_SNDBUF:TCP在核心中的傳送緩衝區大小
SO_RCVBUF:TCP在核心中的接收緩衝區大小
TCP_NODELAY:是否開啟Nagle演算法,為true,關閉Nagle演算法,馬上傳送資料。為false,開啟Nagle演算法,等資料積累緩衝區在傳送。
SO_BACKLOG:TCP三次握手時接收連線的佇列長度。
SO_LINGER:-1表示socket.close呼叫後立即返回,作業系統會將緩衝區的資料全部傳送。0表示socket.close呼叫後立即返回,作業系統會放棄傳送緩衝區的資料。非0的整數表示socket.close呼叫後被阻塞直到超時(數字表示超時時間)傳送緩衝區資料,若超時,接收端收到錯誤。
5、裝配子通道的Pipleline。伺服器接受客戶端的連線後,會呼叫此方法設定channel的Pipeline,Pipeline是個雙向連表,裡面儲存了ChannelInboundHandler(入站處理器,主要負責讀取)和ChannelOutboundHandler(出站處理器,主要負責寫入)。
入站處理器是從前往後執行,如果後面還有入站處理器,將IO事件交給下一個入站處理器處理。否則將結束。
出站處理器是從後往前執行。
6、繫結伺服器,並啟動。注意觀察。bind方法的返回值是ChannelFuture,,是非同步的,呼叫了sync讓它同步執行繫結成功。
7、獲取closeFuture關閉通道,並等待監聽通道關閉。
8、釋放資源。輪詢組內的執行緒,Selector,子通道都會被關閉。