`
Donald_Draper
  • 浏览: 984408 次
社区版块
存档分类
最新评论

netty ServerBootStrap解析

阅读更多
netty 抽象BootStrap定义:http://donald-draper.iteye.com/blog/2392492
引言:
前面一篇文章我们看了抽象Bootstrap的定义,先来回顾一下:
   抽象引导程序AbstractBootstrap,内部关联的一个事件循环组EventLoopGroup,一个通道处理器ChannelHandler,一个通道选项集和一个本地Socket地址及一个通道属性集。内部的方法主要配置事件循环组,通道处理,通道选项集,socket地址,及通道属性,通道注册,地址绑定。注册通道到事件循环组,首先由通道工厂创建通道实例,然后初始化通道,初始化工作待子类实现;然后将实际注册工作委托给事件循环组。绑定定socket地址,首先注册通道到事件循环组,待注册完成时,创建一个绑定任务线程完成地址绑定,实际将地址绑定工作委托给通道,并将绑定任务线程交由通道关联的事件循环的事件执行器执行。

今天我们来看服务端Bootstrap:
package io.netty.bootstrap;

import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.util.AttributeKey;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
 *
 */
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
    //通道选项及属性集
    private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);//当前配置
    private volatile EventLoopGroup childGroup;//事件循环组
    private volatile ChannelHandler childHandler;//通道处理器
    public ServerBootstrap() { }
    private ServerBootstrap(ServerBootstrap bootstrap) {
        super(bootstrap);
        childGroup = bootstrap.childGroup;
        childHandler = bootstrap.childHandler;
        synchronized (bootstrap.childOptions) {
            childOptions.putAll(bootstrap.childOptions);
        }
        synchronized (bootstrap.childAttrs) {
            childAttrs.putAll(bootstrap.childAttrs);
        }
    }
}

从上面可看出,服务端Bootstrap虽然继承与抽象Bootstrap,但他有自己的通道选项及属性集,事件循环组和通道处理器。
/**
 * Specify the {@link EventLoopGroup} which is used for the parent (acceptor) and the child (client).
 父监听器和孩子客户端同用一个事件循环组
 */
@Override
public ServerBootstrap group(EventLoopGroup group) {
    return group(group, group);
}

/**
 * Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
 * {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
 * {@link Channel}'s.
 设置父监听器和孩子客户端的事件循环,两者用于处理SeverChannel和Channel的所有事件循环
 */
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    super.group(parentGroup);
    if (childGroup == null) {
        throw new NullPointerException("childGroup");
    }
    if (this.childGroup != null) {
        throw new IllegalStateException("childGroup set already");
    }
    this.childGroup = childGroup;
    return this;
}

大胆猜测一下,parentGroup事件循环组用于监听器ServerChannel接受连接,childGroup事件循环组用于当Server通道接收客户端的连接时,产生一个通道用于与客户端交互,childGroup事件循环组用于处理与客户端交互的通道相关事件和IO操作。

/**
 * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they get created
 * (after the acceptor accepted the {@link Channel}). Use a value of {@code null} to remove a previous set
 * {@link ChannelOption}.
 用于Server通道接收客户端的连接时,产生的通道选项配置
 */
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    if (childOption == null) {
        throw new NullPointerException("childOption");
    }
    if (value == null) {
        synchronized (childOptions) {
            childOptions.remove(childOption);
        }
    } else {
        synchronized (childOptions) {
            childOptions.put(childOption, value);
        }
    }
    return this;
}

从这个方法来看,上面的猜测是正确的。


/**
 * Set the specific {@link AttributeKey} with the given value on every child {@link Channel}. If the value is
 * {@code null} the {@link AttributeKey} is removed
 客户端交互通道属性配置
 */
public <T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value) {
    if (childKey == null) {
        throw new NullPointerException("childKey");
    }
    if (value == null) {
        childAttrs.remove(childKey);
    } else {
        childAttrs.put(childKey, value);
    }
    return this;
}

/**
 * Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
配置与客户端交互通道的通道处理器
 */
public ServerBootstrap childHandler(ChannelHandler childHandler) {
    if (childHandler == null) {
        throw new NullPointerException("childHandler");
    }
    this.childHandler = childHandler;
    return this;
}
//验证通道配置是否有效
@Override
public ServerBootstrap validate() {
    super.validate();
    if (childHandler == null) {
        throw new IllegalStateException("childHandler not set");
    }
    if (childGroup == null) {
        logger.warn("childGroup is not set. Using parentGroup instead.");
        childGroup = config.group();
    }
    return this;
}
//获取当前Server引导配置
@Override
public final ServerBootstrapConfig config() {
    return config;
}

下面我们来看初始化通道,这个是重点:
@Override
void init(Channel channel) throws Exception {
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        //设置父Server通道选项
        setChannelOptions(channel, options, logger);
    }

    final Map<AttributeKey<?>, Object> attrs = attrs0();
    synchronized (attrs) {
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
            @SuppressWarnings("unchecked")
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            //设置父Server通道属性
            channel.attr(key).set(e.getValue());
        }
    }
   //获取Server通道的Channel管道
    ChannelPipeline p = channel.pipeline();
    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();
            ChannelHandler handler = config.handler();
            if (handler != null) {
	        //将通道处理器添加到通道内部的Channel管道内
                pipeline.addLast(handler);
            }
            
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
		    //将Server引导配置监听器添加到通道内部的Channel管道内
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

我们来看引导配置监听器,实际为一个Inbound通道处理器
 private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
     private final EventLoopGroup childGroup;//与客户端交互通道注册的事件循环组
     private final ChannelHandler childHandler;//与客户端交互通道的通道处理器
     private final Entry<ChannelOption<?>, Object>[] childOptions;//与客户端交互通道的选项配置
     private final Entry<AttributeKey<?>, Object>[] childAttrs;//与客户端交互通道的属性
     private final Runnable enableAutoReadTask;

     ServerBootstrapAcceptor(
             final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
             Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
         this.childGroup = childGroup;
         this.childHandler = childHandler;
         this.childOptions = childOptions;
         this.childAttrs = childAttrs;

         // Task which is scheduled to re-enable auto-read.
         // It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may
         // not be able to load the class because of the file limit it already reached.
         // 此任务用于开启通道自动读取配置,将会被所在的事件循环调度。
         // See https://github.com/netty/netty/issues/1328
         enableAutoReadTask = new Runnable() {
             @Override
             public void run() {
	         //开启通道自动读取配置
                 channel.config().setAutoRead(true);
             }
         };
     }

     @Override
     @SuppressWarnings("unchecked")
     public void channelRead(ChannelHandlerContext ctx, Object msg) {
         //与客户端交互通道
         final Channel child = (Channel) msg;
         //配置与客户端交互通道的通道处理器
         child.pipeline().addLast(childHandler);
         //配置与客户端交互通道的选项
         setChannelOptions(child, childOptions, logger);

         for (Entry<AttributeKey<?>, Object> e: childAttrs) {
	    //配置与客户端交互通道的属性
             child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
         }

         try {
	    //注册与客户端交互通道到childGroup事件循环组
             childGroup.register(child).addListener(new ChannelFutureListener() {
                 @Override
                 public void operationComplete(ChannelFuture future) throws Exception {
                     if (!future.isSuccess()) {
		         //注册失败,则关闭通道
                         forceClose(child, future.cause());
                     }
                 }
             });
         } catch (Throwable t) {
             forceClose(child, t);
         }
     }
      //关闭通道
     private static void forceClose(Channel child, Throwable t) {
         child.unsafe().closeForcibly();
         logger.warn("Failed to register an accepted channel: {}", child, t);
     }

     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
         final ChannelConfig config = ctx.channel().config();
         if (config.isAutoRead()) {
             // stop accept new connections for 1 second to allow the channel to recover
	     //发生异常,则停止接受连接请求1秒钟,允许通道恢复
             // See https://github.com/netty/netty/issues/1328
             config.setAutoRead(false);
             ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
         }
         // still let the exceptionCaught event flow through the pipeline to give the user
         // a chance to do something with it
	 //触发异常
         ctx.fireExceptionCaught(cause);
     }
 }


//ChannelConfig
/**
  * Sets if {@link ChannelHandlerContext#read()} will be invoked automatically so that a user application doesn't
  * need to call it at all. The default value is {@code true}.
  */
 ChannelConfig setAutoRead(boolean autoRead);



回到服务端引导配置:
 ServerBootstrap serverBoot = new ServerBootstrap(); 
            serverBoot.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) 
	   ...
ChannelFuture f = serverBoot.bind(inetSocketAddress).sync();


结合前一篇定义抽象Bootstrap定义,我们来理一下ServerBootstrap绑定,完成的任务:
Server引导配置绑定socket地址,首先初始化通道,对于Server引导配置,这个通道为
NioServerSocketChannel,初始化通道,即初始化Server通道,从上面的初始化通道方法来看,初始化通道,首先将Server引导配置的父类抽象Bootstrap的选项和属性配置给Server通道,然后将ServerBootstrapAcceptor添加到Server通道内部的Channel管道内,然后将Server通道注册到parentGroup事件循环组中,然后通过Server通道#bind方法完成实际socket地址;
Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,
则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。

再来看其他方法:
//创建属性和选项组
@SuppressWarnings("unchecked")
private static Entry<AttributeKey<?>, Object>[] newAttrArray(int size) {
    return new Entry[size];
}
@SuppressWarnings("unchecked")
private static Map.Entry<ChannelOption<?>, Object>[] newOptionArray(int size) {
    return new Map.Entry[size];
}

下面几个方法,没有什么好讲的,很简单:
@Override
@SuppressWarnings("CloneDoesntCallSuperClone")
public ServerBootstrap clone() {
    return new ServerBootstrap(this);
}

/**
 * Return the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
 * if non is configured yet.
 *
 * @deprecated Use {@link #config()} instead.
 */
@Deprecated
public EventLoopGroup childGroup() {
    return childGroup;
}

final ChannelHandler childHandler() {
    return childHandler;
}

final Map<ChannelOption<?>, Object> childOptions() {
    return copiedMap(childOptions);
}

final Map<AttributeKey<?>, Object> childAttrs() {
    return copiedMap(childAttrs);
}

总结:

      服务端Bootstrap虽然继承与抽象Bootstrap,但他有自己的child通道选项及属性集,事件循环组和通道处理器,这些是用于配置,当Server通道接收客户端的请求,创建与客户端交互的通道。当构造Server引导配置时,如果传递一个事件循环,则Server通道监听器和客户端交互的通道公用一个事件循环组,否则parentGroup事件循环组用于监听器ServerChannel接受连接,childGroup事件循环组用于处理与客户端交互的通道相关事件和IO操作。
      Server引导配置绑定socket地址,首先初始化通道,对于Server引导配置,这个通道为NioServerSocketChannel,初始化通道,即初始化Server通道;初始化Server通道,首先将Server引导配置的父类抽象Bootstrap的选项和属性配置给Server通道,然后添加ServerBootstrapAcceptor到Server通道内部的Channel管道内,然后将Server通道注册到事件循环组parentGroup中,然后通过Server通道#bind方法完成实际socket地址;Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。
0
0
分享到:
评论

相关推荐

    netty源码解析视频

    ### Netty源码解析知识点概览 #### 一、Netty简介与应用场景 - **Netty**是一款由JBOSS提供的高性能的异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。 - **应用场景**:Netty广泛...

    Netty源码解析-服务启动过程.pdf

    ### Netty源码解析——服务启动过程 #### 一、Netty概述 Netty是一个高性能、异步事件驱动的网络应用框架,它被广泛应用于快速开发高性能协议服务器和客户端。Netty通过高度优化的设计和实现提供了低延迟和高吞吐...

    基于netty5的自定义协议Demo

    在本Demo中,我们会在`ServerBootstrap`的配置中设置编码器和解码器,确保它们在处理luck协议的数据时被正确调用。这通常涉及`pipeline().addLast()`方法,将我们的luck协议编码器和解码器实例添加到管道中。 总结...

    Netty3.x 源码解析

    使用Netty时,开发者只需实现具体的Handler来处理业务逻辑,而像协议解析、线程管理等复杂的操作都由Netty框架内部处理。 以Netty中的一个简单EchoServer为例,这个服务器能够接收客户端发送的消息并将其回显给...

    Java采用Netty实现基于DTU的TCP服务器 + 多端口 + 多协议

    本文将深入探讨如何使用Java的Netty框架实现一个基于DTU(Data Transfer Unit)的TCP服务器,该服务器具备多端口通信和多协议解析的能力。 首先,DTU是一种专门用于远程数据传输的设备,它能够通过GPRS、3G/4G等...

    Netty3.x源码解析.docx

    Netty 的代码结构包括了 ServerBootstrap、ChannelPipeline、ChannelHandlerContext 等核心模块,这些模块共同构成了 Netty 的骨干。 Netty 的应用 Netty 的应用非常广泛,包括了网络游戏、实时通信、数据传输等...

    c++客户端和java(Netty)服务器端tcp通讯

    利用Netty,我们可以轻松实现TCP服务器,创建ServerBootstrap,配置通道处理器pipeline,监听客户端连接,并处理接收到的数据。 Netty中的ChannelHandlerContext类是事件处理的核心,它提供了发送和接收数据的方法...

    基于netty实现的web框架

    1. **Netty服务器端**: 项目可能包含一个启动类,使用Netty的ServerBootstrap来配置并启动服务器。Netty的ChannelInitializer会注册各种处理器(Handler)到管道(Pipeline),这些处理器用于处理进来的连接、解码...

    netty+4G DTU

    1. **配置 Netty 服务器**:创建 ServerBootstrap,设置 BossGroup 和 WorkerGroup,选择合适的 NIO 或 EPOLL 模型,以及绑定监听端口。 2. **实现 ChannelInitializer**:在 ChannelInitializer 中,添加自定义的 ...

    基于netty的服务器客户端收发消息代码

    在Netty中,我们创建一个`ServerBootstrap`实例,配置服务端的线程模型、处理链(ChannelPipeline)以及绑定的端口。服务器的主要任务是接收连接请求,建立连接,并在连接建立后将数据通道(Channel)添加到管道中,...

    基于netty编写的socket服务端

    ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer() { @Override public void initChannel...

    Netty-API-文档中文版

    通过“Netty-API-文档中文版”,你可以详细了解到如何创建ServerBootstrap、Bootstrap、ChannelFuture、ChannelInboundHandlerAdapter等核心组件,以及如何配置和使用各种编解码器。文档还会介绍如何进行连接管理、...

    Netty example

    2. **src/main/resources**: 可能包含配置文件,如 Netty 的 ServerBootstrap 配置,定义了服务器启动时的参数,如绑定的端口、使用的 EventLoopGroup 和 Channel 类型等。 3. **pom.xml**: Maven 构建文件,列出了...

    netty源码深入分析

    《Netty源码深入分析》是由美团基础架构部的闪电侠老师所分享的一系列关于Netty源码解析的视频教程。以下将根据标题、描述、标签以及部分内容等信息,对Netty及其源码进行深入剖析。 ### Netty简介 Netty是基于...

    Netty权威指南-Netty源码

    源码分析时,首先需要关注的是 Netty 的启动流程,这通常从 `ServerBootstrap` 类开始。ServerBootstrap 配置了 EventLoopGroup(包含多个 EventLoop)和 Channel 实例,如 NioServerSocketChannel。然后通过绑定...

    netty 代码.rar

    - **编解码**:例如,Netty 提供的 LengthFieldBasedFrameDecoder 可以解析带有长度字段的协议,适合即时通讯中的消息传输。 - **WebSocket 支持**:Netty 内置 WebSocket 协议支持,可用于构建实时双向通信服务。...

    netty权威指南 第二版 李林锋pdf

    《Netty权威指南》第二版是由李林锋编著的一本深入解析Netty框架的专业书籍。Netty是一款高性能、异步事件驱动的网络应用程序框架,广泛应用于开发高并发、低延迟的网络应用,如分布式系统、游戏服务器、云计算平台...

    spring boot 整合的netty 实现的socket的服务端和客户端

    在`netty_server`项目中,我们需要创建一个`ServerBootstrap`实例,它是Netty服务器的核心。通过`ServerBootstrap`,我们可以配置服务器的参数,如绑定的端口、使用的事件循环组等。例如: ```java ServerBootstrap...

Global site tag (gtag.js) - Google Analytics