小白科普:Netty有什么用?
隨著移動互聯(lián)網(wǎng)的爆發(fā)性增長,小明公司的電子商務(wù)系統(tǒng)訪問量越來越大,由于現(xiàn)有系統(tǒng)是個單體的巨型應(yīng)用,已經(jīng)無法滿足海量的并發(fā)請求,拆分勢在必行。
在微服務(wù)的大潮之中, 架構(gòu)師小明把系統(tǒng)拆分成了多個服務(wù),根據(jù)需要部署在多個機器上,這些服務(wù)非常靈活,可以隨著訪問量彈性擴展。
世界上沒有免費的午餐, 拆分成多個“微服務(wù)”以后雖然增加了彈性,但也帶來了一個巨大的挑戰(zhàn):服務(wù)之間互相調(diào)用的開銷。
比如說:原來用戶下一個訂單需要登錄,瀏覽產(chǎn)品詳情,加入購物車,支付,扣庫存等一系列操作,在單體應(yīng)用的時候它們都在一臺機器的同一個進程中,說白了就是模塊之間的函數(shù)調(diào)用,效率超級高。
現(xiàn)在好了,服務(wù)被安置到了不同的服務(wù)器上,一個訂單流程,幾乎每個操作都要越網(wǎng)絡(luò),都是遠程過程調(diào)用(RPC), 那執(zhí)行時間、執(zhí)行效率可遠遠比不上以前了。
遠程過程調(diào)用的第一版實現(xiàn)使用了HTTP協(xié)議,也就是說各個服務(wù)對外提供HTTP接口。 小明發(fā)現(xiàn),HTTP協(xié)議雖然簡單明了,但是廢話太多,僅僅是給服務(wù)器發(fā)個簡單的消息都會附帶一大堆無用信息:
- GET /orders/1 HTTP/1.1
- Host: order.myshop.com
- User-Agent: Mozilla/5.0 (Windows NT 6.1; )
- Accept: text/html;
- Accept-Language: en-US,en;
- Accept-Encoding: gzip
- Connection: keep-alive
- ......
看看那User-Agent,Accept-Language ,這個協(xié)議明顯是為瀏覽器而生的!但是我這里是程序之間的調(diào)用,用這個HTTP有點虧。
能不能自定義一個精簡的協(xié)議? 在這個協(xié)議中我只需要把要調(diào)用方法名和參數(shù)發(fā)給服務(wù)器即可,根本不用這么多亂七八糟的額外信息。
但是自定義協(xié)議客戶端和服務(wù)器端就得直接使用“低級”的Socket了,尤其是服務(wù)器端,得能夠處理高并發(fā)的訪問請求才行。
小明復(fù)習(xí)了一下服務(wù)器端的socket編程,最早的Java是所謂的阻塞IO(Blocking IO), 想處理多個socket的連接的話需要創(chuàng)建多個線程, 一個線程對應(yīng)一個。
這種方式寫起來倒是挺簡單的,但是連接(socket)多了就受不了了,如果真的有成千上萬個線程同時處理成千上萬個socket,占用大量的空間不說,光是線程之間的切換就是一個巨大的開銷。
更重要的是,雖然有大量的socket,但是真正需要處理的(可以讀寫數(shù)據(jù)的socket)卻不多,大量的線程處于等待數(shù)據(jù)狀態(tài)(這也是為什么叫做阻塞的原因),資源浪費得讓人心疼。
后來Java為了解決這個問題,又搞了一個非阻塞IO(NIO:Non-Blocking IO,有人也叫做New IO), 改變了一下思路:通過多路復(fù)用的方式讓一個線程去處理多個Socket。
這樣一來,只需要使用少量的線程就可以搞定多個socket了,線程只需要通過Selector去查一下它所管理的socket集合,哪個Socket的數(shù)據(jù)準(zhǔn)備好了,就去處理哪個Socket,一點兒都不浪費。
好了,就是Java NIO了!
小明先定義了一套精簡的RPC的協(xié)議,里邊規(guī)定了如何去調(diào)用一個服務(wù),方法名和參數(shù)該如何傳遞,返回值用什么格式......等等。然后雄心勃勃地要把這個協(xié)議用Java NIO給實現(xiàn)了。
可是美好的理想很快被無情的現(xiàn)實給擊碎, 小明努力了一周就意識到自己陷入了一個大坑之中,Java NIO雖然看起來簡單,但是API還是太“低級”了,有太多的復(fù)雜性,沒有強悍的、一流的編程能力根本無法駕馭,根本做不到高并發(fā)情況下的可靠和高效。
小明不死心,繼續(xù)向領(lǐng)導(dǎo)要人要資源,一定要把這個坑給填上,掙扎了6個月以后,終于實現(xiàn)了一個自己的NIO框架,可以執(zhí)行高并發(fā)的RPC調(diào)用了。
然后又是長達6個月的修修補補,小明經(jīng)常半夜被叫醒:生產(chǎn)環(huán)境的RPC調(diào)用無法返回了! 這樣的Bug不知道改了多少個。
在那些不眠之夜中,小明經(jīng)常仰天長嘆:我用NIO做個高并發(fā)的RPC框架怎么這么難吶!
一年之后,自研的框架終于穩(wěn)定,可是小明也從張大胖那里聽到了一個讓他崩潰的消息: 小明你知道嗎?有個叫Netty的開源框架,可以快速地開發(fā)高性能的面向協(xié)議的服務(wù)器和客戶端。 易用、健壯、安全、高效,你可以在Netty上輕松實現(xiàn)各種自定義的協(xié)議!咱們也試試?
小明趕緊研究,看完后不由得“淚流滿面”:這東西怎么不早點出來??!
好了,這個故事我快編不下去了,要爛尾了。
說說Netty到底是何方神圣, 要解決什么問題吧。
像上面小明的例子,想使用Java NIO來實現(xiàn)一個高性能的RPC框架,調(diào)用協(xié)議,數(shù)據(jù)的格式和次序都是自己定義的,現(xiàn)有的HTTP根本玩不轉(zhuǎn),那使用Netty就是絕佳的選擇。
其實游戲領(lǐng)域是個更好的例子,長連接,自定義協(xié)議,高并發(fā),Netty就是絕配。
因為Netty本身就是一個基于NIO的網(wǎng)絡(luò)框架, 封裝了Java NIO那些復(fù)雜的底層細節(jié),給你提供簡單好用的抽象概念來編程。
注意幾個關(guān)鍵詞,首先它是個框架,是個“半成品”,不能開箱即用,你必須得拿過來做點定制,利用它開發(fā)出自己的應(yīng)用程序,然后才能運行(就像使用Spring那樣)。
一個更加知名的例子就是阿里巴巴的Dubbo了,這個RPC框架的底層用的就是Netty。
另外一個關(guān)鍵詞是高性能,如果你的應(yīng)用根本沒有高并發(fā)的壓力,那就不一定要用Netty了。
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號coderising獲取授權(quán)】