研究了netty的服务端创建过程。至于netty的优势,可以参照网络其他文章。《Netty系列之Netty 服务端创建》是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货。但抛开技术来说,也存在一些瑕疵。

缺点如下

  1. 代码衔接不连贯,上下不连贯。

  2. 代码片段是截图,对阅读代理不便(可能和阅读习惯有关)

本篇主要内容,参照《Netty系列之Netty 服务端创建》,梳理出自己喜欢的阅读风格。

1.整体逻辑图

整体将服务端创建分为2部分:(1)绑定端口,提供服务过程;(2)轮询网络请求

1.1 绑定端口序列图

1.2 类图

类图仅仅涵盖了绑定过程中比较重要的几个组件

1.3 代码分析

step 2 doBind 绑定本地端口,启动服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private ChannelFuture doBind(final SocketAddress localAddress) {
    final ChannelFuture regFuture = initAndRegister();//1
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }
    final ChannelPromise promise;
    if (regFuture.isDone()) {
        promise = channel.newPromise();
        doBind0(regFuture, channel, localAddress, promise);//2
    else {
        // Registration future is almost always fulfilled already, but just in case it's not.
        promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
        regFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                doBind0(regFuture, channel, localAddress, promise);//2
            }
        });
    }
    return promise;
}

主要分为2个处理单元

step3 initAndRegister

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
final ChannelFuture initAndRegister() {
    Channel channel;
    try {
        channel = createChannel();
    catch (Throwable t) {
        return VoidChannel.INSTANCE.newFailedFuture(t);
    }
    try {
        init(channel);
    catch (Throwable t) {
        channel.unsafe().closeForcibly();
        return channel.newFailedFuture(t);
    }
//注册NioServerSocketChannel到Reactor线程的多路复用器上
    ChannelPromise regFuture = channel.newPromise();
    channel.unsafe().register(regFuture);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

createChannel由子类ServerBootstrap实现,创建新的NioServerSocketChannel,并完成Channel初始化,以及注册。

4.ServerBootstrap.createChannel

1
2
3
4
Channel createChannel() {
    EventLoop eventLoop = group().next();
    return channelFactory().newChannel(eventLoop, childGroup);
}

它有两个参数,参数1是从父类的NIO线程池中顺序获取一个NioEventLoop,它就是服务端用于监听和接收客户端连接的Reactor线程。第二个参数就是所谓的workerGroup线程池,它就是处理IO读写的Reactor线程组。

5.ServerBootstrap.init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void init(Channel channel) throws Exception {
//设置Socket参数和NioServerSocketChannel的附加属性
    final Map<ChannelOption<?>, Object> options = options();
    synchronized (options) {
        channel.config().setOptions(options);
    }
    final Map<AttributeKey<?>, Object> attrs = attrs();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            @SuppressWarnings("unchecked")
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            channel.attr(key).set(e.getValue());
        }
    }
//将AbstractBootstrap的Handler添加到NioServerSocketChannel的ChannelPipeline中
    ChannelPipeline p = channel.pipeline();
    if (handler() != null) {
        p.addLast(handler());
    }
    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()));
    }
//将用于服务端注册的Handler ServerBootstrapAcceptor添加到ChannelPipeline中
    p.addLast(new ChannelInitializer<Channel>() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            ch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions,
                    currentChildAttrs));
        }
    });
}

到此处,Netty服务端监听的相关资源已经初始化完毕。

6.AbstractChannel.AbstractUnsafe.register

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final void register(final ChannelPromise promise) {
//首先判断是否是NioEventLoop自身发起的操作,如果是,则不存在并发操作,直接执行Channel注册;
    if (eventLoop.inEventLoop()) {
        register0(promise);
    else {//如果由其它线程发起,则封装成一个Task放入消息队列中异步执行。
        try {
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        catch (Throwable t) {
            logger.warn(
                    "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                    AbstractChannel.this, t);
            closeForcibly();
            closeFuture.setClosed();
            promise.setFailure(t);
        }
    }
}

7.register0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void register0(ChannelPromise promise) {
    try {
        // check if the channel is still open as it could be closed in the mean time when the register
        // call was outside of the eventLoop
        if (!ensureOpen(promise)) {
            return;
        }
        doRegister();
        registered = true;
        promise.setSuccess();
        pipeline.fireChannelRegistered();
         
        if (isActive()) {//完成绑定时,不会调用该代码段
            pipeline.fireChannelActive();
        }
    catch (Throwable t) {
        // Close the channel directly to avoid FD leak.
        closeForcibly();
        closeFuture.setClosed();
        if (!promise.tryFailure(t)) {
            logger.warn(
                    "Tried to fail the registration promise, but it is complete already. " +
                            "Swallowing the cause of the registration failure:", t);
        }
    }
}

触发事件

8.doRegister

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
        //将NioServerSocketChannel注册到NioEventLoop的Selector上
            selectionKey = javaChannel().register(eventLoop().selector, 0this);
            return;
        catch (CancelledKeyException e) {
            if (!selected) {
                // Force the Selector to select now as the "canceled" SelectionKey may still be
                // cached and not removed because no Select.select(..) operation was called yet.
                eventLoop().selectNow();
                selected = true;
            else {
                // We forced a select operation on the selector before but the SelectionKey is still cached
                // for whatever reason. JDK bug ?
                throw e;
            }
        }
    }
}

大伙儿可能会很诧异,应该注册OP_ACCEPT(16)到多路复用器上,怎么注册0呢?0表示只注册,不监听任何网络操作。这样做的原因如下:

  1. 注册方法是多态的,它既可以被NioServerSocketChannel用来监听客户端的连接接入,也可以用来注册SocketChannel,用来监听网络读或者写操作;

  2. 通过SelectionKey的interestOps(int ops)方法可以方便的修改监听操作位。所以,此处注册需要获取SelectionKey并给AbstractNioChannel的成员变量selectionKey赋值。

本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1863497,如需转载请自行联系原作者

netty 5 alph1源码分析(服务端创建过程)相关推荐

  1. netty 关闭chnnal_Netty 源码学习——服务端流程分析

    在上一篇我们已经介绍了客户端的流程分析,我们已经对启动已经大体上有了一定的认识,现在我们继续看对服务端的流程来看一看到底有什么区别. 服务端代码 public class NioServer { pr ...

  2. java调用dubbo服务器_dubbo源码分析-服务端注册流程-笔记

    前面,我们已经知道,基于spring这个解析入口,到发布服务的过程,接着基于DubboProtocol去发布,最终调用Netty的api创建了一个NettyServer. 那么继续沿着Registry ...

  3. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  4. 成品app直播源码,服务端与客户端传输视频文件

    成品app直播源码,服务端与客户端传输视频文件相关的代码 Server端 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARN ...

  5. 校园跑腿小程序源码,服务端+客户端,可运营

    需要准备 1.小程序 2.服务器(推荐配置2h4g3m) 3.域名(需要备案) 搭建教程 使用服务器搭建宝塔 安装pm2管理器 新建项目上传服务器接口 修改/pub/config/config.js ...

  6. 源码通透-mybatis源码分析以及整合spring过程

    源码通透-mybatis源码分析以及整合spring过程 mybatis源码分析版本:mybaits3 (3.5.0-SNAPSHOT) mybatis源码下载地址:https://github.co ...

  7. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

  8. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  9. Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean

    前言 spring创建bean的方式 测试代码准备 createBeanInstance()方法分析 instantiateUsingFactoryMethod()方法分析 总结 spring创建be ...

最新文章

  1. 关于学习Python的一点学习总结(51)
  2. 云曦服务器,云曦南羡最新章节_云曦南羡全本小说
  3. day_06、面向对象
  4. 神策 2021 数据驱动大会丨北京主会场首日直播,拼团早鸟票特惠来袭
  5. 要学会Python爬虫都需要什么基础呢?
  6. UVA1262Password(第K字典序)
  7. Longformer:超越RoBERTa,为长文档而生的预训练模型
  8. 克罗谈投资策略04_感觉与现实
  9. Python核心编程读笔 4
  10. 查看和修改MySQL数据库表存储引擎
  11. 两个数组各个数相加或相乘变成一个矩阵求第K大
  12. Eclipse汉化方法202012月版
  13. 扫描转换html,HTML5/CSS3 3D雷达扫描动画
  14. 计算机科学与技术的学习路线
  15. JavaScript 手写函数柯里化 curry
  16. NatureProtocols:使用MicrobiomeAnalyst统计和功能分析微生物组数据
  17. Egg http proxy
  18. 图像相关层之卷积锐化图片示例
  19. 资本资产定价模型简介-多因子寻找Alpha统计套利
  20. dropbox被封, 试试金山快盘

热门文章

  1. seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  2. 十进制度转换为度分秒
  3. IIS发布ASP.NET应用常见错误及解决办法
  4. fopen时w与wb的区别
  5. opencv 图像雾检测_OpenCV图像处理-基于OpenPose的关键点检测
  6. Redhat安装gtk2.0和pkg-config
  7. UVA_11922 Permutation Transformer 【splay树】
  8. Localization
  9. [USACO11NOV]牛的障碍Cow Steeplechase(匈牙利算法)
  10. 《随笔记录》20170310