怎么通俗的理解Netty呢?
Netty(3.X)
有了Netty,你可以實(shí)現(xiàn)自己的HTTP服務(wù)器,FTP服務(wù)器,UDP服務(wù)器,RPC服務(wù)器,WebSocket服務(wù)器,Redis的Proxy服務(wù)器,MySQL的Proxy服務(wù)器等等。
如果你想知道Nginx是怎么寫出來的,如果你想知道Tomcat和Jetty是如何實(shí)現(xiàn)的,如果你也想實(shí)現(xiàn)一個(gè)簡單的Redis服務(wù)器,那都應(yīng)該好好理解一下Netty,它們高性能的原理都是類似的。
看一下傳統(tǒng)的HTTP服務(wù)器的原理:
創(chuàng)建一個(gè)ServerSocket,監(jiān)聽并綁定一個(gè)端口
一系列客戶端來請求這個(gè)端口
服務(wù)器使用Accept,獲得一個(gè)來自客戶端的Socket連接對象
啟動(dòng)一個(gè)新線程處理連接
讀Socket,得到字節(jié)流
解碼協(xié)議,得到Http請求對象
處理Http請求,得到一個(gè)結(jié)果,封裝成一個(gè)HttpResponse對象
編碼協(xié)議,將結(jié)果序列化字節(jié)流
寫Socket,將字節(jié)流發(fā)給客戶端
繼續(xù)循環(huán)步驟3
HTTP服務(wù)器之所以稱為 HTTP 服務(wù)器,是因?yàn)榫幋a解碼協(xié)議是HTTP協(xié)議,如果協(xié)議是Redis協(xié)議,那它就成了Redis服務(wù)器,如果協(xié)議是WebSocket,那它就成了WebSocket服務(wù)器,等等。
使用Netty你就可以定制編解碼協(xié)議,實(shí)現(xiàn)自己的特定協(xié)議的服務(wù)器。
上面我們說的是一個(gè)傳統(tǒng)的多線程服務(wù)器,這個(gè)也是Apache處理請求的模式。在高并發(fā)環(huán)境下,線程數(shù)量可能會創(chuàng)建太多,操作系統(tǒng)的任務(wù)調(diào)度壓力大,系統(tǒng)負(fù)載也會比較高。那怎么辦呢?
于是 NIO 誕生了,NIO并不是Java獨(dú)有的概念,NIO代表的一個(gè)詞匯叫著IO多路復(fù)用。它是由操作系統(tǒng)提供的系統(tǒng)調(diào)用,早期這個(gè)操作系統(tǒng)調(diào)用的名字是 select ,但是性能低下,后來漸漸演化成了Linux下的epoll和Mac里的kqueue。我們一般就說是epoll,因?yàn)闆]有人拿蘋果電腦作為服務(wù)器使用對外提供服務(wù)。而Netty就是基于Java NIO技術(shù)封裝的一套框架。為什么要封裝,因?yàn)樵腏ava NIO使用起來沒那么方便,而且還有臭名昭著的bug,Netty把它封裝之后,提供了一個(gè)易于操作的使用模式和接口,用戶使用起來也就便捷多了。
那NIO究竟是什么東西呢?
NIO的全稱是NoneBlocking IO,非阻塞IO,區(qū)別與BIO,BIO的全稱是Blocking IO,阻塞IO。那這個(gè)阻塞是什么意思呢?
Accept是阻塞的,只有新連接來了,Accept才會返回,主線程才能繼
Read是阻塞的,只有請求消息來了,Read才能返回,子線程才能繼續(xù)處理
Write是阻塞的,只有客戶端把消息收了,Write才能返回,子線程才能繼續(xù)讀取下一個(gè)請求
所以傳統(tǒng)的多線程服務(wù)器是BlockingIO模式的,從頭到尾所有的線程都是阻塞的。這些線程就干等在哪里,占用了操作系統(tǒng)的調(diào)度資源,什么事也不干,是浪費(fèi)。
那么NIO是怎么做到非阻塞的呢?
它用的是 事件機(jī)制 。它可以用一個(gè)線程把Accept,讀寫操作,請求處理的邏輯全干了。如果什么事都沒得做,它也不會死循環(huán),它會將線程休眠起來,直到下一個(gè)事件來了再繼續(xù)干活,這樣的一個(gè)線程稱之為NIO線程。
while true {events = takeEvents(fds) // 獲取事件,如果沒有事件,線程就休眠for event in events {if event.isAcceptable {doAccept() // 新鏈接來了} elif event.isReadable {request = doRead() // 讀消息if request.isComplete() {doProcess()}} elif event.isWriteable {doWrite() // 寫消息}} }Netty是建立在NIO基礎(chǔ)之上,Netty在NIO之上又提供了更高層次的抽象。
在Netty里面,Accept連接可以使用單獨(dú)的線程池去處理,讀寫操作又是另外的線程池來處理。
Accept連接和讀寫操作也可以使用同一個(gè)線程池來進(jìn)行處理。而請求處理邏輯既可以使用單獨(dú)的線程池進(jìn)行處理,也可以跟放在讀寫線程一塊處理。線程池中的每一個(gè)線程都是NIO線程。用戶可以根據(jù)實(shí)際情況進(jìn)行組裝,構(gòu)造出滿足系統(tǒng)需求的并發(fā)模型。
Netty提供了內(nèi)置的常用編解碼器,包括行編解碼器[一行一個(gè)請求],前綴長度編解碼器[前N個(gè)字節(jié)定義請求的字節(jié)長度],可重放解碼器[記錄半包消息的狀態(tài)],HTTP編解碼器,WebSocket消息編解碼器等等
Netty提供了一些列生命周期回調(diào)接口,當(dāng)一個(gè)完整的請求到達(dá)時(shí),當(dāng)一個(gè)連接關(guān)閉時(shí),當(dāng)一個(gè)連接建立時(shí),用戶都會收到回調(diào)事件,然后進(jìn)行邏輯處理。
Netty可以同時(shí)管理多個(gè)端口,可以使用NIO客戶端模型,這些對于RPC服務(wù)是很有必要的。
Netty除了可以處理TCP Socket之外,還可以處理UDP Socket。
在消息讀寫過程中,需要大量使用ByteBuffer,Netty對ByteBuffer在性能和使用的便捷性上都進(jìn)行了優(yōu)化和抽象。
本質(zhì):
1)JBoss做的一個(gè)Jar包。
2)目的:快速開發(fā)高性能、高可靠性的網(wǎng)絡(luò)服務(wù)器和客戶端程序。
3)優(yōu)點(diǎn):提供異步的、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具。
簡單體驗(yàn)
public void run() {// Configure the server.ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));// Set up the pipeline factory.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {public ChannelPipeline getPipeline() throws Exception {return Channels.pipeline(new EchoServerHandler());}});// Bind and start to accept incoming connections.bootstrap.bind(new InetSocketAddress(port));}這里 EchoServerHandler 是其業(yè)務(wù)邏輯的實(shí)現(xiàn)者,大致代碼如下:
public class EchoServerHandler extends SimpleChannelUpstreamHandler {@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {// Send back the received message to the remote peer.e.getChannel().write(e.getMessage());}Netty的事件驅(qū)動(dòng)機(jī)制
看看 EchoServerHandler 的代碼,其中的參數(shù): public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) ,MessageEvent就是一個(gè)事件。這個(gè)事件攜帶了一些信息,例如這里 e.getMessage() 就是消息的內(nèi)容,而 EchoServerHandler 則描述了處理這種事件的方式。一旦某個(gè)事件觸發(fā),相應(yīng)的Handler則會被調(diào)用,并進(jìn)行處理。這種事件機(jī)制在UI編程里廣泛應(yīng)用,而Netty則將其應(yīng)用到了網(wǎng)絡(luò)編程領(lǐng)域。
在Netty里,所有事件都來自 ChannelEvent 接口,這些事件涵蓋監(jiān)聽端口、建立連接、讀寫數(shù)據(jù)等網(wǎng)絡(luò)通訊的各個(gè)階段。而事件的處理者就是 ChannelHandler ,這樣,不但是業(yè)務(wù)邏輯,連網(wǎng)絡(luò)通訊流程中底層的處理,都可以通過實(shí)現(xiàn) ChannelHandler 來完成了。事實(shí)上,Netty內(nèi)部的連接處理、協(xié)議編解碼、超時(shí)等機(jī)制,都是通過handler完成的。
下圖描述了Netty進(jìn)行事件處理的流程。 Channel 是連接的通道,是ChannelEvent的產(chǎn)生者,而 ChannelPipeline 可以理解為ChannelHandler的集合。
Netty的源碼閱讀
除了之前說到的事件驅(qū)動(dòng)機(jī)制之外,Netty的核心功能還包括兩部分:
Zero-Copy-Capable Rich Byte Buffer
零拷貝的Buffer。為什么叫零拷貝?因?yàn)樵跀?shù)據(jù)傳輸時(shí),最終處理的數(shù)據(jù)會需要對單個(gè)傳輸層的報(bào)文,進(jìn)行組合或者拆分。NIO原生的ByteBuffer無法做到這件事,而Netty通過提供Composite(組合)和Slice(切分)兩種Buffer來實(shí)現(xiàn)零拷貝。這部分代碼在 org.jboss.netty.buffer 包中。
這里需要額外注意,不要和操作系統(tǒng)級別的Zero-Copy混淆了, 操作系統(tǒng)中的零拷貝主要是用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝, NIO中通過DirectBuffer做了實(shí)現(xiàn).
Universal Communication API
統(tǒng)一的通訊API。這個(gè)是針對Java的Old I/O和New I/O,使用了不同的API而言。Netty則提供了統(tǒng)一的API( org.jboss.netty.channel.Channel )來封裝這兩種I/O模型。這部分代碼在 org.jboss.netty.channel 包中。
此外,Protocol Support功能通過handler機(jī)制實(shí)現(xiàn)。
**
Remi醬回來更新啦~
**
文章來源:https://www.tuicool.com/articles/iUry2aZ
總結(jié)
以上是生活随笔為你收集整理的怎么通俗的理解Netty呢?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊分布式锁——Redis和Rediss
- 下一篇: Spring Security 玩出花!