从NIO到netty(8) serverBootstrap#bind()详解

AbstractBootstrap#bind方法

public ChannelFuture bind(int inetPort) {
    return bind(new InetSocketAddress(inetPort));
}

----->

public ChannelFuture bind(SocketAddress localAddress) {
    validate();一些参数的校验
    if (localAddress == null) {
        throw new NullPointerException("localAddress");
    }
    return doBind(localAddress);
}

------->

进入doBind方法之后先分析这句话:

final ChannelFuture regFuture = initAndRegister();

-----》

channel = channelFactory.newChannel();这个channelFacotry就是上一篇分析的ReflectiveChannelFactory,这个直接通过调用该对象的newChannel()创建出一个channel对象。

我们进入到NioServerSocketChannel无参构造器:

public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
      private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
          private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            /**
             *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
             *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
             *
             *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
             */
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }             
//SelectorProvider是jdk提供的一个提供Channel的提供者,java.nio.channels.DatagramChannel、java.nio.channels.Pipe
// java.nio.channels.Selector、java.nio.channels.ServerSocketChannel等Channel都是通过SelectorProvider.provider()
//打开一个通道,但是SelectorProvider.provider()是同步的(有synchronized),netty为了适应在高并发的其工况下,这样的同步会造成性能
//的损失,因此将SelectorProvider.provider()获得的SelectorProvider做成一个名字是DEFAULT_SELECTOR_PROVIDER 的常量,获得通道的时候
//直接使用[ return provider.openServerSocketChannel();]类似这样的用法返回Channel,不会有同步加锁操作,提高了并发,有兴趣的可以看下
//https://github.com/netty/netty/issues/2308 说明,为什么netty这样写。

    /**
     * Create a new instance
     * 无参构造器,主要获取ServerSocketChannel 
     */
    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }              

    /**
     * Create a new instance using the given {@link ServerSocketChannel}.
     * 设置ServerSocketChannel的兴趣事件(初始状态都是SelectionKey.OP_ACCEPT)、ChannelId(唯一的一个编码)、
     * 设置ServerSocketChannel为非阻塞、初始化了ServerSocketChannel的pipline。
     * 
     * config 对ServerSocketChannelConfig进行了赋值。
     */
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

}                     

然后进入init(channel);方法

 final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        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();
            channel.attr(key).set(e.getValue());
        }
    }

    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) {
                pipeline.addLast(handler);
            }

            // We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
            // In this case the initChannel(...) method will only be called after this method returns. Because
            // of this we need to ensure we add our handler in a delayed fashion so all the users handler are
            // placed in front of the ServerBootstrapAcceptor.
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

init方法首先会根据channelOption和attributeKey来初始化channel的一些参数

这里要着重说明下

ChannelOption和AttributeKey

先看ChannelOption

在每个 Channel 创建时都手动配置它可能会变得相当乏味。幸运的是,你不必这样做。相 反,你可以使用 option()方法来将 ChannelOption 应用到引导。你所提供的值将会被自动 应用到引导所创建的所有 Channel

è¿éåå¾çæè¿°

ChannelOption的doc说明
/**
 * A {@link ChannelOption} allows to configure a {@link ChannelConfig} in a type-safe
 * way. Which {@link ChannelOption} is supported depends on the actual implementation
 * of {@link ChannelConfig} and may depend on the nature of the transport it belongs
 * to.
 *
 * @param <T>   the type of the value which is valid for the {@link ChannelOption}
 */
 ChannelOption是一种以一种安全的方式配置ChannelConfig,ChannelOption支持的类型个依赖于ChannelConfig的实际类型
 和他所属的传输层的本质。
 T 类型是ChannelOption的值得类型
public class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {
    private static final ConstantPool<ChannelOption<Object>> pool = new ConstantPool<ChannelOption<Object>>() {
        protected ChannelOption<Object> newConstant(int id, String name) {
            return new ChannelOption<Object>(id, name);
        }
    };
    ...略
ChannelOption的主要作用是用来存在TCP之类的传输层的一些协议的参数,比如:

public static final ChannelOption<Boolean> SO_BROADCAST = valueOf("SO_BROADCAST");
    public static final ChannelOption<Boolean> SO_KEEPALIVE = valueOf("SO_KEEPALIVE");
    public static final ChannelOption<Integer> SO_SNDBUF = valueOf("SO_SNDBUF");
    public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");
    public static final ChannelOption<Boolean> SO_REUSEADDR = valueOf("SO_REUSEADDR");
    public static final ChannelOption<Integer> SO_LINGER = valueOf("SO_LINGER");
    public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");
    public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");

 

注意到ChannelOption中有一个成员变量

private static final ConstantPool<ChannelOption<Object>> pool = new ConstantPool<ChannelOption<Object>>() {
    @Override
    protected ChannelOption<Object> newConstant(int id, String name) {
        return new ChannelOption<Object>(id, name);
    }
};

ConstantPool内部有一个

private final ConcurrentMap<String, T> constants = PlatformDependent.newConcurrentHashMap();实际上就是一个ConcurrentHashMap用来保存一些常量。

valueOf("SO_TIMEOUT");进入可以看到调用的是constantPool#valueOf

public static <T> ChannelOption<T> valueOf(String name) {
    return (ChannelOption<T>) pool.valueOf(name);
}

----->

public T valueOf(String name) {
    checkNotNullAndNotEmpty(name);
    return getOrCreate(name);
}

最终会进入

private T getOrCreate(String name) {
    T constant = constants.get(name);
    if (constant == null) {
        final T tempConstant = newConstant(nextId(), name);
        constant = constants.putIfAbsent(name, tempConstant);
        if (constant == null) {
            return tempConstant;
        }
    }

    return constant;
}

可以发现先从常量池中获取,如果获取不到就创建一个新的常量,并放在concurenntHashMap中

-------

回到setChannelOpitons

synchronized (options) {
    setChannelOptions(channel, options, logger);
}

----->

for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
    setChannelOption(channel, e.getKey(), e.getValue(), logger);
}

这里会遍历传入的channelOpitons,并通过

channel.config()

获取到channel对应的ChannelConfig,把值设置上去。

DefaultChannelConfig#setOption

if (option == CONNECT_TIMEOUT_MILLIS) {
    setConnectTimeoutMillis((Integer) value);
} else if (option == MAX_MESSAGES_PER_READ) {
    setMaxMessagesPerRead((Integer) value);
} else if (option == WRITE_SPIN_COUNT) {
    setWriteSpinCount((Integer) value);
} else if (option == ALLOCATOR) {
    setAllocator((ByteBufAllocator) value);
} else if (option == RCVBUF_ALLOCATOR) {
    setRecvByteBufAllocator((RecvByteBufAllocator) value);
} else if (option == AUTO_READ) {
    setAutoRead((Boolean) value);
} else if (option == AUTO_CLOSE) {
    setAutoClose((Boolean) value);
} else if (option == WRITE_BUFFER_HIGH_WATER_MARK) {
    setWriteBufferHighWaterMark((Integer) value);
} else if (option == WRITE_BUFFER_LOW_WATER_MARK) {
    setWriteBufferLowWaterMark((Integer) value);
} else if (option == WRITE_BUFFER_WATER_MARK) {
    setWriteBufferWaterMark((WriteBufferWaterMark) value);
} else if (option == MESSAGE_SIZE_ESTIMATOR) {
    setMessageSizeEstimator((MessageSizeEstimator) value);
} else if (option == SINGLE_EVENTEXECUTOR_PER_GROUP) {
    setPinEventExecutorPerGroup((Boolean) value);
} else {
    return false;
}

再看AttributeKey

Netty 应用程序通常与组织的专有软件集成在一起,而像 Channel 这样的组件可能甚至会在 正常的 Netty 生命周期之外被使用。在某些常用的属性和数据不可用时,Netty 提供了 AttributeMap 抽象(一个由 Channel 和引导类提供的集合)以及 AttributeKey<T>(一 个用于插入和获取属性值的泛型类)。使用这些工具,便可以安全地将任何类型的数据项与客户 端和服务器 Channel(包含 ServerChannel 的子 Channel)相关联了。实际上可以理解成一个上下文

 

è¿éåå¾çæè¿°

可以看到它和ChannelOption的上层结构是一样的。

对比Attribute ,AttributeKey, AttributeMap

Attribute 中维护了 AttributeKey 对象作为key,以及一个T类型的value, 实现Attribute接口的方法也必须是线程安全的。

AttributeMap 接口当中只提供了 attr() ,也就是 AttributeKey 对象,以及是否存在 attributeKey 的方法。

Channel对象是继承自AttributeMap 对象,所以channel.attr().set(value) 实际上就是通过attributeMap当中的attr获取到Attribute对象,然后再set值进去。

下面摘自netty实战中channelOpiton和AttributeKey的一段代码

//创建一个 AttributeKey 以标识该属性
  final AttributeKey<Integer> id = new AttributeKey<Integer>("ID");
    Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new

    NioEventLoopGroup()).

    channel(NioSocketChannel .class)
.

    handler(
new SimpleChannelInboundHandler<ByteBuf>() {
        @Override
        public void channelRegistered (ChannelHandlerContext ctx)
throws Exception {
//使用 AttributeKey 检索 属性以及它的值
            Integer idValue = ctx.channel().attr(id).get(); // do something with the idValue
        }
        @Override
        protected void channelRead0 (
            ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
            System.out.println("Received data");
        }
);
//
    } bootstrap.option(ChannelOption.SO_KEEPALIVE,true)
        .
// 设置 ChannelOption, 其将在 connect()或者 bind()方法被调用时 被设置到已经创建的 Channel 上
    option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
// 存储该 id 属性
   bootstrap.attr(id,123456);
    ChannelFuture future = bootstrap.connect(
        new InetSocketAddress("www.manning.com", 80)); future.syncUninterruptibly();


回到init方法:

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

在这里可以看到加入了一个ServerBootstrapAcceptor。要说明这个,我们先要说明下Reactor模式,下一篇说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值