Netty学习(六)介绍下netty的原理

261 阅读4分钟

一、说说Netty的工作原理

上图

上面的这个图从网上找来的,对这个图进行说明:

  • Netty抽象出两组线程池,BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写
  • BossGroupWorkerGroup类型的本质都是NioEventLoopGroup类型。
  • NioEventLoopGroup相当于一个线程管理器(类似于ExecutorServevice),它下面维护很多个NioEventLoop线程。(我认为图中的NioEventGroup的地方应该改成NioEventLoop,可能我的理解有点差错吧)
    • 在初始化这两个Group线程组时,默认会在每个Group中生成CPU*2NioEventLoop线程
    • n个连接来了,Group默认会按照连接请求的顺序分别将这些连接分给各个NioEventLoop去处理。
    • 同时Group还负责管理EventLoop的生命周期。
  • NioEventLoop表示一个不断循环的执行处理任务的线程
    • 它维护了一个线程和任务队列。
    • 每个NioEventLoop都包含一个Selector,用于监听绑定在它上面的socket通讯。
    • 每个NioEventLoop相当于Selector,负责处理多个Channel上的事件
    • 每增加一个请求连接,NioEventLoopGroup就将这个请求依次分发给它下面的NioEventLoop处理。
  • 每个Boss NioEventLoop循环执行的步骤有3步:
    • 轮询accept事件
    • 处理accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个Worker NioEventLoopselector上。
    • 处理任务队列到任务,即runAllTasks
  • 每个Worker NioEventLoop循环执行的步骤:
    • 轮询readwrite事件
    • 处理I/O事件,即readwrite事件,在对应的NioSocketChannel中进行处理
    • 处理任务队列的任务,即runAllTasks
  • 每个 Worker NioEventLoop处理业务时,会使用pipeline(管道),pipeline中维护了一个ChannelHandlerContext链表,而ChannelHandlerContext则保存了Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。

二、写个netty的例子


package com.angliali.nio.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * 使用netty进行简单的请求处理
 */
public class NettyServer {

    public static void main(String[] args) throws InterruptedException {

        //初始化两个工作组,这两个工作组,都是线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();


        try {
            //设置启动器,开始使用netty
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup,workerGroup)                //设置两个线程组
                            .channel(NioServerSocketChannel.class)      //指定channel的类型,这里使用netty自己的Nioserversocketchannel
                            .option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到的连接个数
                            .childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                //给pipeline设置handler
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ch.pipeline().addLast(new NettyServerHandler());
                                }
                            });

            System.out.println("服务器装备就绪,马上启动");

            //绑定端口,并生成一个异步对象
            ChannelFuture cf = serverBootstrap.bind(6668).sync();

            //对断开连接进行监听
            cf.channel().close().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
package com.angliali.nio.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * 客户端的handler
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //通道就绪后,执行该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ctx:"+ctx);

        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,服务器", CharsetUtil.UTF_8));
    }

    //当通道有读取事件时,执行
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        System.out.println("ctx:"+ctx);
        //处理服务器返回的消息
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器返回的消息:"+buf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
package com.angliali.nio.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * 使用netty进行简单的请求处理
 */
public class NettyServer {

    public static void main(String[] args) throws InterruptedException {

        //初始化两个工作组,这两个工作组,都是线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();


        try {
            //设置启动器,开始使用netty
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup,workerGroup)                //设置两个线程组
                            .channel(NioServerSocketChannel.class)      //指定channel的类型,这里使用netty自己的Nioserversocketchannel
                            .option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到的连接个数
                            .childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                //给pipeline设置handler
                                protected void initChannel(SocketChannel ch) throws Exception {
                                    ch.pipeline().addLast(new NettyServerHandler());
                                }
                            });

            System.out.println("服务器装备就绪,马上启动");

            //绑定端口,并生成一个异步对象
            ChannelFuture cf = serverBootstrap.bind(6668).sync();

            //对断开连接进行监听
            cf.channel().close().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
package com.angliali.nio.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.nio.charset.Charset;

/**
 * netty中的handler
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    //这个方法就是用来处理读取请求对象
    //简单处理请求,输出请求的原始地址
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("请求上下文:"+ctx);

        ByteBuf buf = (ByteBuf) msg;
        System.out.println("请求对象:"+buf.toString(CharsetUtil.UTF_8));

        //输出原始地址
        System.out.println("请求地址:"+ctx.channel().remoteAddress());
    }

    //请求处理完后的操作
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello",CharsetUtil.UTF_8));
    }

    //发生异常时的处理
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

}

三、简单说明下上面的列子

上面这个例子是用netty做的简单的网络编程,有些方法后面会说到。这里主要说明下bootstrap,我称它为启动器。

上面那串链式编程的主要用途就是为了设置netty工作原理的构建。

  • 绑定工作线程组
  • 设置channel的实现类型
  • 设置主nioeventloopgroup的属性
  • 设置从nioeventloopgroup的属性
  • 给pipeline绑定handler

其余后面再说啦!