Netty4的代码比我想象的要复杂的多,不过Netty4很好的将这种复杂性隐藏了起来,暴露出来的,是一个相对容易使用的接口。Bootstrap就是Netty试图隐藏这种复杂性的一个例子。
bootstrap包
bootstrap包是Netty4代码里最简单的一个包,总共只有4个类:
Bootstrap继承结构
AbstractBootstrap是抽象类,有两个具体的实现,Bootstrap和ServerBootstrap:
Bootstrap例子
Netty4自带的例子里有一个EchoClient,看看它是如何使用Bootstrap启动一个客户端程序的:
- public class EchoClient {
-
- private final String host;
- private final int port;
- private final int firstMessageSize;
-
- public EchoClient(String host, int port, int firstMessageSize) {
- this.host = host;
- this.port = port;
- this.firstMessageSize = firstMessageSize;
- }
-
- public void run() throws Exception {
-
- 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 {
- ch.pipeline().addLast(
-
- new EchoClientHandler(firstMessageSize));
- }
- });
-
-
- ChannelFuture f = b.connect(host, port).sync();
-
-
- f.channel().closeFuture().sync();
- } finally {
-
- group.shutdownGracefully();
- }
- }
-
- public static void main(String[] args) throws Exception {
-
- }
- }
Builder模式?
看上面的例子,Bootstrap的使用很像Builder模式,Bootstrap就是Builder,EventLoopGroup、Channel和Handler等是各种Part。稍有不同的是,准备好各种Part后,并不是直接build出一个Product来,而是直接通过connect()方法使用这个Product。
AbstractBootstrap
为了弄清楚Bootstrap如何工作,我们先从AbstractBootstrap入手:
- public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
-
- private volatile EventLoopGroup group;
- private volatile ChannelFactory<? extends C> channelFactory;
- private volatile SocketAddress localAddress;
- private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
- private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
- private volatile ChannelHandler handler;
-
- }
可以看到,AbstractBootstrap这个抽象Builder一共需要有6个Part,如下图所示:
设置各个Part
AbstractBootstrap有一组方法用来设置各个Part,例如下面这些:
- public B group(EventLoopGroup group)
- public B channel(Class<? extends C> channelClass)
- public B channelFactory(ChannelFactory<? extends C> channelFactory)
- public B localAddress(SocketAddress localAddress)
- public <T> B option(ChannelOption<T> option, T value)
- public <T> B attr(AttributeKey<T> key, T value)
- public B handler(ChannelHandler handler)
还有一组对应方法获得各个Part,如下:
- final SocketAddress localAddress()
- final ChannelFactory<? extends C> channelFactory()
- final ChannelHandler handler()
- public final EventLoopGroup group()
- final Map<ChannelOption<?>, Object> options()
- final Map<AttributeKey<?>, Object> attrs()
ChannelFactory
AbstractBootstrap通过ChannelFactory创建Channel实例,channel(channelClass)方法看起来好像是设置了一个Channel,但实际上只是设置了默认的ChannelFactory实现:
- public B channel(Class<? extends C> channelClass) {
- if (channelClass == null) {
- throw new NullPointerException("channelClass");
- }
- return channelFactory(new BootstrapChannelFactory<C>(channelClass));
- }
默认的ChannelFactory实现使用反射创建Channel实例:
- private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {
- private final Class<? extends T> clazz;
-
- BootstrapChannelFactory(Class<? extends T> clazz) {
- this.clazz = clazz;
- }
-
- @Override
- public T newChannel() {
- try {
- return clazz.newInstance();
- } catch (Throwable t) {
- throw new ChannelException("Unable to create Channel from class " + clazz, t);
- }
- }
- }
Bootstrap.connect()
再来看Bootstrap类的connect()方法:
- public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
- if (remoteAddress == null) {
- throw new NullPointerException("remoteAddress");
- }
- validate();
- return doConnect(remoteAddress, localAddress);
- }
connect()方法调用validate()方法看各个Part是否准备就绪,然后调用doConnect()方法:
- private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
- final ChannelFuture regFuture = initAndRegister();
- final Channel channel = regFuture.channel();
- if (regFuture.cause() != null) {
- return regFuture;
- }
-
- final ChannelPromise promise = channel.newPromise();
- if (regFuture.isDone()) {
- doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
- } else {
- regFuture.addListener(new ChannelFutureListener() {
- @Override
- public void operationComplete(ChannelFuture future) throws Exception {
- doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
- }
- });
- }
-
- return promise;
- }
doConnect()方法首先调用了initAndRegister()方法,然后又调用了doConnect0()方法,方法调用示意图如下:
AbstractBootstrap.initAndRegister()
- final ChannelFuture initAndRegister() {
- final Channel channel = channelFactory().newChannel();
- try {
- init(channel);
- } catch (Throwable t) {
- channel.unsafe().closeForcibly();
- return channel.newFailedFuture(t);
- }
-
- ChannelPromise regPromise = channel.newPromise();
- group().register(channel, regPromise);
-
- }
initAndRegister()方法用ChannelFactory创建了一个Channel的实例,然后调用init()方法初始化Channel,最后将Channel注册到EventLoopGroup上:
而Channel在实例化的时候已经自动关联了Pipeline,这点从AbstractChannel的构造函数可以看出:
- protected AbstractChannel(Channel parent) {
- this.parent = parent;
- unsafe = newUnsafe();
- pipeline = new DefaultChannelPipeline(this);
- }
Bootstrap.init()方法
- void init(Channel channel) throws Exception {
- ChannelPipeline p = channel.pipeline();
- p.addLast(handler());
-
- }
Bootstrap.init()方法把Handler添加到了Pipeline的末尾,到这里,Channel就准备就绪了:
继续Bootstrap.doConnect()
initAndRegister()方法结束之后,doConnect()方法紧接着调用了doConnect0()方法,doConnect0()方法继而调用了Channel.connect()方法,这样Channel就接通服务器,可以收发消息了!
这篇文章接着上一篇,分析一下Netty4的ServerBootstrp是如何工作的。
EchoServer
先看看Netty自带的EchoServer例子:
-
-
-
- public class EchoServer {
-
- private final int port;
-
- public EchoServer(int port) {
- this.port = port;
- }
-
- public void run() throws Exception {
-
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- 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 {
- ch.pipeline().addLast(
-
- new EchoServerHandler());
- }
- });
-
-
- ChannelFuture f = b.bind(port).sync();
-
-
- f.channel().closeFuture().sync();
- } finally {
-
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
-
- public static void main(String[] args) throws Exception {
- int port;
- if (args.length > 0) {
- port = Integer.parseInt(args[0]);
- } else {
- port = 8080;
- }
- new EchoServer(port).run();
- }
- }
可以看出,用法和Bootstrap差不多。
作为Builder的ServerBootstrap
- public final class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
-
- private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
- private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
- private volatile EventLoopGroup childGroup;
- private volatile ChannelHandler childHandler;
-
- }
看代码可以知道,ServerBootstrap比它的超类多了四个Part,如下图所示:
从bind()方法入手
bind()方法实际上是在AbstractBootstrap里定义的,bind()先调用validate()方法检查各个Part是否准备就绪,然后把工作交给了doBind()方法。doBind()方法首先调用initAndRegister()方法,然后把工作交给doBind0()。initAndRegister()会调用模板方法init()来初始化Channel,initAndRegister()方法的细节上篇文章分析过了,这里不再复述。下面是整个方法调用过程的示意图:
init()方法
- @Override
- void init(Channel channel) throws Exception {
- 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());
- }
- }
-
- ChannelPipeline p = channel.pipeline();
- if (handler() != null) {
- p.addLast(handler());
- }
-
- 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(Channel ch) throws Exception {
- ch.pipeline().addLast(new ServerBootstrapAcceptor(
- currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
- }
- });
- }
ServerBootstrap类的init()方法虽然很长,但是却不难理解。首先是给Channel设置options和attrs,然后把User提供的针对NioServerSocketChannel的Handler添加到Pipeline的末尾。接下来复制childOptions和childAttrs,最后实例化一个ChannelInitializer,添加到Pipeline的末尾。init()方法执行完毕之后,AbstractBootstrap的initAndRegister()方法会将NioServerSocketChannel注册到group里。到此为止,NioServerSocketChannel的状态如下图所示:
ChannelInitializer在channelRegistered事件触发后会调用initChannel()方法,随后把自己从Pipeline里删除:
- @Sharable
- public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
-
- private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);
-
-
-
-
-
-
-
-
- protected abstract void initChannel(C ch) throws Exception;
-
- @SuppressWarnings("unchecked")
- @Override
- public final void channelRegistered(ChannelHandlerContext ctx)
- throws Exception {
- boolean removed = false;
- boolean success = false;
- try {
- initChannel((C) ctx.channel());
- ctx.pipeline().remove(this);
- removed = true;
- ctx.fireChannelRegistered();
- success = true;
- } catch (Throwable t) {
- logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
- } finally {
- if (!removed) {
- ctx.pipeline().remove(this);
- }
- if (!success) {
- ctx.close();
- }
- }
- }
- }
所以注册之后的NioServerSocketChannel状态如下图所示:
ServerBootstrapAcceptor
- 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;
-
- @SuppressWarnings("unchecked")
- ServerBootstrapAcceptor(
- EventLoopGroup childGroup, ChannelHandler childHandler,
- Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
- this.childGroup = childGroup;
- this.childHandler = childHandler;
- this.childOptions = childOptions;
- this.childAttrs = childAttrs;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
- Channel child = (Channel) msg;
-
- child.pipeline().addLast(childHandler);
-
- for (Entry<ChannelOption<?>, Object> e: childOptions) {
- try {
- if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
- logger.warn("Unknown channel option: " + e);
- }
- } catch (Throwable t) {
- logger.warn("Failed to set a channel option: " + child, t);
- }
- }
-
- for (Entry<AttributeKey<?>, Object> e: childAttrs) {
- child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
- }
-
- try {
- childGroup.register(child);
- } catch (Throwable t) {
- child.unsafe().closeForcibly();
- logger.warn("Failed to register an accepted channel: " + child, t);
- }
- }
-
- }
ServerBootstrapAcceptor在channelRead事件触发的时候(也就有客户端连接的时候),把childHandler加到childChannel Pipeline的末尾,设置childHandler的options和attrs,最后把childHandler注册进childGroup,如下图所示: