netty使用

netty介绍

网络通讯框架 mina、netty
应用案例:阿里RPC框架:Dubbo
netty官网:https://netty.io/

netty特点:

1、是高性能、异步事件驱动模型,他提供对TCP、UDP、HTTP和文件传输的支持
2、使用更加高效的socket底层,对epoll空轮训引起CPU飙升问题在netty内部进行处理,避免的直接使用NIO的陷阱
3、采用了多种编码、解码的支持、对TCP的粘包、分包做了自动化处理

netty组件

Bootstrap:
netty的启动辅助类,是客户端和服务端的入口,Bootstrap是建客户端连接的启动器,ServerBootStrap是监听服务端端口的启动器。
Channel:
常用的是NioServerSocketChannel和NioSocketChannel
NioServerSocketChannel负责监听一个tcp的端口,有连接时会获取一个NioSocketChannel的连接实例
NioSocketChannel负责读写事件,连接服务端
EventLoop:
核心组件之一
通过EventLoopGroup(是在启动辅助类中设置)生成EventLoop,内部是一个无限循环,维护了一个selector。处理所有的注册到selector上的IO操作,在这里就维护了一个线程连接多个连接的工作
ChannelPipeline:
核心组件之一:channelHandler的容器,netty中的IO操作的通道channel,与channelHandler组成责任链,将所有的读时间、写事件、连接事件依次通过channelPipeline,处理事件
ChannelHandler
是IO事件处理的真正单元、可以自定义的channelhandler的定义来处理自己的逻辑,完全控制处理方式
channelhandler和ChannelPipeline组成的责任链
channelHandler分为inbound和outbound,对应的IO的read和write的执行链
执行链底层实现是将channelHandler组装成双向链表,提供head和tail属性
read操作的话从channelPipeline的head到tail的过程
在这里插入图片描述

netty编程

多客户端和服务端通信:echo命令
引入netty依赖

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.10.Final</version>
        </dependency>

服务端

public class NettyServer {
    public static void main(String[] args) {
        NioEventLoopGroup boss = null;
        NioEventLoopGroup worker = null;
    try{
        //需要eventLoopggrop
        /**
         * NioEventLoopGroup是用来处理IO操作以及接收客户端连接的事件循环组
         * 给定两个事件循环组
         * 一个称之为boss,用来接收连接
         * 一个称之为worker,用来处理已经接受的连接
         */
      boss = new NioEventLoopGroup(1);
      worker = new NioEventLoopGroup();//默认是CPU个数乘以2
        /**
         * 启动辅助类:ServerBootSttap
         * 将配置信息配置在辅助类上,用来启动或者设置相关属性:TCP
         * */
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                //指定事件循环组
                .group(boss,worker)
                //主事件循环组接收的通道的实例类型
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //存放channelHandler的容器
                        ChannelPipeline pipeline = ch.pipeline();
                        //字符串解码器
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        //自定义channelHandler
                        pipeline.addLast(new ServerHandler());

                    }
                });
                //启动服务端,通过同步阻塞方式来启动服务端,即调用sync会阻塞直至服务端绑定端口后才返回
        ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
        System.out.println("服务器启动了");

                //使用同步方法来管理程序
                channelFuture.channel().closeFuture().sync();
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        //关闭线程资源
        if(boss!=null){
            boss.shutdownGracefully();
        }
        if(worker!=null){
            worker.shutdownGracefully();
        }
    }

    }
}
/**
 * 读取数据的提供了接口:ChannelInboundHandler
 */
class ServerHandler extends SimpleChannelInboundHandler<String> {
    //接收数据的发方法
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        System.out.println("channelRead0"+msg);
    }
    //连接成功触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"连接服务端成功");
    }
    //断开连接触发给方法
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+" 断开和服务端连接");
    }
    //接收数据

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+":"+msg);

            ctx.channel().writeAndFlush("echo:" + msg);
        }

}

客户端

public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        //创建事件循环组
        NioEventLoopGroup loopGroup = new NioEventLoopGroup();

        //创建启动辅助类
        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .group(loopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //获取pipeline的实例
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        //自定义channelHandler  接收服务端返回的消息
                        pipeline.addLast(new ClientHandler());
                    }
                });

        //客户端连接服务端 通过同步方式来保证连接成功
        Channel channel = bootstrap.connect("127.0.0.1", 9999).sync().channel();

        //给服务端通信发送消息
        Scanner scanner = new Scanner(System.in);
        scanner.useDelimiter("\n");

        while (true) {
            String msg = scanner.nextLine();

            if ("exit".equals(msg)) break;
            //发送消息给服务端
            channel.writeAndFlush(msg);
        }

        channel.closeFuture().sync();
    }
}
class ClientHandler extends SimpleChannelInboundHandler<String>{
    //接收消息
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String o) throws Exception {

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(msg);
    }
}

Selector Bug问题

epoll特定情况下会发生空轮训,CPU使用100%?
NioEventLoop类下的run()方法
解决方案:对Selector的select操作周期进行统计:selectCnt,每完成一次空的select对selectCnt加1,变量selectCnt会随着连续的空轮训会逐渐变大,到达了阈值(默认是512),则执行rebuildSelector()进行重新构建
会新建一个selector实例,将旧的selector实例上所有的注册的有效事件全部重新注册到新的selector实例上,会将旧的selector调用close方法关闭掉

Bootstrap介绍

serverBootstrap
                    //指定事件循环组
                    .group(boss, worker)
                    //主事件循环组接收的通道的实例类型
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_SNDBUF, 1024) //发送缓冲区
                    .childOption(ChannelOption.AUTO_READ, true)
                    .attr(AttributeKey.valueOf("key"),"1023") //自定义的参数设置
                    .childAttr(AttributeKey.valueOf("key1"),"1024") //附带数据
                    .childHandler(new ChannelInitializer <NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            //存放channelHandler的容器
                            ChannelPipeline pipeline = ch.pipeline();
                            //字符串解码器
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            //自定义channelHandler
                            pipeline.addLast(new Server212Handler());

                        }
                    })
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值