• netty之bootstrap


    转载自https://blog.csdn.net/zxhoo/article/details/17419229

    Netty4学习笔记(2)-- Bootstrap

    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 {
    // 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。

    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就接通服务器,可以收发消息了!

    ---------------------
    作者:zxh0
    来源:CSDN
    原文:https://blog.csdn.net/zxhoo/article/details/17419229
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Key and Certificate Conversion
    openssl
    python http通信实现
    鼠标右键添加cmd
    好文章
    wireshark里无网络接口解决办法
    python垃圾回收
    终于有人把 Docker 讲清楚了
    mongodb的监控与性能优化
    mongodb创建超级用户和普通用户(对应数据库的用户)
  • 原文地址:https://www.cnblogs.com/heroinss/p/10249091.html
Copyright © 2020-2023  润新知