將Netty強大的NIO當做BIO使用
設置啓動的poolSize爲150 先創建150個線程EventLoopGroup,這裏我設置JVM的參數爲
//由於用的是jdk8,所以好多數據會在元空間存放
-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=60m -Xmx50m
//具體代碼如下
public class InitNettyPool {
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
initClientPool(Integer.valueOf(args[0]));
}
static void initClientPool(int poolSize) throws InterruptedException {
for (int i = 0; i < poolSize; i++) {
EventLoopGroup group = new NioEventLoopGroup();
//創建不要使用多線程創建。
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler());
}
});
ChannelFuture sync = bootstrap.connect("61.135.169.125", 80).sync();
sync.channel().closeFuture().addListener((r) -> {
System.out.println("執行shutdown");
group.shutdownGracefully();
});
}
}
}
運行後的結果看到創建了200個nioEventLoopGroup-xx-1的線程,並且堆的大小我們最大設置的50,老年代幾乎已經滿了。可以看到創建大量的EventLoopGroup線程池,由於每個線程本身需要佔用一定的內存,所以連接數比較多的時候會佔有大量的內存。
將上邊的運行代碼傳入參數調整一下創建的數量。設置啓動的poolSize 調整爲200。發現啓動後報堆空間不夠了。
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
GC次數一直在增加,但是並不起左右,這裏說明內存泄漏了。看到服務還是正常運行的。這個時候除非暴力的重啓沒有別的辦法了。想想下如果生產環境也創建過多的線程是不是也只有重啓才能解決。上邊的代碼在創建時候調用的數和創建的數都是一樣的。典型的BIO。
錯誤的使用NIO通信框架,不僅沒有達到優化的效果,而且還發生了OOM異常。
正確使用Netty的NIO
//設置啓動的poolSize爲1000
public class InitNettyPool {
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
initClientPool(Integer.valueOf(args[0]));
}
static void initClientPool(int poolSize) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
//創建不要使用多線程創建。多線程不安全
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler());
}
});
for (int i = 0; i < poolSize; i++) {
//線程安全
ChannelFuture sync = bootstrap.connect("61.135.169.125", 80).sync();
sync.channel().closeFuture().addListener((r) -> {
System.out.println("執行shutdown");
group.shutdownGracefully();
});
}
}
}
調整之後。發現創建1000個連接都沒有發生OOM,具體的系統線程資源佔用,爲CPU內核數的2倍,與TCP的連接數沒有關係,搞定了線程爆發增長的問題。
作爲高性能的通信框架,Netty解決了傳統的BIO模型遇到的問題,在NIO線程中聚合了多路複用器Selector,Selector會不斷地輪訓註冊在其上的Channel。如果某個Channel上邊有新的TCP連接接入,讀和寫事件,這個Channel就處於就像狀態,會被Selector輪詢出來。然後通過key獲得就緒Channel的集合,一個多路複用器上可以輪詢多個Channel,意味着只需要一個線程做輪訓的操作,就可以接入成千上萬的客戶端。
注意事項
- 這裏注意不要多線程的操作同一個Bootstrap實例,Bootstrap是I/O操作工具類,真正的操作在EventLoop線程負責,所以通常多線程操作同一個Bootstrap實例沒有什麼意義。
- 使用Netty注意不要處理和線程組不能同時創建,創建線程組後再創建操作的連接。
創建機制原理分析
由於這裏沒有太明白,代碼跟丟了,所以以後補充吧。
看看Channel的接構圖
具體看地址
https://www.jianshu.com/p/d7f62e3e4abc