感謝網友【黃億華】投遞本稿。
<code></code>大概用netty的,無論新手還是老手,都知道它是一個“網絡通訊架構”。所謂架構,基本上都是一個作用:基于底層api,提供更便捷的程式設計模型。那麼”通訊架構”到底做了什麼事情呢?回答這個問題并不太容易,我們不妨反過來看看,不使用netty,直接基于nio編寫網絡程式,你需要做什麼(以server端tcp連接配接為例,這裡我們使用reactor模型):
監聽端口,建立socket連接配接
建立線程,處理内容
讀取socket内容,并對協定進行解析
進行邏輯處理
回寫響應内容
如果是多次互動的應用(smtp、ftp),則需要保持連接配接多進行幾次互動
關閉連接配接
建立線程是一個比較耗時的操作,同時維護線程本身也有一些開銷,是以我們會需要多線程機制,幸好jdk已經有很友善的多線程架構了,這裡我們不需要花很多心思。 此外,因為tcp連接配接的特性,我們還要使用連接配接池來進行管理:
建立tcp連接配接是比較耗時的操作,對于頻繁的通訊,保持連接配接效果更好
對于并發請求,可能需要建立多個連接配接
維護多個連接配接後,每次通訊,需要選擇某一可用連接配接
連接配接逾時和關閉機制
想想就覺得很複雜了!實際上,基于nio直接實作這部分東西,即使是老手也容易出現錯誤,而使用netty之後,你隻需要關注邏輯處理部分就可以了。
這裡我們引用netty的example包裡的一個例子,一個簡單的echoserver,它接受用戶端輸入,并将輸入原樣傳回。其主要代碼如下:
<code>01</code>
<code>public</code> <code>void</code> <code>run() {</code>
<code>02</code>
<code>// configure the server.</code>
<code>03</code>
<code>serverbootstrap bootstrap =</code><code>new</code> <code>serverbootstrap(</code>
<code>04</code>
<code>new</code> <code>nioserversocketchannelfactory(</code>
<code>05</code>
<code>executors.newcachedthreadpool(),</code>
<code>06</code>
<code>executors.newcachedthreadpool()));</code>
<code>07</code>
<code>08</code>
<code>// set up the pipeline factory.</code>
<code>09</code>
<code>bootstrap.setpipelinefactory(</code><code>new</code> <code>channelpipelinefactory() {</code>
<code>10</code>
<code>public</code> <code>channelpipeline getpipeline()</code><code>throws</code> <code>exception {</code>
<code>11</code>
<code>return</code> <code>channels.pipeline(</code><code>new</code> <code>echoserverhandler());</code>
<code>12</code>
<code>}</code>
<code>13</code>
<code>});</code>
<code>14</code>
<code>15</code>
<code>// bind and start to accept incoming connections.</code>
<code>16</code>
<code>bootstrap.bind(</code><code>new</code> <code>inetsocketaddress(port));</code>
<code>17</code>
這裡<code>echoserverhandler</code>是其業務邏輯的實作者,大緻代碼如下:
<code>1</code>
<code>public</code> <code>class</code> <code>echoserverhandler</code><code>extends</code> <code>simplechannelupstreamhandler {</code>
<code>2</code>
<code>3</code>
<code> </code><code>@override</code>
<code>4</code>
<code> </code><code>public</code> <code>void</code> <code>messagereceived(</code>
<code>5</code>
<code> </code><code>channelhandlercontext ctx, messageevent e) {</code>
<code>6</code>
<code> </code><code>// send back the received message to the remote peer.</code>
<code>7</code>
<code> </code><code>e.getchannel().write(e.getmessage());</code>
<code>8</code>
<code> </code><code>}</code>
<code>9</code>
還是挺簡單的,不是嗎?
完成了以上一段代碼,我們算是與netty進行了第一次親密接觸。如果想深入學習呢?
了解netty的關鍵點在哪呢?我覺得,除了nio的相關知識,另一個就是事件驅動的設計思想。什麼叫事件驅動?我們回頭看看<code>echoserverhandler</code>的代碼,其中的參數:<code>public void messagereceived(channelhandlercontext ctx, messageevent e)</code>,messageevent就是一個事件。這個事件攜帶了一些資訊,例如這裡<code>e.getmessage()</code>就是消息的内容,而<code>echoserverhandler</code>則描述了處理這種事件的方式。一旦某個事件觸發,相應的handler則會被調用,并進行處理。這種事件機制在ui程式設計裡廣泛應用,而netty則将其應用到了網絡程式設計領域。
在netty裡,所有事件都來自<code>channelevent</code>接口,這些事件涵蓋監聽端口、建立連接配接、讀寫資料等網絡通訊的各個階段。而事件的處理者就是<code>channelhandler</code>,這樣,不但是業務邏輯,連網絡通訊流程中底層的處理,都可以通過實作<code>channelhandler</code>來完成了。事實上,netty内部的連接配接處理、協定編解碼、逾時等機制,都是通過handler完成的。當部落客弄明白其中的奧妙時,不得不佩服這種設計! 下圖描述了netty進行事件處理的流程。<code>channel</code>是連接配接的通道,是channelevent的産生者,而<code>channelpipeline</code>可以了解為channelhandler的集合。
了解了netty的事件驅動機制,我們現在可以來研究netty的各個子產品了。netty的包結構如下:
<code>org</code>
<code> </code><code>└── jboss</code>
<code> </code><code>└── netty</code>
<code> </code><code>├── bootstrap 配置并啟動服務的類</code>
<code> </code><code>├── buffer 緩沖相關類,對nio buffer做了一些封裝</code>
<code> </code><code>├── channel 核心部分,處理連接配接</code>
<code> </code><code>├── container 連接配接其他容器的代碼</code>
<code> </code><code>├── example 使用示例</code>
<code> </code><code>├── handler 基于handler的擴充部分,實作協定編解碼等附加功能</code>
<code> </code><code>├── logging 日志</code>
<code> </code><code>└── util 工具類</code>
在這裡面,<code>channel</code>和<code>handler</code>兩部分比較複雜。我們不妨與netty官方的結構圖對照一下,來了解其功能。
zero-copy-capable rich byte buffer 零拷貝的buffer。為什麼叫零拷貝?因為在資料傳輸時,最終處理的資料會需要對單個傳輸層的封包,進行組合或者拆分。nio原生的bytebuffer要做到這件事,需要對bytebuffer内容進行拷貝,産生新的bytebuffer,而netty通過提供composite(組合)和slice(切分)兩種buffer來實作零拷貝。這部分代碼在<code>org.jboss.netty.buffer</code>包中。
universal communication api 統一的通訊api。因為java的old i/o和new i/o,使用了互不相容的api,而netty則提供了統一的api(<code>org.jboss.netty.channel.channel</code>)來封裝這兩種i/o模型。這部分代碼在<code>org.jboss.netty.channel</code>包中。