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

来源:互联网 发布:网络广告公司铭心 编辑:程序博客网 时间:2024/05/22 03:43

研究源码的前提建立在:

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>() {@Overridepublic 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();            }        }

此时的groupNioEventLoopGroup,直接查看它的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();
            }
        }
    }


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



 
阅读全文
0 0