Netty源码-Bind()方法分析

Netty

1、基础回忆

	基于NIO异步通讯框架

阅读源码首先需要了解四大组件

  • 1、 Channel
1、通过管道可以设置参数,TCP相关参数,Netty对其参数进行封装,如果没有Netty则要修改Linux的配置文件。
2、Netty中各个组件的串联者
  • 2、EventLoop
IO操作,定时任务、普通任务
  • 3、Unsafe
直译:线程不安全
IO操作
REDA
Write
  • ChannelPipline
流水线,两大比较重要的参数ChannelContext、ChannelHandler

2、Netty源码

首先思考原有的NIO服务端开发,编码如下:

 public static void main(String[] args) throws IOException, InterruptedException {
        //NIO
        //EventLoop对其进行封装  Select成员变量 EventLoop的构造方法中进行初始化
        Selector selector = Selector.open();
        //创建服务端
        //完成ServerSocketChannel创建过程,初始化操作
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.configureBlocking(false);
        //ServerSocketChannel注册工作 注册操作
        SelectionKey selectionKey = socketChannel.register(selector, 0, null);
        selectionKey.interestOps(SelectionKey.OP_ACCEPT);
        //端口绑定的操场
        socketChannel.bind(new InetSocketAddress(8080));
  }

原有Netty服务端开发代码为:

/**
 * @author :和振斌
 * @date : 2022-11-22 14:41
 * @Describe:类的描述信息
 */
public class NettyServer {

    private static final Logger log = LoggerFactory.getLogger(NettyServer.class);

    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.channel(NioServerSocketChannel.class);
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        final LoggingHandler loggingHandler = new LoggingHandler();
        try {
            serverBootstrap.group(bossGroup, workerGroup);
           serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
               @Override
               protected void initChannel(NioSocketChannel ch) throws Exception {
                   ch.pipeline().addLast(new LoggingHandler());
               }
           });
            Channel channel = serverBootstrap.bind(8080).sync().channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            log.debug("server error is {}", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

对比两者的代码,思考如下三个问题

1、Netty怎么对Nio代码进行封装的,为什么两者的编码全不一样?
2、Netty中NioServerSocketChannel的作用:
		1、Channel可以设置参数
		2、是各个部件的串联者
		3、Pipline的管理者
3、NioServerSocketChannel Netty提供的怎么和Nio代码结合在一起的?
     附件形式,Netty设置过程中是通过附件形式将NioServerSocketChannel与原有的Nio结合在一起的

2.1、分析一下NioServerSocket的类关系图

在这里插入图片描述

2.2、源码阅读

2.2.1、ServerSocketChannel的创建

1、完成上述工作的主要依赖于bind方法
看ServerBootStrap.bind完成工作
1、init SSC创建
2、register SSC在select注册
3、端口绑定

在这里插入图片描述

2、AbstractBootstrap.bind()

在这里插入图片描述

3、AbstractBootstrap.doBind()

在这里插入图片描述

4、AbstractBootstrap.daBind()中的initAndRegister()负责
(1)ServerSocketChannel的创建
(2)ServerSocketChannel的注册
(3)异步操作

在这里插入图片描述

5、AbstractBootstrap.initAndRegister()

在这里插入图片描述

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //1、主要负责ServerSocketChannel的创建
            channel = channelFactory.newChannel();
            //为ServerSocketChannel添加一个handler为ChannelInitializer
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {

                channel.unsafe().closeForcibly();

                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }

            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        //2、注册Selector
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

6、channelFactory.newChannel()创建ServerSocketChannel,利用反射调用无参构造创建NioServerSocketChannel

在这里插入图片描述

以下对上面采用反射构建NioServerSokcetChannel的详述

在这里插入图片描述

调用newSocket(DEFAULT_SELECTOR_PROVIDER)

在这里插入图片描述

其中priverder参数为SelectorProvider.provider()
即为:return SelectorProvider.provider().openServerSocketChannel()
返回值为ServerSocketChannelNioServerSocketChannel创建ServerSocketChannle
并且设置为非阻塞的

在Netty中获取ServerSocketchannel的方式为

在这里插入图片描述
在这里插入图片描述
综上所述:ServerSocketChannel创建的方式是采用反射的方式调用NioServerSocketChannel的无参构造,进行创建的,这个创建方式对Nio的ServerSocketChannel进行深入的封装。

2.2.2、ServerSocketChannel初始化

1、AbstractBootstrap.initAndRegister()中的init()方法

在这里插入图片描述

    void init(Channel channel) {
         //1、设置参数
        setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
        setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
        //2、初始化ServerSocketChannel的pipeline
        ChannelPipeline p = channel.pipeline();
          
        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions =
                childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
       //3、添加handler但没有调用initChannel()这个会回调
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

2、ServerBootstrap.init(Channel)

在这里插入图片描述

1、设置ServerSocketChannel的必要参数
2、为ServerSocket添加一个hanndler为:ChannelInitializer但这只是注册进去,里面的initChannel
并没有进行调用。这个时候ServerSocketChannel中的pipeline中有三个handler,分别为Head、
ChannelInitializer、Tail。
2.2.3、ServerSocketChannel注册

1、AbstractBootstrap.initAndRegister()

在这里插入图片描述

2、MultithreadEventLoopGroup.register(Channel channel)其中next()就是EventLoop

在这里插入图片描述

3、SingleThreadEventLoop.register(Channel channel)

在这里插入图片描述
在这里插入图片描述

4、AbstractChannel.register(EventLoop eventLoop, final ChannelPromise promise)

在这里插入图片描述

if (eventLoop.inEventLoop()) {//判断当前线程是否为Nio线程,如果当前线程不是Nio线程则走else
          register0(promise);
} 

5、AbstractChannel.register0(ChannelPromise promise)中的doRegister()进行ServerSocketChannel的注册

在这里插入图片描述

6、AbstractNioChannel.doRegister() 进行ServerSocketChanel的注册

在这里插入图片描述

 selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
 可以写成serverSocketChannel.register(select,0,this) 这个格式和Nio注册Channel是一样的

在这里插入图片描述
在这里插入图片描述

这个方法的执行直译为执行Hanndler如果需要,这个执行的代码是上面初始化channel的时候注册了一个
ChannelInitializer但里面的initChannel()方法并没有执行,在这里将会执行,

在这里插入图片描述
这个方法的执行会回调ServerSocketChannel初始化过程中添加的那个handler中的ChannelInitializer中的initChannel()为其ServerSocketChannel添加一个处理Accept事件的Handler

safeSetSuccess(promise),这个promise为initAndRegister()的返回值,这行代码执行完成之后,会将该线程的结果返回给Main线程。

在这里插入图片描述

在这里插入图片描述

返回success之后将进行端口绑定的工作

在这里插入图片描述

AbstractBootstrap.doBind0()进行端口绑定

在这里插入图片描述

AbstractChannelHandlerContext.bind()进行端口绑定操作

在这里插入图片描述

NioServerSocketChannel底层执行端口绑定的操作

    protected void doBind(SocketAddress localAddress) throws Exception {
        //JDK版本在7以上
        if (PlatformDependent.javaVersion() >= 7) {
            //javaChannel()为ServerSocketChannel其代码可以表示为
            //ServerSocketChannel.bind(localAddress, config.getBacklog()这个表示形式和原始Nio端口绑定是    
            //一样的
            javaChannel().bind(localAddress, config.getBacklog());
            //Jdk在7以下
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

端口绑定完成之后,开始注册ServerScketChannel监听的事件,这个监听的代码在AbstractChannel.bind()方法调用。

在这里插入图片描述

pipeline.fireChannelActive(); 这行代码是重点,这个代码会将pipline中的所以Handler进行一一调用,现在pileline中主要有三个handler,分别是Head、ServerBootstrapAcceptor()、Tail三个,这三个其实是一个双向链表,调用这个方法会执行Pipeline中的所以handler重点ChannelActive方法,向ServerSocketChannel中的SelectKey设置监听的事件的方法主要需要关注一下,Head的ChannelActive方法。

在这里插入图片描述

注册Accept事件的方法主要在AbstractChannel.doBeginRead()

在这里插入图片描述

原生Nio注册Accept事件的代码如下所示:

        SelectionKey selectionKey = serverSocketChannel.register(selector, 0, null);
        selectionKey.interestOps(SelectionKey.OP_ACCEPT);

我们可以知道SelectionKey.OP_ACCEPT为16,因此我们可以证明在netty中是通过HeadContext中的Read事件对Accept进行注册的

3、总结

   Netty是基于NIO异步通讯的,我们会发现Netty代码和Nio的代码完全不同,其所有封装在bind()
   方法中,在这个方法中通过initAndRegister()方法,首先通过反射的方式创建ServetSocketChannel,
   采用调用NioServerSocketChannel的无参构造,进行ServerSocketChannel的创建,
   其实是对ServerSocketChannel中的Pipline中的handler,初始化过程中会首先加入ServerSocketChannel中的
   ChannelInitializer()但是并不会回调里面的initChannel()方法,最后进行Channel的注册,同时会回调
   ChannelInitializer()中的initChannel(),向Pipeline中添加一个ServerBootstrapAcceptor()进行Accept事件的
   监控,设置promise为Success,这时会进行端口的绑定,端口的绑定其实也是对原始Nio的底层封装,然
   后会设置ServerSocketChannel监控的事件,这个触发是在设置好监听端口之后,会调用pipeline中的所有ChannelActive方法,
   Accept监听主要设置在HeadContext中的ChannelActive方法中。综上所述,在Netty方
   法的Bind方法中,对Netty服务端中的ServerSocketChannel进行创建,pipline的初始化,监听事件的注
   册,端口的绑定都在Bind方法中完成。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值