Netty小结

Netty的概念

什么是Netty

Netty是一个用Java开发的开源框架,是一个基于NIO的异步的、基于事件驱动的网络IO应用框架,使用Netty可以快速开发高性能、高可靠的网络IO程序,Netty被使用在各种中间件中为各节点提供通信服务,比如dubbo,以及在游戏行业中实现服务器之间的通信

简单的Java实现

1、引入Maven依赖

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

2、NettyServerDemo

public class NettyServerDemo {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("NettyServer start....");
            ChannelFuture channelFuture = bootstrap.bind(9000).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }

/**
* 业务实现类,我们只需要在各种重写方法中实现业务逻辑即可
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("Server收到消息:" + buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ByteBuf byteBuf = Unpooled.copiedBuffer("this is from server", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf);
    }
}

3、NettyClientDemo

public class NettyClientDemo {

    public static void main(String[] args) {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new NettyClientHandler());
                        }
                    });
            System.out.println("netty client start...");
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9000).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }
}

/**
* 客户端的业务实现类 
*/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf byteBuf = Unpooled.copiedBuffer("this is from client", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println("收到客户端消息:" + byteBuf.toString(CharsetUtil.UTF_8));
    }
}

Netty底层原理

Netty线程模型

在这里插入图片描述
这是转载自网上的一张Netty线程架构图,对应这张图我们可以逐步理解NettyDemo的代码,以下是转载的内容:

Netty使用两个NioEventLoopGroup分别处理客户端的连接请求和读写请求,一个叫BossGroup,一个叫WorkerGroup,NioEventLoopGroup是一个由多个事件循环线程NioEventLoop组成的事件循环线程组,每个NioEventLoop都包含一个selector,用于监听注册在selector上的socketChannel网络通讯,每个BossGroup内的NioEventLoop线程执行以下步骤:
1、处理客户端的accept事件,与client建立连接,每个client生成一个NioSocketChannel
2、将NioSocketChannel注册到WorkGroup中的某个NioEventLoop中的selector上
3、处理任务队列中的任务
每个WorkerGroup内的NioEventLoop线程循环执行以下步骤:
1、轮询注册到自己的selector上的所有NioSocketChannel的读写事件
2、处理读写事件,执行业务逻辑
3、处理任务队列中的任务
每个Worker NioEventLoop处理NioSocketChannel业务时,会使用pipeline,pipeline维护了很多handler处理器用来处理channel中的数据,同理我们也可以将自定义的handler加入到管道中来实现自己的业务逻辑

Netty中的术语

Bootstrap、ServerBootstrap:
Netty的client和server引导类,负责添加netty的配置和自定义handler

Future、ChannelFutrure:
通过Future和ChannelFuture实现对Netty启动或关闭操作结果的监听,因为这些操作都是异步的,不会直接返回结果

Channel:
实现网络通信的通道,客户端和服务端通过Channel发送接收请求和执行IO操作,这些操作都是异步的,常用的Channel类型:

NioServerChannel 异步的客户端TCP Socket连接
NioServerSocketChannel 异步的服务器端TCP Socket连接
NioDatagramChannel 异步的UDP连接
NioSctpChannel 异步的客户端Sctp连接
NioSctpServerChannel 异步的Sctp服务器端连接

Selector:多路复用器,Netty是基于NIO的,客户端的socketchannel都会被注册到工作线程中的selector上,selector会不停的轮询注册的socketchannel是否发生IO事件

NioEventLoop:内部封装了一个线程和一个队列,线程负责执行注册在selector上的channel的IO事件(accept、read、write等)和队列中的task(register、bind等)

NioEventLoopGroup:由NioEventLoop组成的集合,负责为socketChannel分配NioEventLoop

ChannelHandler:顶层接口,负责处理IO事件,链式调用多个handler,一般是继承它的子类ChannelInboundHandler和ChannelOutboundHandler

ChannelHandkerContext:保存Channel的上下文信息,同时关联一个ChannelHandler,可以通过该对象获取channel

ChannelPipline:管道本质是一个由ChannelHandlerContext组成的双向链表,通过链式调用管道中的handler处理IO事件,每个channel都有一个自己的管道,read事件从head到tail顺序执行,write事件从tail到head顺序执行

Netty源码流程

可以参考我这张图理解服务端核心源码
https://www.processon.com/view/link/5f35f87407912920b49b766e
大致流程如下:
Netty启动时会创建一个serversocketChannel,并对应初始化一个pipeline,往pipeline添加一个new ChannelInitializer,然后将serversocketChannel注册到bossGroup中的一个nioEventLoop上的selector上,同时添加异步任务(一个Runnable)到队列里,selector阻塞等待接收client的accept事件,队列中有任务则启动线程执行任务,任务里会执行pipeline中的ChannelInitializer的initChannel方法添加ServerBootHandler到管道里,客户端启动后连接Netty服务端触发accept事件,服务端执行processSelectedKeys()轮询处理accept事件,处理accept事件的实现类为NioMessageUnsafe,首先为连接构造一个socketChannle并初始化一个pipeline,然后回调ServerBootHandler的channelRead方法添加我们new的ChannelInitializer(),然后将socketChannel注册到workerGroup中的一个NioEventLoop上的selector,接下来的流程就和serversocketChannel注册的流程类似了,依然是提交异步任务,selector阻塞等待客户端的read和wirte事件,异步任务中会执行我们new的ChannelInitializer()的initChannle方法把我们的业务handler添加到pipeline中,客户端发送消息触发read事件,处理read事件的实现类为NioByteUnsafe,循环调用pipeline的channelRead方法,最后调用readComplete等方法

Netty架构设计亮点

主从Reator架构模型

架构模型理论图:
在这里插入图片描述
Netty架构图:
在这里插入图片描述

Netty底层基于NIO实现,架构模型采用主从Reator架构模型,主节点负责处理连接事件,从节点负责处理读写事件,可以有有多个线程

Netty中存储数据的缓存数组

ByteBuf:Netty使用ByteBuf来存储数据,ByteBuf底层是一个字节数组,自带两个索引(指针),一个指向当前将要读取的位置,一个指向当前已写入的位置,Netty读取client发送的数据到byteBuf时是直接申请的堆外内存(直接内存),避免用户态和内核态的转换,即使用零拷贝的方式,大大提高性能

扩容机制:默认阈值4MB,扩容时如果需要使用的容量等于阈值则直接扩容到阈值,如果大于阈值,则以4MB为单位累加,4MB->8MB->12MB->…,如果小于阈值则以64字节为基础,每次翻倍,64->128->256->…,直到到达阈值或达到需要使用的容量

零拷贝提高读写速度

零拷贝不等于不拷贝,只是节省了内核态和用户态之间的拷贝过程,Netty直接操作堆外内存,不需要将数据读到堆内存,但是从堆外内存到网卡也是需要拷贝的

Nio空轮询bug修复

	//Netty解决Nio空轮询bug就是用了一个自增的selectCnt
	int selectedKeys = selector.select(timeoutMillis);
    ++selectCnt;
    ...
    if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
    	//正常情况下跳出重置selectCnt
        selectCnt = 1;
    } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
    	//selectCnt超过阈值重新构建一个selector关闭旧的selector
        selector = this.selectRebuildSelector(selectCnt);
        selectCnt = 1;
        break;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值