小技巧(如何看开源框架的源码)

一断点

二打印

三看调用栈

四搜索

源码解析

//设置niosocket工厂
//NioServerSocketChannelFactory看下面
  bootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));

NioServerSocketChannelFactory.java

public NioServerSocketChannelFactory(
            Executor bossExecutor, Executor workerExecutor) {
            //首先获取当前worker的数量,代码看下面的SelectorUtil.java
            //接着会调用下面三个参数的构造方法NioServerSocketChannelFactory
        this(bossExecutor, workerExecutor, getMaxThreads(workerExecutor));
}
public NioServerSocketChannelFactory(
            Executor bossExecutor, Executor workerExecutor,
            int workerCount) {
            //接着调用下面四个参数的构造方法NioServerSocketChannelFactory
            //boss默认给的1
        this(bossExecutor, 1, workerExecutor, workerCount);
}
public NioServerSocketChannelFactory(
            Executor bossExecutor, int bossCount, Executor workerExecutor,
            int workerCount) {
            //开始new一个worker的池子
            //代码看下面的NioWorkerPool.java
        this(bossExecutor, bossCount, new NioWorkerPool(workerExecutor, workerCount));
}

SelectorUtil.java

//默认数量是当前的核数成2
static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() * 2;

 private static int getMaxThreads(Executor executor) {
 //MaxThreads最大池大小
        if (executor instanceof ThreadPoolExecutor) {
            final int maxThreads = ((ThreadPoolExecutor) executor).getMaximumPoolSize();
            //取maxThreads和DEFAULT_IO_THREADS两者的最小值
            return Math.min(maxThreads, SelectorUtil.DEFAULT_IO_THREADS);
        }
        //因为我们之前的例子中是给的无限大小的池子,所以这里返回DEFAULT_IO_THREADS
        return SelectorUtil.DEFAULT_IO_THREADS;
    }

NioWorkerPool.java

public NioWorkerPool(Executor workerExecutor, int workerCount) {
//调用自己的NioWorkerPool方法,在下面
        this(workerExecutor, workerCount, null);
}
public NioWorkerPool(Executor workerExecutor, int workerCount, ThreadNameDeterminer determiner) {
//调用父类的构造方法
        super(workerExecutor, workerCount, false);
        this.determiner = determiner;
        //init了一次,代码看下面init方法
        init();
}
//实现了抽象方法newWorker
@Override
protected NioWorker newWorker(Executor executor) {
//new了一个NioWorker
        return new NioWorker(executor, determiner);
}

NioWOrker.java

public NioWorker(Executor executor, ThreadNameDeterminer determiner) {
//调用父类方法,看下面AbstractNioWorker.java
        super(executor, determiner);
}
@Override
    protected boolean read(SelectionKey k) {
        final SocketChannel ch = (SocketChannel) k.channel();
        final NioSocketChannel channel = (NioSocketChannel) k.attachment();

        final ReceiveBufferSizePredictor predictor =
            channel.getConfig().getReceiveBufferSizePredictor();
        final int predictedRecvBufSize = predictor.nextReceiveBufferSize();
        final ChannelBufferFactory bufferFactory = channel.getConfig().getBufferFactory();

        int ret = 0;
        int readBytes = 0;
        boolean failure = true;

        ByteBuffer bb = recvBufferPool.get(predictedRecvBufSize).order(bufferFactory.getDefaultOrder());
        try {
            while ((ret = ch.read(bb)) > 0) {
                readBytes += ret;
                if (!bb.hasRemaining()) {
                    break;
                }
            }
            failure = false;
        } catch (ClosedChannelException e) {
            // Can happen, and does not need a user attention.
        } catch (Throwable t) {
            fireExceptionCaught(channel, t);
        }

        if (readBytes > 0) {
            bb.flip();
//在这里封装成了ChannelBuffer
            final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes);
            buffer.setBytes(0, bb);
            buffer.writerIndex(readBytes);

            // Update the predictor.
            predictor.previousReceiveBufferSize(readBytes);

            // Fire the event.产生一个上传的事件
            //channels里面的一个方法
             //   * @param message  the received message
    		//public static void fireMessageReceived(Channel channel, Object message) {
        			//fireMessageReceived(channel, message, null);
    		//}
            fireMessageReceived(channel, buffer);
        }

        if (ret < 0 || failure) {
            k.cancel(); // Some JDK implementations run into an infinite loop without this.
            close(channel, succeededFuture(channel));
            return false;
        }

        return true;
    }

AbstractNioWorker.java

AbstractNioWorker(Executor executor, ThreadNameDeterminer determiner) {
//又调用了父类的抽象方法,看下面AbstractNioSelector.java
        super(executor, determiner);
 }
  @Override
    protected void process(Selector selector) throws IOException {
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        // check if the set is empty and if so just return to not create garbage by
        // creating a new Iterator every time even if there is nothing to process.
        // See https://github.com/netty/netty/issues/597
        if (selectedKeys.isEmpty()) {
            return;
        }
        for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
            SelectionKey k = i.next();
            i.remove();
            try {
                int readyOps = k.readyOps();
                if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) {
                //读数据,向上看NioWorker中的read方法
                    if (!read(k)) {
                        // Connection already closed - no need to handle write.
                        continue;
                    }
                }
                if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                    writeFromSelectorLoop(k);
                }
            } catch (CancelledKeyException e) {
                close(k);
            }

            if (cleanUpCancelledKeys()) {
                break; // break the loop to avoid ConcurrentModificationException
            }
        }
    }

AbstractNioSelector.java

AbstractNioSelector(Executor executor, ThreadNameDeterminer determiner) {
		//给了一个线程池
        this.executor = executor;
//openSelector看下面openSelector方法
        openSelector(determiner);
}
private void openSelector(ThreadNameDeterminer determiner) {
        try {
        //设置当前的selector
            selector = SelectorUtil.open();
        } catch (Throwable t) {
            throw new ChannelException("Failed to create a selector.", t);
        }

        // Start the worker thread with the new Selector.
        boolean success = false;
        try {
        //把这个Nioworker跑起来,因为NioWorker本身是继承AbstractNioSelector这个类的
        //所以跑的是这个类的run方法
        //从哪里启动呢?往下看DeadLockProofWorker.java
        //也就是调用的newThreadRenamingRunnable(id, determiner)
        //newThreadRenamingRunnable看下面AbstractNioWorker.java
            DeadLockProofWorker.start(executor, newThreadRenamingRunnable(id, determiner));
            success = true;
        } finally {
            if (!success) {
                // Release the Selector if the execution fails.
                try {
                    selector.close();
                } catch (Throwable t) {
                    logger.warn("Failed to close a selector.", t);
                }
                selector = null;
                // The method will return to the caller at this point.
            }
        }
        assert selector != null && selector.isOpen();
    }
public void run() {
	for (;;) {
	//标记wakenup状态
            wakenUp.set(false);
            //状态监测的代码
            ...
            //取任务
            processTaskQueue();
			//业务处理,这是一个抽象方法,被三个类实现AbstractNioWorker、NioClientBoss、
			//AbstractNioWorker中的process在上方
			//NioServerBoss中的process在下方
			process(selector);
}

NioServerBoss.java

  @Override
    protected void process(Selector selector) {
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        if (selectedKeys.isEmpty()) {
            return;
        }
        for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
            SelectionKey k = i.next();
            i.remove();
            NioServerSocketChannel channel = (NioServerSocketChannel) k.attachment();

            try {
                // accept connections in a for loop until no new connection is ready
                for (;;) {
                //accept事件
                    SocketChannel acceptedSocket = channel.socket.accept();
                    if (acceptedSocket == null) {
                        break;
                    }
                    //注册的方法在本类的下方,向worker线程里面注册任务
                    registerAcceptedChannel(channel, acceptedSocket, thread);
                }
            } catch (CancelledKeyException e) {
                // Raised by accept() when the server socket was closed.
                k.cancel();
                channel.close();
            } catch (SocketTimeoutException e) {
                // Thrown every second to get ClosedChannelException
                // raised.
            } catch (ClosedChannelException e) {
                // Closed as requested.
            } catch (Throwable t) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to accept a connection.", t);
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    // Ignore
                }
            }
        }
    }

    private static void registerAcceptedChannel(NioServerSocketChannel parent, SocketChannel acceptedSocket,
                                         Thread currentThread) {
        try {
            ChannelSink sink = parent.getPipeline().getSink();
            ChannelPipeline pipeline =
                    parent.getConfig().getPipelineFactory().getPipeline();
                    //找到一个worker,通过下面的方法把每个工作均匀的分配给每一个worker
                    // public E nextWorker() {
        			//return (E) workers[Math.abs(workerIndex.getAndIncrement() % workers.length)];
  					//  }
            NioWorker worker = parent.workerPool.nextWorker();
            //向worker里面注册任务,而不是直接去操作worker
            //让他关注一下socketchannel,即acceptedSocket注册这个东西
            //因为register这个方法是继承AbstractNioSelector的,即完成之后需要提交任务并记过wakenup
           	//代码粘贴过来了
            //public void register(Channel channel, ChannelFuture future) {
        	//Runnable task = createRegisterTask(channel, future);
        	//registerTask(task);
   			// }
   			// protected final void registerTask(Runnable task) {
       		// taskQueue.add(task);
        	//Selector selector = this.selector;
        	//if (selector != null) {
            	//if (wakenUp.compareAndSet(false, true)) {
                	//selector.wakenup();
            //}
        	//} else {
	           //if (taskQueue.remove(task)) {
                // the selector was null this means the Worker has already been shutdown.
                //throw new RejectedExecutionException("Worker has already been shutdown");
            	//}
       		// }
   			// }
            worker.register(new NioAcceptedSocketChannel(
                    parent.getFactory(), pipeline, parent, sink
                    , acceptedSocket,
                    worker, currentThread), null);
        } catch (Exception e) {
            if (logger.isWarnEnabled()) {
                logger.warn(
                        "Failed to initialize an accepted socket.", e);
            }

            try {
                acceptedSocket.close();
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially accepted socket.",
                            e2);
                }
            }
        }
    }

AbstractNioWorker.java

protected ThreadRenamingRunnable newThreadRenamingRunnable(int id, ThreadNameDeterminer determiner) {
//this当前的NioWorker,然后给了一个线程的名称
        return new ThreadRenamingRunnable(this, "New I/O worker #" + id, determiner);
    }
public ThreadRenamingRunnable(Runnable runnable, String proposedThreadName, ThreadNameDeterminer determiner) {
        if (runnable == null) {
            throw new NullPointerException("runnable");
        }
        if (proposedThreadName == null) {
            throw new NullPointerException("proposedThreadName");
        }
        this.runnable = runnable;
        this.determiner = determiner;
        this.proposedThreadName = proposedThreadName;
    }
try {
//这里runnable执行其实就是NioWorker进行了run
//NioWorker.run其实是运行父类的AbstractNioWorker run
//AbstractNioWorker 的run又调用的是父类AbstractNioSelector的run方法
//AbstractNioSelector的run方法,往上面看
            runnable.run();
        }

DeadLockProofWorker.java

public final class DeadLockProofWorker {

    /**
     * An <em>internal use only</em> thread-local variable that tells the
     * {@link Executor} that this worker acquired a worker thread from.
     */
    public static final ThreadLocal<Executor> PARENT = new ThreadLocal<Executor>();

    public static void start(final Executor parent, final Runnable runnable) {
        if (parent == null) {
            throw new NullPointerException("parent");
        }
        if (runnable == null) {
            throw new NullPointerException("runnable");
        }
		//通过线程池启动一个Runnable
        parent.execute(new Runnable() {
            public void run() {
                PARENT.set(parent);
                try {
                //调用Runnable的run方法,然后返回去看
                    runnable.run();
                } finally {
                    PARENT.remove();
                }
            }
        });
    }

    private DeadLockProofWorker() {
    }
}

AbstractNioWorkerPool.java

//这里到了他的父类AbstractNioWorkerPool
blic void run() {
        thread = Thread.currentThread();
        startupLatch.countDown();

        int selectReturnsImmediately = 0;
        Selector selector = this.selector;
...
}
//数组workers
private final AbstractNioWorker[] workers;
//父类的构造方法方法
AbstractNioWorkerPool(Executor workerExecutor, int workerCount, boolean autoInit) {
        if (workerExecutor == null) {
            throw new NullPointerException("workerExecutor");
        }
        if (workerCount <= 0) {
            throw new IllegalArgumentException(
                    "workerCount (" + workerCount + ") " + "must be a positive integer.");
        }
        //有一个workers的数组,new了workerCount的数组
        workers = new AbstractNioWorker[workerCount];
        this.workerExecutor = workerExecutor;
        if (autoInit) {
            init();
        }
    }
protected void init() {
        if (!initialized.compareAndSet(false, true)) {
            throw new IllegalStateException("initialized already");
        }

        for (int i = 0; i < workers.length; i++) {
        //对worker进行初始化,具体实现往下看
            workers[i] = newWorker(workerExecutor);
        }

        waitForWorkerThreads();
    }
    //是一个抽象方法,具体实现看上面的NioWorkerPool.java中的newWorker方法
protected abstract E newWorker(Executor executor);

    @SuppressWarnings("unchecked")
    public E nextWorker() {
        return (E) workers[Math.abs(workerIndex.getAndIncrement() % workers.length)];
    }

    public void rebuildSelectors() {
        for (AbstractNioWorker worker: workers) {
            worker.rebuildSelector();
        }
    }

阅读源码技巧

打印查看

for(;;){
	sout(Thread.currentThread().getName()+" "+wakenup);
	wakenUp.set(false);
	...
	sout(Thread.currentThread().getName()+" "+select);
	int selected = select(selector);
	...
	sout(Thread.currentThread().getName()+" "+processTaskQueue);
	processTaskQueue();
	...
	sout(Thread.currentThread().getName()+" "+process);
	process(selector);
}

输出

...
New I/O server boss #2 process
New I/O server boss #2 wakenup
New I/O server boss #2 select
New I/O server boss #2 processTaskQueue
New I/O server boss #2 process
New I/O server boss #2 wakenup
New I/O server boss #2 select
...
New I/O worker #1 wakenup
New I/O worker #1 select
New I/O worker #1 processTaskQueue
New I/O worker #1 process
New I/O worker #1 wakenup
New I/O worker #1 select
New I/O worker #1 processTaskQueue
New I/O worker #1 process
...

可以看到worker是四个四个循环往复,可是boss线程为什么在select就没有继续执行了?

通过打断点调试

将断点打在wakenup.set

为了只看boss线程,eclipse进入断点的控制页面–》右上角breanpoints–》选中本类右击–》BreakPoint Properties–》新的页面中勾选Conditional–》下面输入“Thread.currentThread().getName().contains(“boss”)”–》点击ok

通过打断点发现到了select方法的时候点进去,然后没有执行默认的select(500),这样的方法,而是执行的select()这种阻塞的方式,所以阻塞住了。

查看调用栈

通过查看调用栈的方式查看某个方法具体调用的堆栈信息

eclipse在debug界面的左上角

idea在debug界面的左下角

基于Netty的RPC架构学习笔记(五):netty线程模型源码分析(二)的更多相关文章

  1. 基于Netty的RPC架构学习笔记(六):netty5案例学习

    文章目录 netty5服务端入门案例 netty5客户端入门案例 单客户端多连接程序 知识普及 线程池原理图 对象池原理图 对象组原理图 结论 理论结合实际 开干开干 总结 netty5服务端入门案例 ...

  2. 基于Netty的RPC架构学习笔记(二):netty服务器

    文章目录 简介 Netty服务端Hello World案例 举个

  3. 基于Netty的RPC架构学习笔记(十二):借助spring实现业务分离、聊天室小项目、netty3和4、5的不同、业务线程池以及消息串行化

    文章目录 借助spring实现业务分离(

  4. 基于Netty的RPC架构学习笔记(十一):粘包、分包分析,如何避免socket攻击

    文章目录 问题 消息如何在管道中流转 源码解析 AbstractNioSelector.java AbstractNioWorker.java NioWorker.java DefaultChanne ...

  5. 基于Netty的RPC架构学习笔记(十):自定义数据包协议

    文章目录 数据包简介 粘包.分包现象 数据包格式 举个

  6. 基于Netty的RPC架构学习笔记(九):自定义序列化协议

    文章目录 为什么需要自定义序列化协议

  7. 基于Netty的RPC架构学习笔记(八):protocol buff学习使用

    文章目录 简介 准备 protobuf配置文件 生成java代码 举个

  8. 基于Netty的RPC架构学习笔记(七):netty学习之心跳

    文章目录 idleStateHandler netty3

  9. 基于Netty的RPC架构学习笔记(四):netty线程模型源码分析(一)

    文章目录 如何提高NIO的工作效率 举个

随机推荐

  1. js中的函数声明和函数表达式的区别

    目录 一.声明与表达式的格式 1.1 声明式的格式: 1.2 表达式的格式: 二.区别 2.1 函数表达式可以直接在后面加括号执行,而函数声明不可以. 2.2 函数表达式可以被提前解析出来 2.3 命 ...

  2. 12、jquery的tree组件

    1. <!--jquery 的主文件...--> <script type="text/javascript" src="../../js/jquery ...

  3. 剑指offer第二版面试题1:数组中重复的数字(JAVA版)

    题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复的次数.请找出数组中任意一个重复的数字.例如如果输入长度为7的数组{ ...

  4. 什么是Web?

    Web这个词刚开始显得有些泛泛,似乎“冲浪”.“网上存在”以及“主页”等等都和它拉上了一些关系.甚至还有一种“Internet综合症”的说法,对许多人狂热的上网行为提出了质疑.我们在这里有必要作一些深 ...

  5. Navicat for MySQL使用手记

    摘要 在管理MySQL数据库的图形化工具中,最为熟知的就是phpMyAdmin和Mysql-Front了,今天跟大家分享另外一个管理mysql数据库的另外一个利器---Navicat MySQL. N ...

  6. Nginx学习——proxy_pass

    参考官网:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass 定义:用来设置被代理服务器的协议(http或https ...

  7. Linux操作基础

    摘要 一.Linux操作系统概述 二.Linux操作系统安装 三.Linux文件系统及文件基础 四.Linux操作系统命令使用基础 五.Linux应用程序的安装与卸载基础 五.用户及进程 六.相关信息 ...

  8. UncategorizedSQLException异常处理办法

    如题,先贴console org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQ ...

  9. vue-cli 利用moment.js转化时间格式为YYYY年MM月DD日,或者是YYYY-MM-DD HH:MM:SS 等格式

    1.在mian.js引入moment import moment from 'moment' Vue.prototype.$moment = 'moment' 2. 在main.js 设置全局过滤器 ...

  10. 辞职信也要玩出高big

    辞职信尊敬的各位公司领导:值此用人之际,不期请辞,实属不敬.历经四季,余以凡才,承蒙殊待,幸受公司各位领导知遇之恩,得以与诸位贤达公事.时光荏苒,吾经竭力而为,以图报效,虽幸遇领导执手相教,然资质愚钝 ...