概述 最近在學習netty的相關知識,也在看netty的源碼,光看不練假把式,所以也正好利用自己學習的機會寫幾篇netty的分析文章,主要還是一些源碼解析的文章,一方面有輸出會促使自己在看源碼,學習原理的過程中更系統,更深入,同時也能加強記憶,鞏固對知識的理解。 關於netty的簡介和應用我就不做介 ...
概述
最近在學習netty的相關知識,也在看netty的源碼,光看不練假把式,所以也正好利用自己學習的機會寫幾篇netty的分析文章,主要還是一些源碼解析的文章,一方面有輸出會促使自己在看源碼,學習原理的過程中更系統,更深入,同時也能加強記憶,鞏固對知識的理解。
關於netty的簡介和應用我就不做介紹了,在網路上都能搜索到相關的資料。
netty是一個性能非常優秀的網路框架,採用reactor模式,使用非常高效的基於事件驅動的io線程模型,即經典的多路復用的io模式。關於io線程模型其實又是一個很大的話題,涉及到操作系統的底層原理,後面有時間我也打算深入學習一下這方面的知識,並寫一些相關的分析文章。
我的整體思路是這樣的:首先,以netty提供給用戶的常用的介面為切入點,一步步地深入netty內核進行分析,因為netty的模塊眾多,而且某一個層級往往有很多種平級的模塊,這些不同的模塊一般代表了不同的實現機制或不同的功能,如根據多路復用系統調用的不同就分為EpollEventLoop,NioEventloop,KQueueEventLoop等;根據不同的應用層協議把編解碼器分為不同種類,如http,smtp, http2, xml等等。所以,我們只需要分析其中的一套,其他類型的在實際用到時在深入瞭解,這樣既能整體上掌握框架,也能深入實現的細節。
netty應用示例
之前對spark-core的源碼進行過一些講解,分析了spark-core中的大部分模塊,但是其中有一個很重要的模塊卻沒有分析,那就是spark的rpc模塊,spark的rpc模塊是基於netty實現的,也是對netty的一個很典型的應用,所以這裡我還是以spark中的rpc模塊為示例,切入netty的源碼分析。
1 // 初始化netty服務端 2 private void init(String hostToBind, int portToBind) { 3 4 // io模式,有兩種選項NIO, EPOLL 5 IOMode ioMode = IOMode.valueOf(conf.ioMode()); 6 // 創建bossGroup和workerGroup,即主線程組合子線程組 7 EventLoopGroup bossGroup = 8 NettyUtils.createEventLoop(ioMode, conf.serverThreads(), conf.getModuleName() + "-server"); 9 EventLoopGroup workerGroup = bossGroup; 10 11 // 緩衝分配器,分為堆記憶體和直接記憶體 12 PooledByteBufAllocator allocator = NettyUtils.createPooledByteBufAllocator( 13 conf.preferDirectBufs(), true /* allowCache */, conf.serverThreads()); 14 15 // 創建一個netty服務端引導對象,並設置相關參數 16 bootstrap = new ServerBootstrap() 17 .group(bossGroup, workerGroup) 18 .channel(NettyUtils.getServerChannelClass(ioMode)) 19 .option(ChannelOption.ALLOCATOR, allocator) 20 .childOption(ChannelOption.ALLOCATOR, allocator); 21 22 // 記憶體使用的度量對象 23 this.metrics = new NettyMemoryMetrics( 24 allocator, conf.getModuleName() + "-server", conf); 25 26 // 排隊的連接數 27 if (conf.backLog() > 0) { 28 bootstrap.option(ChannelOption.SO_BACKLOG, conf.backLog()); 29 } 30 31 // socket接收緩衝區大小 32 if (conf.receiveBuf() > 0) { 33 bootstrap.childOption(ChannelOption.SO_RCVBUF, conf.receiveBuf()); 34 } 35 36 // socket發送緩衝區大小 37 if (conf.sendBuf() > 0) { 38 bootstrap.childOption(ChannelOption.SO_SNDBUF, conf.sendBuf()); 39 } 40 41 // 子channel處理器 42 bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { 43 @Override 44 protected void initChannel(SocketChannel ch) { 45 RpcHandler rpcHandler = appRpcHandler; 46 for (TransportServerBootstrap bootstrap : bootstraps) { 47 rpcHandler = bootstrap.doBootstrap(ch, rpcHandler); 48 } 49 context.initializePipeline(ch, rpcHandler); 50 } 51 }); 52 53 InetSocketAddress address = hostToBind == null ? 54 new InetSocketAddress(portToBind): new InetSocketAddress(hostToBind, portToBind); 55 // 綁定到ip地址和埠 56 channelFuture = bootstrap.bind(address); 57 // 同步等待綁定成功 58 channelFuture.syncUninterruptibly(); 59 60 port = ((InetSocketAddress) channelFuture.channel().localAddress()).getPort(); 61 logger.debug("Shuffle server started on port: {}", port); 62 }
netty使用一個引導對象ServerBootstrap來引導服務端的啟動,最後的bootstrap.bind(address)實際出發了一系列的初始化機制。
總結
本節,我主要是開了一個頭,以spark中rpc服務端的初始化為例子切入netty的源碼。