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模式,下一篇说明。