1. 程式人生 > >netty 可能出現記憶體上升的問題

netty 可能出現記憶體上升的問題

原文地址:http://www.blogjava.net/usherlight/archive/2014/11/27/420676.html

 

Netty作為一個非同步非阻塞式的框架,是不允許在ChannelHandler中長時間處理事務(比如資料庫的操作),阻塞I/O的讀寫處理的。


在Netty in Action中是這樣描述的:

While the I/O thread must not be blocked at all, thus prohibiting any direct blocking operations within your ChannelHandler, there is a way to implement this requirement. 

You can specify an EventExecutorGroup when adding ChannelHandlers to the ChannelPipeline. 

This EventExecutorGroup will then be used to obtain an EventExecutor, which will execute all the methods of the ChannelHandler. 

This EventExecutor will use a different thread from the I/O thread, thus freeing up the EventLoop.

I/O執行緒是不允許被阻塞的,也就是不能在ChannelHandler中進行任何阻塞式的處理,但是對此我們也有相應的解決方法.

就是在把ChannelHanders新增到ChannelPipeline的時候,指定一個EventExecutorGroup,ChannelHandler中所有的方法都將會在這個指定的EventExecutorGroup中執行。

而這個EVentExecutorGroup執行的執行緒與I/O執行緒不同,達到不阻塞I/O的目的。 

程式示例如下:

Channel ch = ...;

ChannelPipeline p = ch.pipeline();

EventExecutor e1 = new DefaultEventExecutorGroup(16);

EventExecutor e2 = new DefaultEventExecutorGroup(8);



p.addLast(new MyProtocolCodec());

p.addLast(e1, new MyDatabaseAccessingHandler());

p.addLast(e2, new MyHardDiskAccessingHandler());


需要補充說明一下,上面的示例程式似乎有點問題。使用上述方法新增ChannelHandler到pipeline中以後,channelHandler的所有方法確實什麼在一個單獨的執行緒中被處理。
但是,每次DefaultEventExcutorGroup執行緒池中的執行緒不能被重用,每次都會生成一個新的執行緒,然後在新的執行緒中呼叫ChannelHandler, 在visualvm可以看到執行緒數量直線增長。

解決的方法是:不能使用區域性變數形式的DefaultEventExecutorGroup。而使用類靜態成員變數:

 

static final EventExecutor e1 = new DefaultEventExecutorGroup(16);


 

我分析原因可能是:在新的連線到來,建立ChannelPipeline給新Channel的時候,如果不使用靜態的共享變數,而使用區域性變數的話,就造成DefaultEventExecutorGroup被多次重複建立。因此,雖然一個DefaultEventExecutorGroup中的Thread數量是固定的,但是卻產生了多餘的DefaultEventExecutorGroup。從VisualVM中也可以看到,DefaultEventExecutorGroup執行緒的名字會是:

xxx-2-1

xxx-3-1

xxx-4-1

xxx-n-1

說明是Group的數量(第一個數字)在增多,而不是Group中的執行緒數量(第二個數字)在增多

改成靜態變數後,執行緒名會是:

xxx-2-1

xxx-2-2

xxx-2-3

xxx-2-n

最後一個n就是在建立DefaultEventExecutorGroup時候,傳入的執行緒個數引數的大小。