netty源码深入研究(从客户端入手)第一篇

研究源码的前提建立在:

1、首先我们得知道怎么用这个框架

2、其次以用法为入口进行源码研究

ok,我们首先找到入口函数,官方文档有详细介绍,官方地址:点击打开链接,官方文档有提供的详细例子:

 

public static void main(String[] args) throws Exception {
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            Bootstrap b = new Bootstrap(); // (1)
            b.group(workerGroup); // (2)
            b.channel(NioSocketChannel.class); // (3)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });
            
            // Start the client.
            ChannelFuture f = b.connect(host, port).sync(); // (5)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

 

这就是官方提供的一个简单的客户端demo,大体上可以了解到,创建客户端必须先创建Bootstrap(英文意思是引导的意思),将

EventLoopGroup的创建绑定到Bootstrap,暂且叫他事件群管理,接下来绑定通道b.channel(NioSocketChannel.class),然后b.option(ChannelOption.SO_KEEPALIVE, true);开始设置socket属性,再初始化管道设置,最后连接。

现在我们点进

connect

实现链接的第一步

publicChannelFuture connect(SocketAddress remoteAddress) {
        if (remoteAddress == null) {
            throw newNullPointerException("remoteAddress");
        }
          /**
         * 判断有没有设置handler,没有抛异常程序结束
         */
        validate();
        returndoResolveAndConnect(remoteAddress, config.localAddress());
    }
     validate方法主要判断初始化的一些必要参数用户有没有设置  if (group == null) {
           throw new IllegalStateException("group notset");
       }
       if (channelFactory == null) {
           throw new IllegalStateException("channel orchannelFactory not set");
       }
       if (config.handler() == null) {
           throw new IllegalStateException("handler notset");
       }

 

继续走会进入   
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();


        if (regFuture.isDone()) {
            if (!regFuture.isSuccess()) {
                return regFuture;
            }
            return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    // Directly obtain the cause and do a null check so we only need one volatile read in case of a
                    // failure.
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();
                        doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

 

跟进initAndRegister这个方法注册方法很重要, 连接的时候注册初始化

 

 final ChannelFuture initAndRegister() {        Channel channel = null;        try {
channelFactory
//利用channelFactory创建通道channel ,也就是上面所传入的NioSocketChannel.class
            channel = channelFactory.newChannel();
//开始初始化channel跟进

            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
     /**
     * 注册通道
       */
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }


        return regFuture;
    }

 

 

//进入init()方法继续
 void init(Channel channel) throws Exception {
    /**
    * 把初始化handler句柄加入管道里
    * 用默认的DefaultChannelPipeline管道对象
    */
        ChannelPipeline p = channel.pipeline();
        p.addLast(config.handler());
        final Map<ChannelOption<?>, Object> options = options0();
        //把属性的一些设置(如前面设置的 bootStrap.option(ChannelOption.SO_KEEPALIVE, true);
bootStrap.option(ChannelOption.TCP_NODELAY, true);)设置到configuration里面
        synchronized (options) {
            setChannelOptions(channel, options);
        }
/**
attrs属性也设置到config配置里面
*/
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }


这个方法最重要的代码是  ChannelPipeline p = channel.pipeline(); p.addLast(config.handler());这两句,向管道里面添加上面注册的handler句柄,那么handelr是什么呢,暂且把他理解为处理数据的不同管道,你加载多少个管道数据就会一级级的去处理。这是源码提供的图形我想这个应该看起来更具体。

 

 

 

果然还是有图更具体,数据通过这个管道处理器一级一级的去处理数据,打个比方,我与服务端定一个自己的数据协议,那么socket传给我们的是byte数据最终我们会必须得转换为我们需要的类,这时候我们自定义一个编码器(通过一系列封装最终发送服务器所需要的数据),一个解码器(通过一系列解析数据,最终拼接成我们所需要的数据)即可。我们接着继续往下走进addLast,既然知道channel是NioSocketChannel了,那么找到

protected AbstractChannel(Channel parent) {
        .
        pipeline = newChannelPipeline();
    }
   protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }那么管道一目了然了默认就是DefaultChannelPipeline,进入 
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
        //检查handler是否合法
            checkMultiplicity(handler);

//handler的包装类
            newCtx = newContext(group, filterName(name, handler), handler);

//将newCtx 加入队列中
            addLast0(newCtx);
//从上面我们看出init方法在注册方法前面所以此时registered=false
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }


            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }
// 进入   
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
        assert !registered;
//第一次added肯定为true,那么task =PendingHandlerAddedTask(此类实现了runnble接口)
        PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
        PendingHandlerCallback pending = pendingHandlerCallbackHead;
        if (pending == null) {
            pendingHandlerCallbackHead = task;
        } else {
            // Find the tail of the linked-list.
            while (pending.next != null) {
                pending = pending.next;
            }
            pending.next = task;
        }
    }

 

最后走callHandlerAdded0(newCtx)方法,进入瞧瞧是干什么的,麻蛋,类实在太多了,最终调用的是

private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
        try {
            ctx.handler().handlerAdded(ctx);
            ctx.setAddComplete();
}}那么我们找到handler的handlerAdded,也就是ChannelInitializer类的handlerAdded方法  public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
           //最终调用我们的子类的抽象方法实现类
            initChannel(ctx);
        }
    }


也就是它

bootStrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//加入我们自己的管道
ch.pipeline().addLast("encoder", new Encoder());
ch.pipeline().addLast("decoder", new Decoder());
ch.pipeline().addLast("handler",
new ClientHandler(NettyCore.this));
}
});


继续回到前面,初始化调用完之后开始注册,注册通道

   ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }


此时的group为NioEventLoopGroup,直接查看它的register方法 

 public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }

 

经过一系列调用,最终调用了NioEventLoop的register方法,这设计的就有点巧妙了,初始化NioEventLoopGroup的时候调用父 类的构造函数

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
     /**
      * 传入的线程池为空就采用默认的
     */
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

//根据线程的数量创建多个EventExecutor,也就是newChild方法返回的NioEventLoop(每个NioEventLoop都持有相同的线程池,持有不同的队列)
        children = new EventExecutor[nThreads];


        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }


                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            } 
        }

//创建NioEventLoop的选择器,每次重新注册的时候,根据规则选取一个NioEventLoop
        chooser = chooserFactory.newChooser(children);
//添加回调

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };


        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }


        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

chooser是怎么样的呢

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EventExecutor[] executors;


        GenericEventExecutorChooser(EventExecutor[] executors) {
            this.executors = executors;
        }


        //每次返回随机集合里面的NioEventLoop
        public EventExecutor next() {
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }
    }
}


继续进入NioEventLoop的注册方法

 public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }


给channel套一个封装类

 public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
最后又转到unsafe去注册,妈的,想死!
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

 

最后终于在  NioSocketChannel的内部类 NioSocketChannelUnsafe里找到注册方法

/**
     * 注册ChannelPromise
    */
        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
//进行各方面的检测
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
//不能重复注册
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }


            AbstractChannel.this.eventLoop = eventLoop;


            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

 

方法最终调用doRegister

 得到selectionKey
 */
    @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
//注册nio的选择事件,默认0选择所有的事件(读就绪,已连接,写就绪等)
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

 

那么最后就剩开始连接服务器了,连接服务器的最终调用方法:

private static void doConnect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromie) {
        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        final Channel channel = connectPromise.channel();
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (localAddress == null) {
                    channel.connect(remoteAddress, connectPromise);
                } else {
                    channel.connect(remoteAddress, localAddress, connectPromise);
                }
                connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        });
    }

管道最终调用connect方法,调来调去又回到NioSocketChannel调用方法

  protectedboolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress)throws Exception {
        if (localAddress != null) {
            doBind0(localAddress);
        }
        boolean success = false;
        try {
            boolean connected =SocketUtils.connect(javaChannel(), remoteAddress);
            if (!connected) {
               selectionKey().interestOps(SelectionKey.OP_CONNECT);
            }
            success = true;
            return connected;
        } finally {
            if (!success) {
                doClose();
            }
        }
    }

好了,整个连接走通了,下一篇讲解,数据解析,发送的逻辑管道顺序,本人第一次写帖子,以前实在是很懒去写贴子,写了之后才知道其中的辛酸,和不容易,思路也可以更好的撸清,以后要坚持写帖子了,如果你喜欢这篇文章,请转载。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值