Netty服务端和客户端创建的源码分析

<h2>服务端</h2>

以下是Netty官方的一个Echo服务示例:

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(new EchoServerHandler());
                 }
             });
  
            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();
  
            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
        
    }
}

上面代码创建Server端服务的流程如下:

  1. 创建ServerBootstrap实例
  2. 设置EventLoopGroup
  3. 设置创建的Channel类型
  4. option配置属性
  5. 设置Handler,处理请求
  6. 设置ChildHandler,处理对应channel的请求
  7. 通过bind创建Chnnel并绑定,启动服务

服务端创建时序图

服务端重要组件
ServerBootstrap
ServerBootstrap是Netty服务端的启动辅助类,提供一系列的方法用于设置服务端的参数和配置,简化开发。(衍生一点:ServerBootstrap的构造方法是无参的,因为参数太多所以采用了Builder模式)

继承自AbstractBootstrap,核心属性有childGroup和childHandler。

  • childGroup:负责调度和执行客户端的接入、网络读写事件的处理、用户自定义任务和定时任务的执行
  • childHandler:自定义的业务Handler

AbstractBootstrap核心属性有group和handler。

  • group:处理客户端的链接请求,并转交给childGroup(读取的数据是穿件的NioSocketChannel)

Reactor线程池
Netty的Reactor线程池是EventLoopGroup,实际上是一个EventLoop的数组。

EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel,Selector的轮训操作有EventLoop线程run方法驱动。

另外用户自定义的Task和定时任务Task也由统一的EventLoop负责处理。

Channel
作为Nio服务,需要创建ServerSocketChannel,Netty对原生NIO类库做了封装,对应实现类为NioServerSocketChannel。

用户只需要制定Channel的实现类型,内部通过反射机制来创建对应的实例。

因为只在监听端口时创建,所以反射的性能影响并不大。

ChannelPipeline
ChannelPipeline是网络事件处理的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转。

ChannelHandler
ChannelHandler是提供给用户定制和扩展的关键接口,包括编解码,业务处理等都是通过ChannelHandler进行的。

Selector
Selector轮训操作由NioEventLoop调度和执行,选择准备就绪的Channel集合。

NioServerSocketChannel
绑定Server端地址的Server,读取客户端的链接请求(只有一个,在bind时创建)。

NioSocketChannel
和客户端之间的链接。

服务端线程模型

  • mainReactor:parentGroup
  • subReactor:childGroup
  • ThreadPool:如果没指定,使用childGroup执行,如果指定了则是业务线程(执行业务Handler的线程)

Handler模型

Server启动的关键流程

  1. bind操作创建了NioServerSocketChannel并注册到NioEventLoop中(parent group中会有一个线程执行selector的轮训操作)

Server端的请求接入流程

  1. NioEventLoop轮训到到就绪时间后,调用Unsafe.read(NioMessageUnsafe实现)创建NioSocketChannel并传递到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
  2. ServerBootstrapAcceptor的channelRead方法将NioSocketChannel注册到childGroup的Selector上(实现代码是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group处理,这点和NioServerSocketChannel的注册、处理是一样的;和上面的线程模型也是呼应的:两个reactor)。

<h2>客户端</h2>

public static void main(String[] args) throws Exception {
    // Configure SSL.git
    final SslContext sslCtx;
    if (SSL) {
        sslCtx = SslContextBuilder.forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
    } else {
        sslCtx = null;
    }

    // Configure the client.
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioSocketChannel.class)
         .option(ChannelOption.TCP_NODELAY, true)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 if (sslCtx != null) {
                     p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                 }
                 //p.addLast(new LoggingHandler(LogLevel.INFO));
                 p.addLast(new EchoClientHandler());
             }
         });

        // Start the client.
        ChannelFuture f = b.connect(HOST, PORT).sync();

        // Wait until the connection is closed.
        f.channel().closeFuture().sync();
    } finally {
        // Shut down the event loop to terminate all threads.
        group.shutdownGracefully();
    }
}

创建客户端的大致流程:

  1. 创建Bootstrap实例
  2. 设置EventLoop
  3. 指定Channel类型
  4. option配置
  5. 指定Handler
  6. connect

客户端创建时序图

客户端和服务端的模式基本一致,由线程轮训Selector的事件,由Pipeline进行事件传递,EventLoop进行处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容