IO模型中,一個連接來了,會創(chuàng)建一個線程,對應一個while死循環(huán),死循環(huán)的目的就是不斷監(jiān)測這個連接上是否有數(shù)據(jù)可以讀。在大多數(shù)情況下,1萬個連接里面同一時刻只有少量的連接有數(shù)據(jù)可讀,因此,很多while死循環(huán)都白白浪費掉了,因為讀不出數(shù)據(jù)。數(shù)據(jù)讀寫是以字節(jié)流為單位的。
NIO模型中,這么多while死循環(huán)轉(zhuǎn)換為一個死循環(huán),這個死循環(huán)由一個線程控制,那么NIO又是如何做到一個線程一個while死循環(huán)就能監(jiān)測1萬個連接是否有數(shù)據(jù)可讀的呢?
這就是NIO模型中Selector的作用,一個連接來了之后,不會創(chuàng)建一個while死循環(huán)去監(jiān)聽是否有數(shù)據(jù)可讀,而是直接把這條連接注冊到Selector上。然后,通過檢查這個Selector,就可以批量監(jiān)測出有數(shù)據(jù)可讀的連接,進而讀取數(shù)據(jù)。
通常:
NIO模型中會有兩個線程,每個線程都綁定一個輪詢器Selector。serverSelector負責輪詢是否有新連接,clientSelector負責輪詢連接是否有數(shù)據(jù)可讀。
服務端監(jiān)測到新連接之后,不再創(chuàng)建一個新線程,而是直接將新連接綁定到clientSelector上,這樣就不用IO模型中的1萬個while循環(huán)死等。
clientSelector被一個while死循環(huán)包裹著,如果在某一時刻有多個連接有數(shù)據(jù)可讀,那么通過clientSelector.select(1)方法可以輪詢出來,進而批量處理。
數(shù)據(jù)的讀寫是面向Buffer的。
直接基于JDK原生NIO來進行網(wǎng)絡開發(fā)十分復雜而且很容易出bug,Netty封裝了NIO,不用再寫一大堆復雜代碼了。
使用JDK原生NIO需要了解太多概念,編程復雜,一不小心就Bug橫飛。
Netty底層IO模型隨意切換,而這一切只需要做微小的改動,改改參數(shù),Netty可以直接從NIO模型變身為IO模型。
Netty自帶的拆包/粘包、異常檢測等機制讓你從NIO的繁重細節(jié)中脫離出來,只需要關(guān)心業(yè)務邏輯即可。
Netty解決了JDK很多包括空輪詢在內(nèi)的Bug。
Netty底層對線程、Selector做了很多細小的優(yōu)化,精心設(shè)計的Reactor線程模型可以做到非常高效的并發(fā)處理。
自帶各種協(xié)議棧,讓你處理任何一種通用協(xié)議都幾乎不用親自動手。
Netty社區(qū)活躍,遇到問題隨時郵件列表或者Issue。
Netty已經(jīng)歷各大RPC框架、消息中間件、分布式通信中間件線上的廣泛驗證,健壯性無比強大。
用Netty來實現(xiàn)一個簡單的通信demo
Maven依賴
<dependency>
? ? ? ? <groupId>io.netty</groupId>
? ? ? ? <artifactId>netty-all</artifactId>
? ? ? ? <version>4.1.6.Final</version>
</dependency>
服務端:
public static void main(String[] args) {
? ? ? ? ServerBootstrap serverBootstrap =new ServerBootstrap();
NioEventLoopGroup boss =new NioEventLoopGroup();
NioEventLoopGroup worker =new NioEventLoopGroup();
serverBootstrap
? ? ? ? ? ? ? ? .group(boss,worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
? ? ? ? ? ? ? ? ? ? protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler() {
@Override
? ? ? ? ? ? ? ? ? ? ? ? ? ? protected void channelRead0(ChannelHandlerContext ctx,String msg) {
System.out.println(msg);
}
});
}
})
.bind(8000);
}
客戶端:
public static void main(String[] args)throws InterruptedException {
Bootstrap bootstrap =new Bootstrap();
NioEventLoopGroup group =new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
? ? ? ? ? ? ? ? protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder());
}
});
Channel channel =bootstrap.connect("127.0.0.1",8000).channel();
channel.close();
while (true) {
channel.writeAndFlush(new Date() +": hello world!");
Thread.sleep(2000);
}
}