Netty从入门到精通(一)

Netty是什么?

Netty是一个高性能的、异步的、基于事件驱动的网络应用型框架。

本质:网络应用程序框架

实现:异步、事件驱动

特性:高性能、可维护、快速开发

用途:开发服务器和客户端

Netty的架构

Core: 

  1. 可扩展的事件模型。

  2. 统一的通信api(无论是http还是socket都使用统一的api)。

  3. 零拷贝机制与字节缓冲区。

Transport Services

  1. 支持socket和datagram(数据报)。

  2. 支持http协议。

  3. In-VM Pipe(管道协议)。

Protocol Support

  1. http 以及 websocket。     
  2. SSL 安全套接字协议⽀持。      
  3. Google Protobuf (序列化框架)。       
  4. ⽀持zlib、gzip压缩。    
  5. ⽀持⼤⽂件的传输。    
  6. RTSP(实时流传输协议,是TCP/IP协议体系中的⼀个应⽤层协议)。    
  7. ⽀持⼆进制协议并且提供了完整的单元测试。
     

为什么使用Netty

  1. netty更友好,更强大。JDK中NIO的一些API功能薄 弱且复杂,Netty隔离了JDK中 NIO的实现变化及实现细节 譬如:ByteBuffer -> ByteBuf 主要负责从底层的IO中读取数据ByteBuf,然后传递给应用 程序,应用程序处理完之后封 装为ByteBuf,写回给IO
  2. Netty自身线程安全。使用JDK原生API需要对多线程要很熟悉 因为 NIO涉及到的Reactor设计模式,得对里面的 理要相当的熟悉
  3. 完整的高可用机制。JDK原生方式要实现高可用,需 要自己实现断路重连、半包读 写、粘包处理、失败缓存处理 等相关操作,而Netty则做的更 多,它解决了传输的一些问题 譬如粘包半包现象,它支持常 用的应用层协议,完善的断路 重连,idle等异常处理
  4. 规避了java自带NIO中的BUG

其他网络通信框架:

ØApache Mina:和Netty是同一作者,但是推荐Netty,作者认为Netty是针对Mina的重新打造版本,解 决了一些问题并提高了扩展性

ØSun Grizzly:用得少、文档少,更新少。

ØApple Swift NIO、ACE 等:其他语言不作考虑

ØCindy :生命周期不长

ØTomcat、Jetty:还没有独立出来,另外他们有自己的网络通信层实现,是为了专门针对servelet容器而 做的,不具备通用性。 那tomcat在网络通信层为什么不选择Netty呢?主要是由于tomcat出现的比较早

Netty的应用

数据库: Cassandra

大数据处理: Spark、Hadoop

Message Queue:RocketMQ

检索: Elasticsearch

框架:gRPC、Apache Dubbo、Spring5(响应式编程WebFlux)

分布式协调器:ZooKeeper

工具类: async-http-client .......

Netty对三种IO的支持(主要是NIO)

点击了解AIO\NIO\BIOhttp://t.csdn.cn/Dfrce

 Netty中Reactor的实现

点击了解Reactor模型http://t.csdn.cn/HZ21h

Netty线程模型是基于Reactor模型实现的,对Reactor三种模式都有非常好的支持,并做了一定的改进,也非常的灵活,一般情况,在服务端会采用主从架构模型。

工作流程:

Ø1)Netty 抽象出两组线程池:BossGroup 和 WorkerGroup,每个线程池中都有EventLoop 线程(可以是OIO,NIO,AIO)。BossGroup 中的线程专门负责和客户端建立连接,WorkerGroup 中的线程专门负责处理连接上的读写, EventLoopGroup 相当于一个事件循环组, 这个组中含有多个事件循环

Ø2)EventLoop 表示一个不断循环的执行事件处理的线程,每个EventLoop 都包含一个Selector,用于监听注册在其上的 Socket 网络 连接(Channel),当监听到时候会操作任务放到TaskQueue。

Ø3)每个 Boss EventLoop 中循环执行以下三个步骤:

  1. select:轮训遍历注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)
  2. processSelectedKeys:处理 accept 事件,与客户端建立连接,生成一个SocketChannel,并将其注册到某个 Worker EventLoop 上的 Selector 上
  3. runAllTasks:再去以此循环处理任务队列中的其他任务

Ø4)每个 Worker EventLoop 中循环执行以下三个步骤:

  1. select:轮训遍历注册在其上的SocketChannel 的read/write 事件(OP_READ/OP_WRITE事件)
  2. processSelectedKeys:在对应的SocketChannel 上处理 read/write 事件
  3. runAllTasks:再去以此循环处理任务队列中的其他任务

 Ø5)在以上两个processSelectedKeys步骤中,会使用 Pipeline(管道),Pipeline 中引用了 Channel,即通过 Pipeline 可以获取到对 应的 Channel,Pipeline 中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)

Netty线程模型总结

Ø1)Netty 的线程模型基于主从多Reactor模型。通常由一个线程负责处理OP_ACCEPT事件,拥有 CPU核数的两倍的IO线程处理读写事 件

Ø2)一个通道的IO操作会绑定在一个IO线程中,而一个IO线程可以注册多个通道

Ø3)在一个网络通信中通常会包含网络数据读写,编码、解码、业务处理。默认情况下网络数据读写,编码、解码等操作会在IO线程中 运行,但也可以指定其他线程池。

Ø4)通常业务处理会单独开启业务线程池(看业务类型),但也可以进一步细化,例如心跳包可以直接在IO线程中处理,而需要再转发 给业务线程池,避免线程切换

Ø5)在一个IO线程中所有通道的事件是串行处理的。

Ø6)通常业务操作会专门开辟一个线程池,那业务处理完成之后,如何将响应结果通过 IO 线程写入到网卡中呢?业务线程调用 Channel 对象的 write 方法并不会立即写入网络,只是将数据放入一个待写入缓存区,然后IO线程每次执行事件选择后,会从待写入缓存区中获取 写入任务,将数据真正写入到网络中

ChannelPipeline & ChannelHandler

ChannelPipeline 提供了 ChannelHandler 链的容器,ChannelHandler 是一个双向链表。以服务端程序为例,客户端发送过来的数据要接收,读取处理,我们称数据是入站的,需要经过一系列Handler处理后;如果服务器想向客户端写回数据,也需要经过一 系列Handler处理,我们称数据是出站的。

 执行流程: 

  1. 在Netty中,每个Channel被创建的时候都需要被关联一个对应的pipeline(通道),这种关联关系是永久的(整个程序运行的生命周期中)。
  2. 当SocketChannel被创建时候,ChannelPipeline也被创建并和SocketChannel关联绑定,
  3. ChannelPipeline初始状态的默认有head和tail两个handler,中间部分的handler都是我们自己创建的用来处理业务等数据
  4. SocketChannel的read事件被触发后(被Eventloop检测到后),head部分的handler会自动降内核中的数据转到应用区
  5. 自定义handler处理数据

ChannelHandler 分类

inbound/outbound

inbound入站事件处理顺序(方向)是由链表的头到链表尾,outbound事件的处理顺序是由链表尾到链表头。

inbound入站事件由netty内部触发,最终由netty外部的代码消费。 outbound事件由netty外部的代码触发,最终由netty内部消费

Hello Netty

服务端



public class NettyServer {

    public static void main(String[] args) {
        NettyServer nettyServer = new NettyServer();
        nettyServer.start(8888);
    }

    public  void start(int port) {
        //准备EvenLoopGroup
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            // 核心引导类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        /**
                         * 客户端channel初始化时回调
                         * @param ch
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new ServerInboundHandler1());
                        }
                    });

            // 绑定端口启动
            ChannelFuture future = serverBootstrap.bind(port).sync();
            // 监听端口的关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }

}

handler



public class ServerInboundHandler1 extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ServerInboundHandler1 channelActive");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ServerInboundHandler1 channelInactive");
        super.channelInactive(ctx);
    }

    /**
     * 有数据时的回调
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        String content = new String(bytes, Charset.defaultCharset());
        System.out.println("收到的数据:"+content);
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ServerInboundHandler1 channelReadComplete");
        //向客户端写回数据
        Channel channel = ctx.channel();
        ByteBuf buffer = ctx.alloc().buffer();
        buffer.writeBytes("hello,nettclient".getBytes(StandardCharsets.UTF_8));
        channel.writeAndFlush(buffer);
        super.channelReadComplete(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("ServerInboundHandler1 exceptionCaught," + cause.getMessage());
        super.exceptionCaught(ctx, cause);
    }
}

客户端



public class NettyClient {

    public static void main(String[] args) {
        NettyClient nettyClient = new NettyClient();
        nettyClient.connect("127.0.0.1", 8888);
    }

    public void connect(String host,int port) {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            Channel channel = channelFuture.channel();

            //向服务端写数据
            ByteBuf buffer = channel.alloc().buffer();
            buffer.writeBytes("hello nettyserver".getBytes(StandardCharsets.UTF_8));
            channel.writeAndFlush(buffer);

            //
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

hello netty源码下载https://download.csdn.net/download/weixin_44680802/87338750

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值