1、問題描述java
Netty是最近很是流行的高性能異步通信框架,相對於Java原生的NIO接口,Netty封裝後的異步通信機制要簡單不少。bootstrap
可是小K最近發現並非全部開發人員在使用的過程當中都瞭解其內部實現機制,而是照着葫蘆畫瓢。服務器
網上簡單搜索下,在客戶端使用Netty創建鏈接池的文章也是比較少。今天小K給你們簡單介紹下使用Netty創建鏈接池的方法。框架
首先咱們來看下Netty官方給出的客戶端sample實例:異步
//建立一個EventLoopGroup,能夠簡單認爲是Netty框架下的線程池,默認最大線程數量是處理器數量的2倍 EventLoopGroup group = new NioEventLoopGroup(); try { //Netty創建鏈接的輔助類 Bootstrap b = new Bootstrap(); //配置屬性,向pipeline添加handler b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); } //p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new EchoClientHandler()); } }); //啓動創建鏈接 ChannelFuture f = b.connect(HOST, PORT).sync(); //block直到鏈接被關閉 f.channel().closeFuture().sync();
很簡單?沒錯,確實如此。那麼如今問題來了,若是你如今須要鏈接100個服務器,你會怎麼作呢?ide
下面這樣處理怎麼樣呢?咱們在外層加了一個for循環oop
for(Host host : hosts){ //建立一個EventLoopGroup,能夠簡單認爲是Netty框架下的線程池,默認線程數量是處理器數量的2倍 EventLoopGroup group = new NioEventLoopGroup(1); try { //Netty創建鏈接的輔助類 Bootstrap b = new Bootstrap(); //配置屬性,向pipeline添加handler b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); } //p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new EchoClientHandler()); } }); //啓動創建鏈接 ChannelFuture f = b.connect(HOST, PORT).sync(); //block直到鏈接被關閉 f.channel().closeFuture().sync(); }
問題很明顯,若是每個channel都對應一個NIOEventLoopGroup,那麼咱們實際上構建了一個connection:thread = 1:1的模型,隨着鏈接數不斷地擴大,線程膨脹的問題就會突顯出來。性能
1、問題解決ui
那麼如何避免線程膨脹的問題呢?很簡單,咱們只要稍微修改下上面的代碼就能夠了。spa
NioEventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); try { b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); } //p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new EchoClientHandler()); } }); for(Host HOST : Hosts){ ChannelFuture f = b.connect(HOST, PORT).sync(); }
在上面的代碼中,咱們使用同一個bootstrap建立了多個鏈接,從而使鏈接共享了一個NioEventLoopGroup,避免了線程膨脹的問題。
問題就這樣解決了嗎?NO,還遠遠沒有結束哦,那麼問題又來了。
一、若是但願每一個鏈接可以使用不一樣的Handler怎麼辦?
二、每一個鏈接如何可以再次複用,避免重複建立channel?
爲了可以建立異步操做的鏈接池咱們須要實現以下的模型。
爲了可以方便地創建一個異步操做的鏈接池,咱們會使用到FixedChannelPool(不瞭解的同窗麻煩Google一下吧,(⌒_⌒))
其僞代碼以下(具體的代碼實現結構仍是留給讀者本身思考吧):
Bootstrap b = new Bootstrap().channel(NioSocketChannel.class).group( new NioEventLoopGroup()); //自定義的channelpoolhandler ChannelPoolHandler handler = new ChannelPoolHandler(); //建立一個FixedChannelPool FixedChannelPool pool = new FixedChannelPool(bootstrap, handler); //從channelpool中獲取鏈接或者建立新的鏈接,並添加listener pool.acquire.addlistener();
3、總結:
一般狀況下,咱們並不須要使用Netty創建鏈接池,common pool能夠知足咱們的需求,可是有些業務場景(例如:返回結果時間不肯定)須要使用這種異步的鏈接池,在正確的業務場景下選擇正確的解決方案纔是王道哦。