netty 自定义客户端连接池和channelpool

目录标题

客户端池化

通信完成之后,一般要关闭channel,释放内存。但是与一个服务器频繁的打开关闭浪费资源。

通过连接池,客户端和服务端之间可以创建多个 TCP 连接,提升消息的收发能力,同时利用池化技术,可以重用连接,防止反复申请和释放连接,提高连接的使用率。

下例就是启动时建立100个连接,然后去给服务端发消息。

java 复制代码
    public static void main(String[] args) throws Exception {
        List<Channel> CLIENT_LIST = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
            Channel channel = channelFuture.channel();
            CLIENT_LIST.add(channel);
            channel.closeFuture().addListener(future -> {
                eventExecutors.shutdownGracefully();
            });
        }
        for (Channel channel : CLIENT_LIST) {
            ByteBuf buffer = Unpooled.buffer().alloc().buffer(1024);
            // 模拟业务操作
            // 数据发送
            for (int j = 0; j < 1024; j++) {
                buffer.writeByte((byte) j);
            }
            channel.writeAndFlush(buffer);
        }
    }

运行

visualVM执行情况如下

分析

客户端的eventloopgroup负责建立连接以及后续的IO请求,默认线程数是(CPU * 2)。

代码中每建立一个客户端连接,就创建一个group,每个group又有若干线程。在这个例子中,即使每个group只使用了一个线程,但初始化时是children = new EventExecutor[nThreads];又白白浪费了很多内存。

问题修复

  1. 只需要循环connect即可。让所有的TCP连接共享同一个group。
    这样里面的eventloop也是共享的,所以发生异常时不能关闭enventloop或者group,否则会影响其他客户端。不推荐。
java 复制代码
      for (int i = 0; i < 100; i++) {
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
            Channel channel = channelFuture.channel();
            CLIENT_LIST.add(channel);
            channel.closeFuture().addListener(future -> {
                // eventExecutors.shutdownGracefully();此处不能关闭
            });
        }
  1. netty自带的channelpool
    他有两个实现,一个无界,一个有界。
    实际使用中,是一个map,key是目标端的地址和端口,value就是池子。这里不涉及多个服务器,所以用channelpool即可。客户端的group
java 复制代码
  NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventExecutors)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, false)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                    }
                }).remoteAddress("127.0.0.1", 6666);

        ChannelPool channelPool = new FixedChannelPool(bootstrap, new ChannelPoolHandler() {
            @Override
            public void channelReleased(Channel ch) throws Exception {
                System.out.println("pool release");
            }

            @Override
            public void channelAcquired(Channel ch) throws Exception {
                System.out.println("pool: occur acquire operation");
            }

            @Override
            public void channelCreated(Channel ch) throws Exception {
                System.out.println("pool channel create");
                ch.closeFuture();
            }
        }, 50);
        for (int i = 0; i < 100; i++) {
            Future<Channel> acquire = channelPool.acquire();
            acquire.addListener(future -> {
                    ByteBuf buffer = Unpooled.buffer().alloc().buffer(100);
                    // 模拟业务操作
                    // 数据发送
                    buffer.writeBytes("hello hello hello hello hello hello hello hello".getBytes());
                    ((Channel) future.get()).writeAndFlush(buffer).sync();
                    // buffer不需要释放,传递到headcontext自动释放
                    channelPool.release(((Channel) future.get())); // 把该channel还给池子
            });
        }
    }

参考:netty进阶-李林峰

本文作者:WKP9418

原文地址:https://blog.csdn.net/qq_43179428/article/details/140562757

相关推荐
hellojackjiang20113 天前
IM聊天不掉线?生产级WebSocket断线重连、心跳保活实战
websocket·网络协议·网络编程·html5·即时通讯·im
爱写代码的倒霉蛋5 天前
我用book-to-skill把尹圣雨的《TCP/IP网络编程》做成了知识库(适合快速入门网络编程)
网络·网络编程·知识库
TAKETODAY5 天前
Netty 是怎么把 HTTP 请求交给 MVC 的?一个请求的完整链路
java·netty
洋不写bug11 天前
初识网络通信,网络发展背景,TCP/IP五层模型简介
计算机网络·网络编程·协议·ip·端口号·tcp/ip模型·网络发展背景
j7~1 个月前
【Linux】三十八.C++ 手写 HTTP 服务器:裸 socket + fork 多进程,从 HTTP 报文解析到浏览器打开网页
linux·网络·网络协议·学习·http·网络编程
贾伟康1 个月前
【HarmonyOS 7新能力|040】QUIC长连接工程封装:把接入逻辑放进可维护的分层结构
网络编程·harmonyos·arkts·quic·软件架构
j7~1 个月前
【Linux网络加餐课】(篇八)网络版计算器(终篇):守护进程、标准 IO 重定向与部署打包
linux·运维·网络编程·tcp·守护进程·io重定向·部署打包
j7~1 个月前
【Linux网络加餐】(篇七)网络版计算器(中):协议落地、报文分隔与完整链路
linux·c++·网络编程·tcp·报文分割·网络计算器·协议落地
j7~1 个月前
【Linux网络】三十七.TCP 协议通讯流程
linux·网络编程·三次握手·四次挥手·tcp 协议通讯流程