摘要:客戶端客戶端啟動(dòng)的常規(guī)代碼如下用于接收客戶端請求的線程池職責(zé)如下。注冊對(duì)應(yīng)的網(wǎng)絡(luò)監(jiān)聽狀態(tài)為到多路復(fù)用器。由多路復(fù)用器在現(xiàn)場中輪詢個(gè),處理連接結(jié)果。具體服務(wù)端與客戶端如何通信,以及內(nèi)存管理等方面的知識(shí)下一次再寫。
歡迎關(guān)注公眾號(hào):【愛編程】
如果有需要后臺(tái)回復(fù)2019贈(zèng)送1T的學(xué)習(xí)資料哦??!
本文是基于Netty4.1.36進(jìn)行分析
服務(wù)端Netty服務(wù)端的啟動(dòng)代碼基本都是如下:
private void start() throws Exception { final EchoServerHandler serverHandler = new EchoServerHandler(); /** * NioEventLoop并不是一個(gè)純粹的I/O線程,它除了負(fù)責(zé)I/O的讀寫之外 * 創(chuàng)建了兩個(gè)NioEventLoopGroup, * 它們實(shí)際是兩個(gè)獨(dú)立的Reactor線程池。 * 一個(gè)用于接收客戶端的TCP連接, * 另一個(gè)用于處理I/O相關(guān)的讀寫操作,或者執(zhí)行系統(tǒng)Task、定時(shí)任務(wù)Task等。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup childGroup = new NioEventLoopGroup(); try { //ServerBootstrap負(fù)責(zé)初始化netty服務(wù)器,并且開始監(jiān)聽端口的socket請求 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, childGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 為監(jiān)聽客戶端read/write事件的Channel添加用戶自定義的ChannelHandler socketChannel.pipeline().addLast(serverHandler); } }); ChannelFuture f = b.bind().sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully().sync(); childGroup.shutdownGracefully().sync(); } }
從上圖的代碼可以總結(jié)為以下幾個(gè)步驟:
1、創(chuàng)建ServerBootStrap實(shí)例
2、設(shè)置并綁定Reactor線程池:EventLoopGroup,EventLoop就是處理所有注冊到本線程的Selector上面的Channel
3、設(shè)置并綁定服務(wù)端的channel
4、5、創(chuàng)建處理網(wǎng)絡(luò)事件的ChannelPipeline和handler,網(wǎng)絡(luò)時(shí)間以流的形式在其中流轉(zhuǎn),handler完成多數(shù)的功能定制:比如編解碼 SSl安全認(rèn)證
6、綁定并啟動(dòng)監(jiān)聽端口
7、當(dāng)輪訓(xùn)到準(zhǔn)備就緒的channel后,由Reactor線程:NioEventLoop執(zhí)行pipline中的方法,最終調(diào)度并執(zhí)行channelHandler
它就是主要引導(dǎo)啟動(dòng)服務(wù)端,工作包括以下:
1.創(chuàng)建服務(wù)端Channel
2.初始化服務(wù)端Channel
3.將Channel注冊到selector
4.端口綁定
1.創(chuàng)建服務(wù)端Channel流程:
首先從用戶代碼的bind()其實(shí)就是AbstractBootstrap.bind(),然后通過反射工廠將用戶通過b.channel(NioServerSocketChannel.class)傳入的NioServerSocketChannel通過調(diào)用底層的jdk的SelectorProvider創(chuàng)建channel,同時(shí)也接著創(chuàng)建好對(duì)應(yīng)的ChannelPipeline。
詳情可以參考下圖,自己去查看一下源碼:
主要工作如下:
1)設(shè)置的option緩存到NioServerSocketChannelConfig里
2)設(shè)置的attr設(shè)置到channel里
3)保存配置的childOptions,配置的childAttrs 到ServerBootstrapAcceptor里
4)往NioSocketChannel的pipeline中添加一個(gè)ServerBootstrapAcceptor
主要的核心源碼如下:
@Override void init(Channel channel) throws Exception { final Map, Object> options = options0(); synchronized (options) { setChannelOptions(channel, options, logger); } final Map , Object> attrs = attrs0(); synchronized (attrs) { for (Entry , Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey
小結(jié):
總體如上面工作流程所述。
特別地建議:查看ServerBootstrapAcceptor源碼,你可以發(fā)現(xiàn)ServerBootstrapAcceptor在channelRead事件觸發(fā)的時(shí)候(也就有客戶端連接的時(shí)候),把childHandler加到childChannel Pipeline的末尾,設(shè)置childHandler的options和attrs,最后把childHandler注冊進(jìn)childGroup
注冊過程如下圖
小結(jié):
Channel 注冊過程所做的工作就是將 Channel 與對(duì)應(yīng)的 EventLoop 關(guān)聯(lián)。
1).每個(gè) Channel 都會(huì)關(guān)聯(lián)一個(gè)特定的 EventLoop, 并且這個(gè) Channel 中的所有 IO 操作都是在這個(gè) EventLoop 中執(zhí)行的;
2).當(dāng)關(guān)聯(lián)好 Channel 和 EventLoop 后, 會(huì)繼續(xù)調(diào)用底層的 Java NIO SocketChannel 的 register 方法, 將底層的 Java NIO SocketChannel 注冊到指定的 selector 中.
通過這兩步, 就完成了 Netty Channel 的注冊過程.
4.端口綁定端口綁定的源碼流程基本如下圖,詳情可以還是你自己讀一下源碼比較好點(diǎn)。
小結(jié):
其實(shí)netty端口綁定是調(diào)用 jdk的javaChannel().bind(localAddress, config.getBacklog());進(jìn)行綁定,然后TCP鏈路建立成功,Channel激活事件,通過channelPipeline進(jìn)行傳播。
客戶端啟動(dòng)的常規(guī)代碼如下:
private void start() throws Exception { /** * Netty用于接收客戶端請求的線程池職責(zé)如下。 * (1)接收客戶端TCP連接,初始化Channel參數(shù); * (2)將鏈路狀態(tài)變更事件通知給ChannelPipeline */ EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host,port)) .handler(new ChannelInitializer流程:() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new EchoClientHandler()); } }); //綁定端口 ChannelFuture f = b.connect().sync(); f.channel().closeFuture().sync(); } catch (Exception e) { group.shutdownGracefully().sync(); } }
1.用戶線程創(chuàng)建Bootstrap實(shí)例,通過API設(shè)置創(chuàng)建客戶端相關(guān)的參數(shù),異步發(fā)起客戶端連接。
2.創(chuàng)建處理客戶端連接、I/O讀寫的Reactor線程組NioEventLoopGroup,默認(rèn)為CPU內(nèi)核數(shù)的2倍。
3.通過Bootstrap的ChannelFactory和用戶指定的Channel類型創(chuàng)建用于客戶端NioSocketChannel,它的功能類似于JDK NIO類庫提供的SocketChannel
4.創(chuàng)建默認(rèn)的Channel Handler Pipeline,用于調(diào)度和執(zhí)行網(wǎng)路事件。
5.異步發(fā)起TCP連接,判斷連接是否成功。如果成功,則直接將NioSocketChannel注冊到多路復(fù)用器上,監(jiān)聽讀操作位,用于數(shù)據(jù)包讀取和消息發(fā)送,如果沒有立即連接成功,則注冊連接監(jiān)聽為到多路復(fù)用器,等待連接結(jié)果。
6.注冊對(duì)應(yīng)的網(wǎng)絡(luò)監(jiān)聽狀態(tài)為到多路復(fù)用器。
7.由多路復(fù)用器在I/O現(xiàn)場中輪詢個(gè)Channel,處理連接結(jié)果。
8.如果連接成功,設(shè)置Future結(jié)果,發(fā)送連接成功事件,觸發(fā)ChannelPipeline執(zhí)行。
9.由ChannelPipeline調(diào)度執(zhí)行系統(tǒng)和用戶的ChannelHandler,執(zhí)行邏輯。
小結(jié):
客戶端是如何發(fā)起 TCP 連接的?
如下圖:
特別提醒:
在AbstractChannelHandlerContext.connect()#findContextOutbound這步操作是返回的結(jié)果next其實(shí)是頭節(jié)點(diǎn),也就是說在下一步next.invokeConnect()這里的next就是頭節(jié)點(diǎn),所以最終是調(diào)用HeadContext .connect()
本文主要講述netty服務(wù)端和客戶端的簡單工作流程。
具體服務(wù)端與客戶端如何通信,以及內(nèi)存管理等方面的知識(shí)下一次再寫。
如果對(duì) Java、大數(shù)據(jù)感興趣請長按二維碼關(guān)注一波,我會(huì)努力帶給你們價(jià)值。覺得對(duì)你哪怕有一丁點(diǎn)幫助的請幫忙點(diǎn)個(gè)贊或者轉(zhuǎn)發(fā)哦。
關(guān)注公眾號(hào)【愛編碼】,回復(fù)2019有相關(guān)資料哦。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74769.html
摘要:它使用了事件通知以確定在一組非阻塞套接字中有哪些已經(jīng)就緒能夠進(jìn)行相關(guān)的操作。目前,可以把看作是傳入入站或者傳出出站數(shù)據(jù)的載體。出站事件是未來將會(huì)觸發(fā)的某個(gè)動(dòng)作的操作結(jié)果,這些動(dòng)作包括打開或者關(guān)閉到遠(yuǎn)程節(jié)點(diǎn)的連接將數(shù)據(jù)寫到或者沖刷到套接字。 netty的概念 定義 Netty 是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架,支持快速地開發(fā)可維護(hù)的高性能的面向協(xié)議的服務(wù)器和客戶端。我們可以很簡單的...
摘要:是一個(gè)面向字節(jié)流的協(xié)議,它是性質(zhì)是流式的,所以它并沒有分段??苫诜指舴鉀Q。編解碼的主要目的就是為了可以編碼成字節(jié)流用于在網(wǎng)絡(luò)中傳輸持久化存儲(chǔ)。 showImg(https://segmentfault.com/img/remote/1460000015895049); 前言 記得前段時(shí)間我們生產(chǎn)上的一個(gè)網(wǎng)關(guān)出現(xiàn)了故障。 這個(gè)網(wǎng)關(guān)邏輯非常簡單,就是接收客戶端的請求然后解析報(bào)文最后發(fā)送...
摘要:目錄源碼分析之番外篇的前生今世的前生今世之一簡介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端源碼分析之一揭開神秘的紅蓋頭服務(wù)器 目錄 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 簡介 Java NIO 的前生今世 ...
摘要:我想這很好的解釋了中,僅僅一個(gè)都這么復(fù)雜,在單線程或者說串行的程序中,編程往往是很簡單的,說白了就是調(diào)用,調(diào)用,調(diào)用然后返回。 Netty源碼分析(三) 前提概要 這次停更很久了,原因是中途迷茫了一段時(shí)間,不過最近調(diào)整過來了。不過有點(diǎn)要說下,前幾天和業(yè)內(nèi)某個(gè)大佬聊天,收獲很多,所以這篇博文和之前也會(huì)不太一樣,我們會(huì)先從如果是我自己去實(shí)現(xiàn)這個(gè)功能需要怎么做開始,然后去看netty源碼,與...
摘要:一證書本文只介紹版,其他系統(tǒng)只供參考生成證書下載并安裝未編譯編譯好在目錄下打開命令行,輸入在本目錄得到和文件生成服務(wù)端和客戶端私鑰命令行輸入密碼自己設(shè)定,好幾個(gè)密碼,別弄亂了就好,分不清的話都設(shè)成一樣的根據(jù)生成文件 一、證書 (本文只介紹windows版,其他系統(tǒng)只供參考) 1.生成ca證書 下載 openssl 并安裝 未編譯 編譯好 在openssl/bin目錄下打開命令行,輸入...
閱讀 1690·2021-11-23 10:07
閱讀 2682·2019-08-30 11:10
閱讀 2871·2019-08-29 17:08
閱讀 1811·2019-08-29 15:42
閱讀 3212·2019-08-29 12:57
閱讀 2424·2019-08-28 18:06
閱讀 3576·2019-08-27 10:56
閱讀 416·2019-08-26 11:33