Netty源码分析(二):服务端启动

Stella981
• 阅读 544

上一篇粗略的介绍了一下netty,本篇将详细介绍Netty的服务器的启动过程。

ServerBootstrap

看过上篇事例的人,可以知道ServerBootstrap是Netty服务端启动中扮演着一个重要的角色。 它是Netty提供的一个服务端引导类,继承自AbstractBootstrap
ServerBootstrap主要包括两部分:bossGroupworkerGroup。其中bossGroup主要用于绑定端口,接收来自客户端的请求,接收到请求之后,就会把这些请求交给workGroup去处理。就像现实中的老板和员工一样,自己开个公司(绑定端口),到外面接活(接收请求),使唤员工干活(让worker去处理)。

端口绑定

端口绑定之前,会先check引导类(ServerBootstrap)的bossGroup和workerGroup有没有设置,之后再调用doBind。

    private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化并注册一个channel,并将chanelFuture返回
        final ChannelFuture regFuture = initAndRegister();
        // 得到实际的channel(初始化和注册的动作可能尚未完成)
        final Channel channel = regFuture.channel();
        // 发生异常时,直接返回
        if (regFuture.cause() != null) {
            return regFuture;
        }
        // 当到这chanel相关处理已经完成时
        if (regFuture.isDone()) {
            // 到这可以确定channel已经注册成功
            ChannelPromise promise = channel.newPromise();
            // 进行相关的绑定操作
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // 注册一般到这就已经完成,到以防万一
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 添加一个监听器
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        // 修改注册状态为成功(当注册成功时不在使用全局的executor,使用channel自己的,详见 https://github.com/netty/netty/issues/2586)
                        promise.registered();
                        // 进行相关的绑定操作
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

上面的代码主要有两部分:初始化并注册一个channel和绑定端口。

初始化并注册一个channel

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 生产各新channel
            channel = channelFactory.newChannel();
            // 初始化channel
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // 注册失败时强制关闭
                channel.unsafe().closeForcibly();
                // 由于channel尚未注册好,强制使用GlobalEventExecutor
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        // 注册channel
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

channel的初始化方法:

    void init(Channel channel) throws Exception {
        // 获取bossChannel的可选项Map
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }
        // 获取bossChannel的属性Map
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
        ChannelPipeline p = channel.pipeline();
        // 设置worker的相关属性
        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                // 添加handler到pipeline
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                // 通过EventLoop将ServerBootstrapAcceptor到pipeline中,保证它是最后一个handler
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

channel的注册方法,最终是调用doRegister,不同的channel有所不同,下面以Nio为例:

    protected void doRegister() throws Exception {
        boolean selected = false;
        for (; ; ) {
            try {
                // 直接调用java的提供的Channel的注册方法
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    throw e;
                }
            }
        }
    }

绑定端口

最终调用的是NioServerSocketChannel的doBind方法。

    protected void doBind(SocketAddress localAddress) throws Exception {
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

到这就完成了netty服务端的整个启动过程。

文中帖的代码注释全在:https://github.com/KAMIJYOUDOUMA/nettyForAnalysis.git , 有兴趣的童鞋可以关注一下。


本篇到此结束,如果读完觉得有收获的话,欢迎点赞、关注、加公众号【贰级天災】,查阅更多精彩历史!!!

Netty源码分析(二):服务端启动

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Netty之旅三:Netty服务端启动源码分析,一梭子带走!
Netty服务端启动流程源码分析前记哈喽,自从上篇《Netty之旅二:口口相传的高性能Netty到底是什么?》后,迟迟两周才开启
Stella981 Stella981
2年前
Netty 启动过程源码分析 (本文超长慎读)(基于4.1.23)
前言作为一个Java程序员,必须知道Java社区最强网络框架Netty,且必须看过源码,才能说是了解这个框架,否则都是无稽之谈。今天楼主不会讲什么理论和概念,而是使用debug的方式,走一遍Netty(服务器)的启动过程。1\.demo源码楼主clone的netty的源码,值得一提
Stella981 Stella981
2年前
Netty3之ServerBootstrap分析
概述ServerBootstrap是Netty提供的一个服务端工具类,通过设置ChanneFactory,ChannelPipelineFactory,用户可以很方便的启动一个服务端。ServerBootstrap是做什么的ServerBootstrap是一个帮助类,用来创建服务端的Channel以
Stella981 Stella981
2年前
Netty创建服务器与客户端
Netty创建Server服务端Netty创建全部都是实现自AbstractBootstrap。客户端的是Bootstrap,服务端的则是ServerBootstrap。创建一个HelloServerpackageorg.examp
Stella981 Stella981
2年前
Netty之大名鼎鼎的EventLoop
EventLoopGroup与Reactor:前面的章节中我们已经知道了,一个Netty程序启动时,至少要指定一个EventLoopGroup(如果使用到的是NIO,通常是指NioEventLoopGroup),那么,这个NioEventLoopGroup在Netty中到底扮演着什么角色呢?我们知道,Netty是Reactor模型的
Stella981 Stella981
2年前
Netty源码解析
Netty源码解析服务端启动一个简单的服务端代码:publicclassSimpleServer{publicstaticvoidmain(Stringargs){NioEventLoopGroupbossnewNioEventLoopGroup
Stella981 Stella981
2年前
Netty 学习笔记(1)
服务端启动流程packagecom.example.netty;importcom.example.netty.handler.HelloServerHandler;importio.netty.bootstrap.ServerBootstrap;importio.netty.cha
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这