netty使用实践

github地址,欢迎star

Server

public void server(int nettyPort) {

    /**
     * boss事件循环组, 接收客户端的连接请求
     */
    EventLoopGroup boss = new NioEventLoopGroup();

    /**
     * worker事件循环组,处理已经成功建立的连接
     */
    EventLoopGroup worker = new NioEventLoopGroup();

    /**
     * 业务线程池
     */
    EventExecutorGroup executors = new DefaultEventExecutorGroup(16);

    ServerBootstrap bootstrap = new ServerBootstrap()
        // 绑定 boss、worker组
        .group(boss, worker)
        .channel(NioServerSocketChannel.class)
        //长连接
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        .childHandler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            // channel管道,在管道中添加各种handler
            ChannelPipeline pipeline = ch.pipeline();
            // 添加编码、解码器
            pipeline.addLast(new StringDecoder(), new StringEncoder());
            // 添加业务handler,并将其绑定到业务线程池
            pipeline.addLast(executors, new SimpleChannelInboundHandler() {
              @Override
              protected void channelRead0(ChannelHandlerContext ctx, Object msg)
                  throws Exception {
                System.out.println(" client say : " + msg);
                ctx.writeAndFlush("hello, this is server !!!");
              }

              @Override
              public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
                System.out.println(" An exception happen");
                cause.printStackTrace();
                ctx.close();
              }
            });
          }
        });

    ChannelFuture future = null;
    try {
      // 绑定端口
      future = bootstrap.bind(nettyPort).sync();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    if (future.isSuccess()) {
      System.out.println("Server 启动成功 ");
    }
  }

Client

public void client(String ip, int nettyPort) {
    EventLoopGroup group = new NioEventLoopGroup();
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(group)
        .channel(NioSocketChannel.class)
        .handler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            // channel管道,在管道中添加各种handler
            ChannelPipeline pipeline = ch.pipeline();
            // 添加编码、解码器
            pipeline.addLast(new StringDecoder(), new StringEncoder());
            pipeline.addLast(new SimpleChannelInboundHandler() {
              @Override
              protected void channelRead0(ChannelHandlerContext ctx, Object msg)
                  throws Exception {
                System.out.println(" Server say:  " + msg);
              }

              @Override
              public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
                System.out.println(" An exception happen");
                cause.printStackTrace();
                ctx.close();
              }
            });
          }
        });

    try {
      ChannelFuture future = bootstrap.connect(ip, nettyPort).sync();
      new Thread(new Runnable() {
        @Override
        public void run() {
          Channel channel = (SocketChannel) future.channel();
          ByteBuf buffer = Unpooled.copiedBuffer("hello, this is client !!!".getBytes());
          channel.writeAndFlush(buffer);
        }
      }).start();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

服务端性能优化

耗时业务另起线程池 DefaultEventExecutorGroup

netty中的channel所有事件始终绑定到woker组中的一个单线程的EventLoop中, 且多个chanel可共享同一个EventLoop,因此该线程不可执行耗时或导致线程阻塞的代码,只适合做编解码相关的简单业务,查询数据库等耗时业务需使用其它线程池异步处理

使用 directBuffer 及缓冲池

ByteBuf dataByteBuf = PooledByteBufAllocator.DEFAULT.buffer();

使用 directBuffer 可减少数据拷贝次数(无需从用户空间拷贝到内核空间)提升性能,进行池化后,性能更佳。需注意的是 directBuffer对象 不在JVM 堆中,GC 无法管理,需手动 release ByteBuf 对象

使用派生对象

ByteBuf.slice();
ByteBuf.readSlice();

派生对象与源共享一份数据,避免内存复制,创建成本低,可灵活使用以满足业务逻辑。

使用 ctx 发送数据

使用 ctx.writeAndFlush(), 会将数据传递给channel管道中的下一个 handler, 如果使用 channel.writeAndFlush(),数据会从管道尾部重新开始,流经所有 handler。

采坑

字节顺序

字节顺序有大端、小端之分,顺序刚好相反。

在与客户端对接时,需要提前进行约定并保持一致

netty 提供了如下接口,方便读、写

// read
byteBuf.readInt();
byteBuf.readIntLE();

// write
byteBuf.writeInt();
byteBuf.writeIntLE();

// 同理: 有 Long/Double/Float/Short 等

内存溢出

中间业务创建的 ByteBuf, 需手动进行release。

ByteBuf.release();
ReferenceCountUtil.release();

慎用 ByteBuf.readBytes, 因为它会创建一个新的ByteBuf, 需手动进行release,可使用 readSlice 代替。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值