易燃物匯聚 YouTube 有聲書與書評
Netty權威指南 封面

Netty權威指南

李林鋒

4360人在讀
21萬字總字數
¥15.8定價

內容簡介

《Netty權威指南》是異步非阻塞通信領域的經典之作,基於最新版本Netty 5.0編寫,是國內首本深入介紹Netty原理和架構的技術書籍,也是作者多年實戰經驗的總結和濃縮。內容包含基礎功能、高級應用、系統架構、源碼分析和行業應用,深入闡述了Java I/O的Netty NIO開發、Netty編解碼開發、Netty多協議開發等各方面的技術要點,包含了對源碼的深刻解讀,並且對Netty的應用現狀和未來趨勢進行分析,旨在幫助從業人員提升自我,更快更明確地發展職業道路。 本書適合架構師、設計師、軟件開發工程師、測試人員和其他對Java NIO框架、Java通信感興趣的相關人士閱讀,相信通過學習本書,能夠熟悉和掌握Netty這一優秀的異步通信框架,實現高可用分佈式系統的構建。

讀者書評

★★★★★

零、Netty的組件分成了下面幾種 (1)EventLoop:這裏就說線程模型,bossGroup和workerGroup。這裏的調優關心線程數量。 (2)Channel:對NioSocketChannel和NioServerSocketChannel的抽象,象徵着IO事件。那兩個子類一個是連接讀寫事件、一個是發起連接事件的抽象。 (3)ChannelHandler: (4)ChannelPipeline:過濾器鏈,初始化的時候就在這裏放上一堆過濾器,通過EventLoop監視底層IO事件的時候,每個事件觸發都要經過上層這一堆過濾器的處理之後才能完成。 (5)ByteBuf:這裏可以說直接內存構成的內存池,每次申請內存都從這個內存池裏面走。 這裏是內存池的調優,關心每一個內存頁的大小,16k還是32k,再關注整個內存池的大小。 一、 從BIO到NIO2.0 核心:每一次進化都是減少阻塞的 (1)BIO存在線程阻塞,每個連接符都對應一個線程,這裏就存在大量線程資源浪費; (2)NIO1.0 是 Java4 的時候增加進來的,這之前Java只能用BIO,epoll那些系統調用調不到,而那時候高性能網絡服務器都是用C++去寫的,C++能直接調系統調用。 NIO1.0就把select、epoll這些加進來了,減少線程阻塞。 (3)select和epoll之間對比: select只能判斷當前集合裏有準備好的channel,但需要遍歷一遍去尋找。這個遍歷一遍的過程在大量連接的情況下極損性能。 理論上一種情況就是每個連接都有數據,非常活躍,這種情況select比epoll性能好,因爲epoll會有多餘處理。 (4)NIO和AIO epoll通知的時候數據只是在內核態準備好了,需要我們自己拷貝到用戶態;但是IO_uring通知的時候,數據已經在用戶態裏面了,程序直接能順着運行,這就省下來一個內核態和用戶態的切換操作,也節約了用戶態的阻塞。 (5)NIO2.0 NIO1.0存在讀寫時候的用戶態阻塞,NIO2.0使用了底層的異步IO系統調用,如果IO完成了,操作系統會給JVM一個特殊的信號或者事件,JVM監聽到這個信號或者事件之後,自己執行代碼裏的回調函數。 就爲了節約用戶態的IO阻塞時間。 二、EventLoop和EventLoopGroup (1)EventLoopGroop是一個線程池,是ScheduledThreadPoolExecutor定時線程池的子類。 (2)EventLoop是一個處理器,每個channel往EventLoopGroup上註冊的時候,EventLoopGroop都會按照輪詢策略分配一個EventLoop處理器出來,其實就是一個處理線程。 由於一個Channel只註冊在一個處理線程上,所以多個EventLoop線程之間也沒啥併發關聯,各跑各的。 (3)然後這個線程在死循環執行run方法。 run方法裏面兩個核心流程,【一個是讀取epoll的系統調用】,有沒有待處理任務。如果有待處理任務,把這些任務放到自己的待處理任務隊列裏。 【第二個流程是全量跑自己的待處理任務隊列】,執行所有註冊的epoll讀寫任務以及定時任務。 (4)3和4講的是客戶端的EventLoop,【服務器端的EventLoop】的職責是接受連接,並且把SocketChannel分配給childEventLoopGroop,實現如下: 起一個ServerEventLoopGroop監控通過bind監聽某個端口的accept操作,並且初始化的時候,給這個Channel上掛一個ChannelHandler,這個ChannelHandler的核心操作是改寫channelRead方法,如果接收到連接,把這個連接封裝一下,並且跟用戶註冊的channelHandler綁定起來,扔到childEventLoop裏面去,等着childEventLoop的循環去監測上面的讀寫事件,並且觸發用戶自定義的channelHandler。 三、Channel Channel是一個數據通道,核心就是read和write兩個方法,channel實現的作用是對IO資源的封裝抽象,有FileChannel、SocketChannel、ServerSocketChannel各種IO資源。 1、JDK裏的SocketChannel和ServerSocketChannel兩種類 (1)系統調用都在ServerSocketChannel上,bind、listen、accept,從這裏產出SocketChannel。 (2)SocketChannel(連接)上就只有有數據、沒數據兩種情況,不存在epoll系統調用,只有讀寫操作。 (3)這就是Netty爲什麼要封裝一個Channel接口的原因,JDK裏面是兩個接口,不通用,把這兩個合併抽出一個公共接口Channel來。 2、Channel和讀寫緩衝區 (1)Linux底層上每個socket_fd是附帶了兩個讀寫緩衝區的,這是TCP的發送和接受緩衝區,內核態的 (2)用戶態給每個SocketChannel都做了兩個一模一樣的出來,用戶態的讀寫緩衝區解決的問題: 寫緩衝區要解決拆包的問題,如果內核態寫不進去了,就先存在用戶的寫緩衝區裏面,通過byteBuffer的寫緩衝指針標識寫到哪了 讀緩衝區也解決拆包問題,還解決業務線程處理不過來的問題,暫時充當緩存。 (3)epoll可以註冊的監控任務有 讀、寫、連接、斷連 這些任務。寫任務是因爲TCP磁盤緩衝區不一定有位置,可能寫不下去,所以先寫到寫操作對應的用戶態環形緩衝區裏面,等TCP磁盤緩衝區有位置了之後再執行寫操作。 (4)所以當真執行寫操作的時候,先寫到用戶態的讀寫緩衝裏面,註冊一個Linux內核的寫事件的監控扔給EventLoop,等TCP在內核上的緩衝區可寫之後,EventLoop執行這個寫操作。 (5)每一個SocketChannel生成的時候,理論上都是通過ServerSocketChannel的accept方法生成的,生成之後把socketChannel跟channelHandler綁定在一起,扔給workerGroup去處理,channel和處理器的綁定是初始化生成出來的時候就綁定上的。 四、ChannelHandler 1、過濾器模式的優點: 過濾器提供了一種面向對象的模塊化機制,用來將公共任務封裝到可插入的組件中。這些組件通過Web部署配置文件(web.xml)進行聲明,可以方便地添加和刪除過濾器,無須改動任何應用程序代碼或JSP頁面,由Servlet進行動態調用。通過在請求/響應鏈中使用過濾器,可以對應用程序(而不是以任何方式替代)的Servlet或JSP頁面提供的核心處理進行補充,而不破壞Servlet或JSP頁面的功能。 2、 channelHandler裏存在的各種回調API: read、write、readComplete、writeComplete、connect、disconnect、close; 各種回調,都跟Channel裏面的API一一對應,永遠都是Channel裏先觸發某個流程,然後去ChannelHandler裏面去找對應的鉤子函數。 3、channelHandler的應用 (1)鏈路的有效性校驗,心跳機制 心跳就是一個定時任務,提交給EventLoop線程池去執行; 定時任務提交的時機,是channelHandler裏的connect調用返回,服務端握手發一個消息回來,客戶端就開始運行定時任務了 (2)斷路重連機制 斷路重連是服務端主動斷開連接,客戶端從disconnect裏面讀取到數據,或者從socket連接裏面讀出eof之類的報錯,觸發重連 (3)a. 協議解析:在實現特定網絡協議時,可以通過Pipeline中的Handler來進行協議的解碼和編碼。 b. 日誌和監控:可以在Pipeline中加入日誌Handler來記錄數據流經過的各個階段,以便於調試和監控。 c. 安全性:通過加入如SSL處理的Handler,可以爲數據傳輸提供加密和安全保障。 3、channelHandler的頭尾處理器上面帶真正的讀寫函數 4、ChannelPipeline核心就是維護這個過濾器鏈,跟Map、List的維護沒什麼區別,源碼寫的半斤八兩。 五、ByteBuffer 這個底層就是一個數組,是對於InputStream的升級,升級點有兩個:【非阻塞】和【直接內存】。 1、 傳統的read、write函數,都是阻塞的,比如read(char[])就是,讀取某個文件,除非報錯、文件結尾EOF或者傳入的數組讀滿了,不然會一直阻塞。 ByteBuffer結合【Channel可以實現非阻塞】(主要是channel實現的),如果當前channel裏暫時沒有內容了直接退出。 2、 ByteBuffer可以使用堆外的直接內存,較少內存複製。 3、ByteBuf源碼講解 (1)可以從field去理解核心邏輯,核心字段: byte[]數組、readIndex、writeIndex、最大容量 (2)field上相比於ByteBuffer的升級就是把讀指針和寫指針拆開了,ByteBuffer只有一個指針位,這一個指針位把數組分成三段,每次都要自己指定讀取左邊那段還是寫右邊那段 (3)完全的升級就是,【自動擴容】和【讀寫全雙工】 ByteBuf如果容量不夠了可以自己擴容,因爲記錄了寫指針可以自己計算可用容量。 擴容的時候先嚐試內存整理,把已經讀過的那部分丟掉,重新整理一次byte[],如果夠了就不用申請新的容量了;如果這還不夠,那就走數組擴容流程,設置一個閾值,先倍增後步進,這個策略就是爲了在浪費內存和更少擴容次數之間取平衡。 (4)其他一些API就是基於讀寫指針來操作的,比如複製就有duplicate和copy兩種,共享一個byte[]搞兩套讀寫指針,以及byte[]數組和讀寫指針都複製兩份,兩種策略 再就是多個ByteBuf進行聚合,組成一個更大的ByteBuf。 (5)粘包拆包的時候,從一個大的byteBuffer數組裏面,通過slice能精確拆分出一部分出來,這個也是零拷貝的一種實現。 六、TCP和UDP 1、TCP的粘包拆包問題略 2、TCP以流的方式進行數據傳輸,上層的應用協議爲了對消息進行區分,往往採用如下4種方式。 (1)消息長度固定,累計讀取到長度總和爲定長LEN的報文後,就認爲讀取到了一個完整的消息;將計數器置位,重新開始讀取下一個數據報; (2)將回車換行符作爲消息結束符,例如FTP協議,這種方式在文本協議中應用比較廣泛; (3)將特殊的分隔符作爲消息的結束標誌,回車換行符就是一種特殊的結束分隔符; (4)通過在消息頭中定義長度字段來標識消息的總長度。 Netty對上面四種應用做了統一的抽象,提供了4種解碼器來解決對應的問題,使用起來非常方便。有了這些解碼器,用戶不需要自己對讀取的報文進行人工解碼,也不需要考慮TCP的粘包和拆包。 3、Netty裏處理粘包拆包的處理器是半包處理器,半包處理器是第一個處理器,後面是編解碼Encode/Decode處理器; 粘包拆包和編解碼是兩個不同的處理器 4、UDP連接的底層調用: (1)bind、sendto、recvfrom,只有三個系統調用,比TCP的減少了accept建立連接的過程,直接就從綁定到端口的那個套接字裏面接受數據了。 (2)Netty的兼容處理,一個是建立連接的channel那裏需要改,不需要建立連接; 另一個是handler函數要改,因爲UDP數據報文不太一樣,類型不一樣。 七、 RPC的組成部分以及Netty組成之間的映射 1、 序列化考察標準 序列化的時間性能(快不快)、空間性能(序列化之後多大); 支持幾種語言、擴展性、需要開發者開發的多不多 這幾種性能卡一下xml和Java原生序列化,都是極差,不能採用。 2、 Thrift主要由5部分組成。 (1)語言系統以及IDL編譯器:負責由用戶給定的IDL文件生成相應語言的接口代碼; (2)TProtocol:RPC的協議層,可以選擇多種不同的對象序列化方式,如JSON和Binary; 這個是【報文內容的協議編碼層】。 (3)TTransport:RPC的傳輸層,同樣可以選擇不同的傳輸層實現,如socket、NIO、MemoryBuffer等; 這個是【傳輸層的編碼】,可以認爲是Socket的報文編碼,它的協議頭尾。 (4)TProcessor:作爲協議層和用戶提供的服務實現之間的紐帶,負責調用服務實現的接口; 這就是你提供的handler (5)TServer:聚合TProtocol、TTransport和TProcessor等對象。 這個可以理解成那個代理類,客戶端和服務端的代理類,這個代理類需要實現功能聚合。 3、 序列化知識補充 (1)Unicode是一種字符集合,utf-8、utf-16、utf-32都是對這種字符集的一種實現;所以unicode8/16/32的字符範圍都是一樣的,就是效率不一樣 Unicode-8是用1-4個字節來表示某一個Unicode字符,互聯網最常用Unicode-8編碼; Unicode-16包含了大部分東亞語言,編碼變成了2-4字節; Unicode-32用4字節定長表示。 (2)base64解決的是二進制編碼的打印問題 一串0101不一定能映射成String,所以6位一截斷,重新編碼之後找到能打印的字符。 (3)urlEncode的原理和原因 url裏需要對各種特殊字符進行特殊識別,比如/?=#之類的,這些字符如果出現在url裏面會有歧義,需要把這些特殊字符轉化成%25這種百分號編碼避免歧義。 (4)JSON序列化 A. Jackson和Gson對比: 性能速度:Jackson通常比Gson更快,因爲它直接解析JSON流,而不是先將其轉換爲中間表示形式。 內存使用:Jackson的內存佔用通常較低,因爲它可以逐行處理JSON數據,避免將整個JSON加載到內存中。 解釋Jackson對字符串的解析跟對象的生成是渾然一體的,Gson會先進行Json解析,變成JsonObject、JsonElement這種中間變量,匯聚成一棵Json樹的形式,再把這棵Json樹轉變成對象; 所以Gson的內存佔用高,且效率低。 B. FastJson FastJson會通過ASM的方式,根據已有的類定義,爲這個類量身定做一個工具類進行序列化和反序列化,避免了反射操作。 FastJson的安全性問題,是類型直接寫到了Json字符串裏面去,數據類型是從Json字符串讀出來的,通過@Type,而不是用戶自己輸入的,如果人爲構造這個類型變成跟RMI相關的類就能操縱機器去連接遠程服務器。 Gson是自己輸入的類型,避免了安全問題。 (5)RPC二進制序列化方式 a. Hession的格式,類定義、字段名、數據類型、變長長度、value b. Thrift的格式,id、數據類型、變長長度、value c. gRpc的格式,id、變長長度、value

★★★★★

就簡單介紹了netty的使用,沒有深入原理,需要配套其他的書籍文檔來深入netty。也講了一些Java的知識,作爲入門來說還算合格。這個推薦就是給的入門推薦

★★★★★

很多書沒有對比就沒有傷害,可以看到這本書作者花了很多心思,看完對 Netty 理解的很通透,感謝作者

★★★★★

由淺入深,有層次遞進感,讀了幾本,感覺這本還算不錯,也有深度,章節安排也合理;對於學習 netty 算是一本不錯的教材 源碼章節後續有空再看看。 不過netty 的內容確實很多,先整上幾個案例,喫透幾個,再來閱讀,就心裏有底多了。

★☆☆☆☆

全都一筆帶過,像一個API解說文檔

★★★☆☆

一個優秀的框架是怎麼樣的,基於本書來說,首先要立足於各種IO模型的優缺點,之後從傳輸層看TCP.UDP協議,以後選擇序列化協議,最好能夠跨語言,跨平臺,極致壓榨性能,方便開發者使用,再之後就是圍繞框架的各種插件可插拔,騰訊的trpc和字節跳動的kratos都已經開源了,感興趣的讀者可以去看看

★★★★★

一本講解netty使用的實戰書,值得推薦!

★★★★★

書其實還行

★★★★★

整體說的挺清楚了 就是源碼少了點

★☆☆☆☆

不明白其他人推薦的理由,我認爲只適合入門新手看。

書籍信息

作者李林鋒
出版社電子工業出版社
出版時間2014-06-01
ISBN9787121233432
分類計算機-計算機綜合
格式EPUB
完結狀態連載中
章節數27