【源码】netty源码笔记

目录

服务端代码

NioEventLoopGroup

NioEventLoop

ServerBootstrap

Bind(port)

initAndRegister()

Init(channel)

group().register(channel)

register0(promise)

doBind0()

doBind(localAddress)

isActive()

pipeline.fireChannelActive()

SingleThreadEventExecutor.execute(task)

NioEventLoop.run()

Select()

processSelectedKey(SelectionKey,Channel)

runAllTasks()

Unsafe.read()

Accept:NioMessageUnsafe.read()

doReadMessages(readBuf)

注册NioSocketChannel到workerGroup:ServerBootstrapAcceptor

Read:NioByteUnsafe.read()

ChannelPipeline

inbound传播过程

outbound传播过程


随便写写,代码基于netty4.0.35版本。

服务端代码

NioEventLoopGroup

创建NioEventLoopGroup,会默认创建2*jvm可用cpu个数个NioEventLoop(可以指定)。

NioEventLoopGroup的父类MultithreadEventExecutorGroup有一个children属性,是一个EventExecutor[],用来存放指定数量的NioEventLoop.

实例化children之后,为每个NioEventLoop的执行结果Future添加监听线程执行结果的listener。

NioEventLoop

NioEventLoop名为事件循环,主要用来处理channel上的accept、read、write等事件。NioEventLoop主要包含thread(线程类)、selector(多路复用器)、taskQueue等属性。

  • selector:多路复用器。一个NioEventLoop管理一个或多个通道(通道会注册到NioEventLoop的selector上),然后执行select()来监听channel上的事件。
  • thread:和NioEventLoop绑定的线程。在创建NioEventLoop时会创建thread并执行thread.run()。thread启动时会执行NioEventLoop的run方法(NioEventLoop本身并不是线程,只是有一个名字叫run的方法),run方法的具体内容见下面介绍。
  • taskQueue:taskQueue是一个Queue<Runnable>,仅仅用来存放上面的thread(NioEventLoop.run())还没启动时的任务,如ServerChannel和端口绑定时的fireChannelActive执行,还有通道第一次在NioEventLoop上注册的任务,这些任务会在NioEventLoop.run()中调用runAllTasks()来执行。当NioEventLoop的thread启动时,后面的任务就都不会存放在taskQueue中而是直接在NioEventLoop中同步执行了。

这是一段比较典型的代码,当selector监听到read事件时,会调用通道上的head(ChannelHandler)的fireChannelRead(),如果当前执行线程是这个通道注册的NioEventLoop的thread时,就直接执行,否则就执行execute(),将任务封装成一个Thread,放入taskQueue中。

    public ChannelHandlerContext fireChannelRead(final Object msg) {
        final AbstractChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRead(msg);
        } else {
            executor.execute(new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeChannelRead(msg);
                }
            });
        }
        return this;
    }

    public boolean inEventLoop() {
        return inEventLoop(Thread.currentThread());
    }

    public void execute(Runnable task) {
        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }
        ......
    }

创建NioEventLoop会调用自己及父类SingleThreadEventExecutor的构造方法,创建线程绑定到的NioEventLoop的thread属性,同时初始化taskQueue。

    protected SingleThreadEventExecutor(
            EventExecutorGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {
        ......
        // 创建thread
        thread = threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                ......
                // 执行NioEventLoop.run()
                SingleThreadEventExecutor.this.run();
                ......
            }
        });
        threadProperties = new DefaultThreadProperties(thread);
        // 初始化taskQueue
        taskQueue = newTaskQueue();
    }

 

NioEventLoop.run()监听到IO事件后,是直接在当前NioEventLoop中同步调用处理方法的,即监听IO事件的线程和处理事件的线程是同一个线程,这样做的好处就是能避免创建太多线程时频繁的上下文切换。当然netty也并不由将线程数量定死,你在创建NioEventLoopGroup时可以指定线程数量。

ServerBootstrap

这是服务器通道的启动类。有两个NioEventLoopGroup属性(group【bossGroup】和childGroup【workerGroup】)

服务端一般会传两个NioEventLoopGroup,一个boss,一个worker;客户端只会传一个,worker。

Bind(port)

两大核心方法:initAndRegister()和doBind0()。

initAndRegister()

首先会new一个channel(包括pipeline、ChannelConfig、ChannelId、ChannelHandler等)。

Init(channel)

然后进行初始化,执行init(channel)。该方法仅初始化了一些基本的选项(options)或属性(attr),以及在pipeline中添加一些ChannelHandler(包括一个ServerBootstrapAcceptor)。详见ServerBootstrap.init(channel)。

group().register(channel)

Group()会返回一个NioEventLoopGroup(服务端是bossGroup),然后调用它的register(channel)

AbstractChannel.this.eventLoop = eventLoop;将传入的NioEventLoop与创建的这个Channel绑定。

接下来会执行register0(),但是然后会判断eventLoop.inEventLoop()即Thread.currentThread()【当前执行的线程】是否是eventLoop的thread(此处是main线程),是就直接执行,不是就执行eventLoop.execute(),将执行的方法放入到一个线程中,存入taskQueue,稍后执行。

register0(promise)

这个方法是真正注册的方法,看方法就知道是将这个channel注册到绑定的eventLoop的selector上

doBind0()

如果通道注册成功,则进行端口绑定

最终到了AbstractChannel.bind()。

doBind(localAddress)

doBind(localAddress)就是真正将程序和通道进行了绑定。

isActive()

就是判断当前服务通道是否已经和一个地址绑定,是返回true,否则false。

pipeline.fireChannelActive()

如果channel和端口绑定了就将pipeline.fireChannelActive()这个任务创建为一个线程放入到eventLoop的taskQueue中,任务执行时在该channelPipeline上执行fireChannelActive(),然后沿着pipeline链一路传递下去。Server启动是沿着inbound链传递。

SingleThreadEventExecutor.execute(task)

SingleThreadEventExecutor是NioEventLoop的祖先类。Execute(task)的作用是将task加入到taskQueue中,此外如果不是从NioEventLoop.run()中进入execute()的话,就会启动NioEventLoop绑定的thread。

这个绑定的thread启动会执行NioEventLoop.run()的run方法。

创建server的过程中,会执行execute()来将通道注册任务(register0())和pipeline.fireChannelActive()任务放入到taskQueue中,所以此时thread会被启动。之后server就是监听连接事件,所以的连接事件会在NioEventLoop.run()中进行处理,就都是直接执行了。

NioEventLoop.run()

NioEventLoop.run()是一个无限循环。主要进行:

  1. 监听注册在NioEventLoop的selector上的通道事件(连接、读写等)。
  2. 处理所有selector监听到的事件【processSelectedKeys()】。
  3. 处理所有taskQueue中的task。【runAllTasks()】。

Select()

processSelectedKey(SelectionKey,Channel)

对于监听到的channel事件,进行判断,然后进行操作,可以看到,包括了四种IO事件

runAllTasks()

就是执行了taskQueue中的所有task的run方法。

Unsafe.read()

这个unsafe是调用了channel的unsafe()后返回的。而NioServerSocketChannel(父类是AbstractNioMessageChannel)返回NioMessageUnsafe,NioSocketChannel(父类是AbstractNioByteChannel)返回NioByteUnsafe。

上面监听到read或accept事件都会调用unsafe.read(),就是因为NioMessageUnsafe.read()是处理accept事件、NioByteUnsafe.read()是处理读事件的。

Accept:NioMessageUnsafe.read()

NioMessageUnsafe.read()最终会在一个无限循环中去接受连接并创建NioSocketchannel(因为可能同时有多个客户端建立连接),所有创建的channel都会存放在readBuf中,然后执行readBuf中所有的channel的pipeline的fireChannelRead()。最后执行NioServerSocketChannel的fireChannelReadComplete(),表示本次accept事件处理完成。

            private final List<Object> readBuf = new ArrayList<Object>();

            public void read() {
                 for (;;) {
                        int localRead = doReadMessages(readBuf);
                        // dosomething();
                }
                ......
                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    // 注意这里是将创建的NioSocketChannel当做参数
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                pipeline.fireChannelReadComplete();
            }

doReadMessages(readBuf)

首先accept()接受连接,获取连接通道,然后将通道添加到buf中。

    protected int doReadMessages(List<Object> buf) throws Exception {
        SocketChannel ch = javaChannel().accept();
        try {
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            logger.warn("Failed to create a new channel from an accepted socket.", t);
            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }
        return 0;
    }

注册NioSocketChannel到workerGroup:ServerBootstrapAcceptor

上满提到了在bind(port)的时候会初始化NioServerSocketChannel,就是init()。这个方法中会将ServerBootstrapAcceptor(ChannelInboundHandlerAdapter)添加到NioServerSocketChannel的pipeline中,所以当一个连接创建成功触发channelRead()时,ServerBootstrapAcceptor的channlRead()会被调用。

        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;
            ......
            try {
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

显然,这与NioServerSocketChannel的注册过程是一样的,不过NioServerSocketChannel是被注册到bossGroup中,而NioSocketChannel是被注册到workerGroup中。

Read:NioByteUnsafe.read()

NioByteUnsafe.read()首先会不断的读取NioSocketChannel中的数据到一个byteBuf中,然后触发inbound链的channelRead方法,将数据交给ChannelHanlder去处理,最终会传递到用户自定义的ChannelHanlder中,对数据进行业务处理。等所有数据都读取完成后,就调用fireChannelReadComplete()。

public final void read() {
    ......
    do{
        int localReadAmount = doReadBytes(byteBuf);
        ......
        pipeline.fireChannelRead(byteBuf);
    while (++ messages < maxMessagesPerRead);
    ......
    pipeline.fireChannelReadComplete();
}

ChannelPipeline

netty中的ChannelPipeline与channel绑定,每创建一个channel,就会创建一个ChannelPipeline。

    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        unsafe = newUnsafe();
        pipeline = new DefaultChannelPipeline(this);
    }

ChannelPipeline相当于一个双向链表,节点类型就是ChannelHandler。而ChannelHandler又分为两类:inbound和outbound。ChannelPipeline中初始就有两个节点:head(outbound)和tail(inbound)

    public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;
        tail = new TailContext(this);
        head = new HeadContext(this);
        head.next = tail;
        tail.prev = head;
    }

读数据时,数据会在inbound链上传播(从head到tail),而写数据时(wirteAndFlush),数据会在outbound链上传播(从tail到head)。

inbound传播过程

这里以客户端从通道读取数据为例,最终unsafe.read()会执行到NioByteUnsafe.read()。

            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
                ......
            }

从前面可知,对于每次读到的数据都会调用pipeline.fireChannelRead(byteBuf),进入该方法里面发现是调用的head.fireChannelRead(msg),所以是从head开始传递。然后在下一个方法调用findContextInbound()来找到下一个ChannelHandler,并执行它的channelRead()。

    public ChannelPipeline fireChannelRead(Object msg) {
        head.fireChannelRead(msg);
        return this;
    }

    public ChannelHandlerContext fireChannelRead(final Object msg) {
        ......
        final AbstractChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRead(msg);
        } else {
            executor.execute(new OneTimeTask() {
                @Override
                public void run() {
                    next.invokeChannelRead(msg);
                }
            });
        }
        return this;
    }

很明显,就是在链表上通过循环的方式找到下一个inbound类型的ChannelHandler,直到tail。


    private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

outbound传播过程

这里以channel.writeAndFlush(msg)为例。可以看到首先调用的tail.writeAndFlush(msg)。然后进入到了一个write方法中,通过findContextOutbound()来找出下一个要传播的ChannelHandler。

    public ChannelFuture writeAndFlush(Object msg) {
        return tail.writeAndFlush(msg);
    }

    public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
        ......
        write(msg, true, promise);
        return promise;
    }

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeWrite(msg, promise);
            if (flush) {
                next.invokeFlush();
            }
        } else {
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, msg, promise);
            }  else {
                task = WriteTask.newInstance(next, msg, promise);
            }
            safeExecute(executor, task, promise, msg);
        }
    }

跟上面一样,从尾往头找到outbound类型的ChannelHandler进行传播,直到head。

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值