netty in action讀書筆記

關於netty的內存管理:

netty在4.1.x版本默認使用的allocator是PooledByteBufAllocator,在分配ByteBuf的時候,引入了新的alloc機制——jemalloc,netty內部自己按照jemalloc的機制,實現了一個java版的(這個有待考證,目前我翻了一下源碼,好像是java實現的,沒有調用native的部分)

關於Channel.write和ChannelHandlerContext.write:

ChannelHandlerContextwrite方法,把值寫到channelPipline的後一個channelHandler節點中,而channel.write不同,因爲write是個outbound事件,所以DefaultChannelPipeline直接找到tail部分的context,調用其write()方法是從尾部向前傳遞的。(這個時候,頭指的是朝向外部的一端,尾部指朝向本地的一端)
更詳細的說明:
http://blog.csdn.net/zxhoo/article/details/17264263

關於netty的ByteBuf:

netty大塊來說有兩種buf,一種分配在堆內存中,一種分配在堆外內存(就是系統內存)裏。各有各的好處。
1.分配在堆外,避免了每次調用I/O操作之前或者之後,將緩衝區的內容複製到一箇中間緩衝區,缺點也很明顯——堆外收集和分配比較昂貴,使用需要複製到堆內存中。
2.分配在堆內存中的,byteBuf.hasArray()將會返回true,說明在堆內存中有一個支持數組

關於ChannelHandlerContext:

每當有ChannelHandler被添加到ChannelPipeline中,都會創建一個ChannelHandlerContext,二者之間的綁定關係是永遠不會改變的,所以可以緩存某個ChannelHandlerChannelHandlerContext。但是一個channelHandler可以被安裝到多個ChannelPipeline中,但是需要加@Sharable註解。

關於netty的異常處理:

如果入站處理中出現了異常,異常會像所有入站事件一樣向後傳遞,如果一直傳遞到底都沒有被處理,那麼netty將會warn級別記錄,並且嘗試釋放該異常。

關於netty的EventLoop:

Netty in action這本書中講的是:一個EventLoop由一個線程來支撐,這個我翻了一下源碼,看到的確實是這個樣子。後面接着說:如果提交任務的線程就是支撐EventLoop的線程,那麼任務將直接被執行;如果不是,那麼EventLoop將調度該任務以便稍後執行,並將它放到內部的隊列中。
這個隊列是個LinkedBlockingQueue,我把DefaultEventLoop的execute方法(繼承自SingleThreadEventExecutor)拿出來:

@Override public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } boolean inEventLoop = inEventLoop(); if (inEventLoop) { addTask(task); } else { startThread(); addTask(task); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); } }

再看看inEventLoop方法:

@Override public boolean inEventLoop(Thread thread) { //這裏我省略了一個方法,入參的thread是從Thread.currentThread()方法返回的,即當前線程 return thread == this.thread; }

這麼來看我們可以看到,應該是:是支撐EventLoop的線程,把任務放到隊列中,不是的直接執行。不知道是作者筆誤?
繼續debug了一下netty,我發現,startThread()這個方法的調用是在AbstractChannel初始化的時候就執行了的,這個時候整個線程以及跑起來了,所以這個時候只要添加任何一個任務,就會直接拿出來執行,不知道作者的意思是不是這樣的。但是其實還有一個問題,如果這個時候taskQueue中不是空的,那麼就算調用任務的線程是支撐EventLoop的線程,這個任務也不會馬上執行(因爲這個任務不管怎麼樣都是在taskQueue中),只能保證比其他別的線程調用的任務稍微快一點點執行(其他線程的任務要先調用startThread這個方法)。

關於Netty的傳輸類型和EventLoopGroup:

我們看一下這種代碼:
EventLoopGroup e = new NioEventLoopGroup(); BootStrap b = new BootStrap(); b.channel(OioSocketChannel.class) ...
以上這段代碼將會報錯,因爲我們選用不兼容的傳輸。BootStrap.handler方法尤其重要,因爲它要配置好ChannelPipeline

Netty的優雅關閉:

EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group)... Future<?> f = group.shutdownGracefully(); f.syncUninterruptibly()

關於netty的解碼器的一些細節:

對於編碼器和解碼器來說,一旦消息被編碼或者解碼,它就會被ReferenceCountUtil.release(message)調用自動釋放。如果不想消息被釋放,可以調用ReferenceCountUtil.retain(message)增加引用計數。

關於netty中的WebSocket:

WebSocketServerHandshaker有好多種實現,比如WebSocketServerHandshaker13,就是13版的WebSocket實現。WebSocketServerProtocolHandler按照WebSocket規範的要求,處理WebSocket升級握手、PingWebSocketFramePongWebSocketFrameCloseWebSocketFrame
WebSocket升級之前的ChannelPipeline中的狀態:(這裏只是根據我們的例子中的handler)

HtthRequestDecoder -> HttpResponseEncoder -> HttpObjectAggregator
-> HttpRequestHandler -> WebSocketServerProtocolHandler
-> TextWebSocketFrameHandler

HttpObjectAggregator:將一個HttpMessage和跟隨它的多個HttpContent聚合爲單個FullHttpRequest或者FullHttpResponse(取決於它是被用來處理請求還是響應)。安裝這個之後,ChannelPipeline中的下一個ChannelHandler將只會收到完整的HTTP請求或響應。

WebSocket協議升級完成之後,WebSocketServerProtocolHandler將會把HttpRequestDecoder替換爲WebSocketFrameDecoder,把HTTPResponseEncoder替換爲WebSocketFrameEncoder。爲了性能最大化,它將移除任何不再被WebSocket連接所需要的ChannelHandler。也包含了HttpObjectAggregatorHttpRequestHandler。Netty根據客戶端支持的版本,自動選擇WebSocketFrameEncoder和Decoder,下面是升級之後的Pipeline:
WebSocketFrameDecoder13 -> WebSocketFrameEncoder13
-> WebSocketServerProtocolHandler -> TextWebSocketFrameHandler
(假設我們選用13版的WebSocket協議)

關於WebSocket的握手:

我們上文說道WebSocketServerProtocolHandler會做很多替換Handler和移除Handler的方法,WebSocketServerProtocolHandler重寫了handlerAdded方法,會在管道中添加一個WebSocketServerProtocolHandshakeHandler,這個方法在channelRead的時候會執行握手handshaker.handshake,這個handshaker是WebSocketServerHandshaker的實體,我們可以看到這個握手過程中,會進行handler的替換、移除。

關於WebSocket的Idle狀態:

觸發IdleStateEvent的是IdleStateHandler,這個Handler的channelActivechannelRegistriedhandlerAdded的時候schedule一個Task,不停地來觸發IdleStateEvent

關於Netty的bind過程:

ServerBootStrap爲例(簡稱sbs),new一個sbs的時候,什麼也不會做,但是在調用sbs的channel方法的時候,會初始化ChannelFactory,默認用的是ReflectiveChannelFactory。bind的時候會初始化一個channel,這個channel是從ChannelFactory中創建出來的,然後在initAndRegister中調用:
ChannelFuture regFuture = config().group().register(channel);這裏這個group方法返回的就是group方法傳入的EventLoopGroup。這就會在EventLoopGroup中註冊一個Channel。我們以NioEventLoopGroup爲例,調用了父類MultithreadEventLoopGroup的register方法,這裏會先使用EventExecutorChooser來選擇一個EventLoop,然後調用EventLoop的Channel註冊到一個EventLoop鍾,這就是我們之前說的,一個EventLoop會綁定很多Channel,但是一個Channel只會綁在一個EventLoop上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章