Netty4學習筆記(2)-- Bootstrap

etty4的代碼比我想象的要複雜的多,不過Netty4很好的將這種複雜性隱藏了起來,暴露出來的,是一個相對容易使用的接口。Bootstrap就是Netty試圖隱藏這種複雜性的一個例子。java

 

bootstrap包

 

bootstrap包是Netty4代碼裏最簡單的一個包,總共只有4個類:bootstrap

 

 

Bootstrap繼承結構

AbstractBootstrap是抽象類,有兩個具體的實現,Bootstrap和ServerBootstrap:promise

 

 

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 {  
        // 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 {  
                     ch.pipeline().addLast(  
                             //new LoggingHandler(LogLevel.INFO),  
                             new EchoClientHandler(firstMessageSize));  
                 }  
             });  
  
            // 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();  
        }  
    }  
  
    public static void main(String[] args) throws Exception {  
    // ...  
    }  
}  

Builder模式?

看上面的例子,Bootstrap的使用很像Builder模式,Bootstrap就是Builder,EventLoopGroup、Channel和Handler等是各類Part。稍有不一樣的是,準備好各類Part後,並非直接build出一個Product來,而是直接經過connect()方法使用這個Product。ide

 

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,以下圖所示:oop

 

 

設置各個Part

AbstractBootstrap有一組方法用來設置各個Part,例以下面這些:ui

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,以下:this

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實現:spa

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就接通服務器,能夠收發消息了!

相關文章
相關標籤/搜索