netty学习笔记

netty使用

netty工作模型图

工作架构图如下:

请添加图片描述

  1. Netty 抽象出两组线程池:BossGroup 和 WorkerGroup,也可以叫做 BossNioEventLoopGroup 和 WorkerNioEventLoopGroup。每个线程池中都有 NioEventLoop 线程。BossGroup 中的线程专门负责和客户端建立连接,WorkerGroup 中的线程专门负责处理连接上的读写。BossGroup 和 WorkerGroup 的类型都是 NioEventLoopGroup。
  2. NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就是一个 NioEventLoop。
  3. NioEventLoop 表示一个不断循环的执行事件处理的线程,每个 NioEventLoop 都包含一个 Selector,用于监听注册在其上的 Socket 网络连接(Channel)。
  4. NioEventLoopGroup 可以含有多个线程,即可以含有多个 NioEventLoop。
  5. 每个 BossNioEventLoop 中循环执行以下三个步骤:
    • select:轮训注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)
    • processSelectedKeys:处理 accept 事件,与客户端建立连接,生成一个 NioSocketChannel,并将其注册到某个 WorkerNioEventLoop 上的 Selector 上
    • runAllTasks:再去以此循环处理任务队列中的其他任务
  6. 每个 WorkerNioEventLoop 中循环执行以下三个步骤:
    • select:轮训注册在其上的 NioSocketChannel 的 read/write 事件(OP_READ/OP_WRITE 事件)
    • processSelectedKeys:在对应的 NioSocketChannel 上处理 read/write 事件
    • runAllTasks:再去以此循环处理任务队列中的其他任务
  7. 在以上两个processSelectedKeys步骤中,会使用 Pipeline(管道),Pipeline 中引用了 Channel,即通过 Pipeline 可以获取到对应的 Channel,Pipeline 中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)。这里暂时不详细展开讲解 Pipeline。

创建BossGroup 和 WorkerGroup

根据上面的介绍,可以知道BossGroup 和 WorkerGroup 的类型都是 NioEventLoopGroup。

NioEventLoopGroup创建 默认的线程数目为CPU核数的2倍

//构造两个线程组
EventLoopGroup bossrGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

BossGroup 中的线程专门负责和客户端建立连接,WorkerGroup 中的线程专门负责处理连接上的读写。BossGroup中accept到Channel会注册到workerGroup中线程的selector。

服务端启动器ServerBootstrap

创建

要启动netty服务端,就需要依靠netty提供的服务端启动类ServerBootstrap。同过对启动类传递需要的参数就可以以配置的参数启动netty应用了。ServerBootstrap的用的是链式编程的设计实现,使用我们等会使用的时候会链式调用。

//服务端启动类
ServerBootstrap bootstrap = new ServerBootstrap();

根据官方的API文档,我们可以得知有两个启动类,分别是:

  • bootstrap (客户端使用的)
  • serverbootstrap(服务端使用的)

请添加图片描述

AbstractBootstrap主要方法:

方法说明
group()用来指定线程模型bossrGroup和workerGroup
channel()用来指定 IO 模型,如NioServerSocketChannel
handler()用来指定服务端通道需要处理的业务逻辑
childHandler()用来指定客户端通道需要处理的业务逻辑
attr()给服务端通道绑定自定义属性
childAttr()给客户端通道绑定自定义属性
option()给服务端通道设置配置
childOption()给客户端通道设置配置
bind()用来绑定端口号

使用示例

这里先忽略HttpServerInitializer,这个是自定义的类,后面会讲到。这里的childHandler主要用于传递要处理业务逻辑的调度器类。当服务器和客户端建立连接获取的SocketChannel如何使用就关系到ChannelHandler的编写。

根据上面的工作模型图可以得知,当WorkerNioEventLoop通过select选择到的processSelectedKeys(即在对应的 SocketChannel 上处理 read/write 事件),如果处理这个事件,就是ChannelHandler的工作了。

bootstrap.group(bossGroup, workerGroup)   //指定bossGroup, workerGroup
            .channel(NioServerSocketChannel.class) //指定IO模型,有BIO,NIO,AIO
            .childHandler(new HttpServerInitializer()); //传递ChannelHandler接口的实现类,以处理客户端SocketChannel的业务逻辑

ChannelFuture future = bootstrap.bind(8080).sync();//绑定一个8080端口

Channel、ChannelPipeline、ChannelHandler 之间的关系

Channel 是一个连接通道,客户端和服务端连接成功之后,会维持一个 Channel,可以通过 Channel 来发送数据。Channel 有且仅有一个 ChannelPipeline 与之相对应,ChannelPipeline 又维护着一个由多个 ChannelHandlerContext 组成的双向链表,ChannelHandlerContext 又关联着一个 ChannelHandler。

请添加图片描述

ChannelPipeline

通过ChannelPipeline,我们可以往里面放ChannelHandler,ChannelHandlerContext 又关联着一个 ChannelHandler。

ChannelPipeline主要方法:

方法描述
addFirst(…)添加 ChannelHandler 在 ChannelPipeline 的第一个位置
addBefore(…)在 ChannelPipeline 中指定的 ChannelHandler 名称之前添加 ChannelHandler
addAfter(…)在 ChannelPipeline 中指定的 ChannelHandler 名称之后添加 ChannelHandler
addLast(…)在 ChannelPipeline 的末尾添加 ChannelHandler
remove(…)删除 ChannelPipeline 中指定的 ChannelHandler
replace(…)替换 ChannelPipeline 中指定的 ChannelHandler
ChannelHandler first()获取链表当中的第一个节点
ChannelHandler last()获取链表当中的最后一个节点

使用

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
    .group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<NioSocketChannel>() {
        protected void initChannel(NioSocketChannel ch) {
           ch.pipeline().addLast(new InboundHandler1());
		   ch.pipeline().addLast(new InboundHandler2());
        }
    });

public class InboundHandler1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("inbound1在channelRead时刻进行处理");
        //1.往下传递
        super.channelRead(ctx, msg);
    }
}

public class InboundHandler2 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("inbound2在channelRead时刻进行处理");
    }
}

ChannelinboundHandler 和 ChanneloutboundHandler

inboundHandler主要处理入站流量,而outboundHandler处理出战操作。

  • ChannelinboundHandler接口的适配类主要有:ChannelInboundHandlerAdapter
  • ChanneloutboundHandler接口的适配类主要有:ChannelOutboundHandlerAdapter

入站事件和出站事件在一个双向链表中,入站事件会从链表head往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的handler,两种类型的handler互不干扰。

ChannelHandler接口

Netty提供了很多ChannelHandler接口的实现类供外部继承使用,通过这些实现类,我们就可以在获取相关信息以处理我们的业务逻辑。

实现了ChannelHandler接口的Java类有许多,如:ChannelInitializer,ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter,HttpServerCodec等

ChannelInitializer

public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
    
    protected void initChannel(SocketChannel sc) throws Exception {
        ChannelPipeline pipeline = sc.pipeline();
     //处理http消息的编解码,HttpServerCodec类是官方提供的对http的[code-decode]的实现类
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //添加自定义的ChannelHandler
        pipeline.addLast("httpServerHandler", new HttpServerHandler());
    }
}

ChannelInboundHandlerAdapter

public class InboundHandler1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("inbound1在channelRead时刻进行处理");
        //1.往下传递
        super.channelRead(ctx, msg);
    }
}

主要:入站事件和出站事件在一个双向链表中,入站事件会从链表head往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的handler,两种类型的handler互不干扰。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值