天天看點

《Netty 權威指南》—— 選擇Netty的理由

在開始本節之前,我先講一個親身經曆的故事:曾經有兩個項目組同時用到了nio程式設計技術,一個項目組選擇自己開發nio服務端,直接使用jdk原生的api,結果2個多月過去了,他們的nio服務端始終無法穩定,問題頻出。由于nio通信是它們的核心元件之一,是以,項目的進度受到了嚴重的影響,上司對此非常惱火。另一個項目組直接使用netty作為nio服務端,業務的定制開發工作量非常小,測試表明,功能和性能都完全達标,項目組幾乎沒有在nio服務端上花費額外的時間和精力,項目進展也非常順利。

這兩個項目組的不同遭遇提醒我們:開發出高品質的nio程式并不是一件簡單的事情,除去nio固有的複雜性和bug不談,作為一個nio服務端需要能夠處理網絡的閃斷、用戶端的重複接入、用戶端的安全認證、消息的編解碼、半包讀寫等等,如果你沒有足夠的nio程式設計經驗積累,一個nio架構的穩定往往需要半年甚至更長的時間。更為糟糕的是一旦在生産環境中發生問題,往往會導緻跨節點的服務調用中斷,嚴重的可能會導緻整個叢集環境都不可用,需要重新開機伺服器,這種非正常停機會帶來巨大的損失。

從可維護性角度看,由于nio采用了異步非阻塞程式設計模型,而且是一個io線程處理多條鍊路,它的調試和跟蹤非常麻煩,特别是生産環境中的問題,我們無法有效調試和跟蹤,往往隻能靠一些日志來輔助分析,定位難度很大。

在本小節,我們總結下為什麼不建議開發者直接使用jdk的nio類庫進行開發的原因:

1)      nio的類庫和api繁雜,使用麻煩,你需要熟練掌握selector、serversocketchannel、socketchannel、bytebuffer等;

2)      需要具備其它的額外技能做鋪墊,例如熟悉java多線程程式設計,因為nio程式設計涉及到reactor模式,你必須對多線程和網路程式設計非常熟悉,才能編寫出高品質的nio程式;

3)      可靠性能力補齊,工作量和難度都非常大。例如用戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等等,nio程式設計的特點是功能開發相對容易,但是可靠性能力補齊工作量和難度都非常大;

4)      jdk nio的bug,例如臭名昭著的epoll bug,它會導緻selector空輪詢,最終導緻cpu 100%。官方聲稱在jdk1.6版本的update18修複了該問題,但是直到jdk1.7版本該問題仍舊存在,隻不過該bug發生機率降低了一些而已,它并沒有被根本解決。該bug以及與該bug相關的問題單如下:

<a href="http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6403933">http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6403933</a>

<a href="http://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719">http://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719</a>

異常堆棧如下:

<code>01</code>

<code>java.lang.thread.state: runnable</code>

<code>02</code>

<code>        </code><code>at sun.nio.ch.epollarraywrapper.epollwait(native method)</code>

<code>03</code>

<code>        </code><code>at sun.nio.ch.epollarraywrapper.poll(epollarraywrapper.java:</code><code>210</code><code>)</code>

<code>04</code>

<code>        </code><code>at sun.nio.ch.epollselectorimpl.doselect(epollselectorimpl.java:</code><code>65</code><code>)</code>

<code>05</code>

<code>        </code><code>at sun.nio.ch.selectorimpl.lockanddoselect(selectorimpl.java:</code><code>69</code><code>)</code>

<code>06</code>

<code>        </code><code>- locked &amp;amp;lt;</code><code>0x0000000750928190</code><code>&amp;amp;gt; (a sun.nio.ch.util$</code><code>2</code><code>)</code>

<code>07</code>

<code>        </code><code>- locked &amp;amp;lt;</code><code>0x00000007509281a8</code><code>&amp;amp;gt; (a java.util.collections$unmodifiableset)</code>

<code>08</code>

<code>        </code><code>- locked &amp;amp;lt;</code><code>0x0000000750946098</code><code>&amp;amp;gt; (a sun.nio.ch.epollselectorimpl)</code>

<code>09</code>

<code>        </code><code>at sun.nio.ch.selectorimpl.select(selectorimpl.java:</code><code>80</code><code>)</code>

<code>10</code>

<code>        </code><code>at net.spy.memcached.memcachedconnection.handleio(memcachedconnection.java:</code><code>217</code><code>)</code>

<code>11</code>

<code>        </code><code>at net.spy.memcached.memcachedconnection.run(memcachedconnection.java:</code><code>836</code><code>)</code>

由于上述原因,在大多數場景下,我不建議大家直接使用jdk的nio類庫,除非你精通nio程式設計或者有特殊的需求,在絕大多數的業務場景中,我們可以使用nio架構netty來進行nio程式設計,它既可以作為用戶端也可以作為服務端,同時支援udp和異步檔案傳輸,功能非常強大。

下個小節我們就看看為什麼選擇netty作為基礎通信架構。

netty是業界最流行的nio架構之一,它的健壯性、功能、性能、可定制性和可擴充性在同類架構中都是首屈一指的,它已經得到成百上千的商用項目驗證,例如hadoop的rpc架構avro使用netty作為底層通信架構。很多其它業界主流的rpc架構,也使用netty來建構高性能的異步通信能力。

通過對netty的分析,我們将它的優點總結如下:

1)      api使用簡單,開發門檻低;

2)      功能強大,預置了多種編解碼功能,支援多種主流協定;

3)      定制能力強,可以通過channelhandler對通信架構進行靈活的擴充;

4)      性能高,通過與其它業界主流的nio架構對比,netty的綜合性能最優;

5)      成熟、穩定,netty修複了已經發現的所有jdk nio bug,業務開發人員不需要再為nio的bug而煩惱;

6)      社群活躍,版本疊代周期短,發現的bug可以被及時修複,同時,更多的新功能會被加入;

7)      經曆了大規模的商業應用考驗,品質已經得到驗證。在網際網路、大資料、網絡遊戲、企業應用、電信軟體等衆多行業得到成功商用,證明了它可以完全滿足不同行業的商業應用。

正是因為這些優點,netty逐漸成為java nio程式設計的首選架構。

本章通過一個簡單的demo開發-時間伺服器程式,讓大家熟悉傳統的同步阻塞io、僞異步io、非阻塞io(nio)和異步io(aio)的程式設計和使用差異。然後對比了各自的優缺點,給出了使用建議。

最後,我們詳細介紹了為什麼不建議讀者朋友們直接使用jdk的nio原生類庫進行異步io的開發,同時對netty的優點進行分析和總結,給出使用netty進行nio開發的理由。

相信學完本章之後,大家對java的網絡程式設計已經有了初步的認識,從下一個章節開始,我們正式進入netty的世界,學習和掌握基于netty的網絡開發。