基于Netty的RPC架构学习笔记(五):netty线程模型源码分析(二)
小技巧(如何看开源框架的源码)
一断点
二打印
三看调用栈
四搜索
源码解析
//设置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线程模型源码分析(二)的更多相关文章
- 基于Netty的RPC架构学习笔记(六):netty5案例学习
文章目录 netty5服务端入门案例 netty5客户端入门案例 单客户端多连接程序 知识普及 线程池原理图 对象池原理图 对象组原理图 结论 理论结合实际 开干开干 总结 netty5服务端入门案例 ...
- 基于Netty的RPC架构学习笔记(二):netty服务器
文章目录 简介 Netty服务端Hello World案例 举个
- 基于Netty的RPC架构学习笔记(十二):借助spring实现业务分离、聊天室小项目、netty3和4、5的不同、业务线程池以及消息串行化
文章目录 借助spring实现业务分离(
- 基于Netty的RPC架构学习笔记(十一):粘包、分包分析,如何避免socket攻击
文章目录 问题 消息如何在管道中流转 源码解析 AbstractNioSelector.java AbstractNioWorker.java NioWorker.java DefaultChanne ...
- 基于Netty的RPC架构学习笔记(十):自定义数据包协议
文章目录 数据包简介 粘包.分包现象 数据包格式 举个
- 基于Netty的RPC架构学习笔记(九):自定义序列化协议
文章目录 为什么需要自定义序列化协议
- 基于Netty的RPC架构学习笔记(八):protocol buff学习使用
文章目录 简介 准备 protobuf配置文件 生成java代码 举个
- 基于Netty的RPC架构学习笔记(七):netty学习之心跳
文章目录 idleStateHandler netty3
- 基于Netty的RPC架构学习笔记(四):netty线程模型源码分析(一)
文章目录 如何提高NIO的工作效率 举个
随机推荐
- CSS:CSS 伪类(Pseudo-classes)
ylbtech-CSS:CSS 伪类(Pseudo-classes) 1.返回顶部 1. CSS 伪类(Pseudo-classes) CSS伪类是用来添加一些选择器的特殊效果. 语法 伪类的语法: ...
- webogic基本使用
文章目录 启动 注入 部署应用: 访问 启动 /root/Oracle/Middleware/user_projects/domains/weblogic/bin/startWebLogic.sh 上 ...
- redis 配置文件aof配置
redis 配置文件aof配置: bind 127.0.0.1 port 6379 daemonize yes dbfilename dump.rdb dir /new_renpeng/redis/ ...
- 20140506 visio 画布大小 栈实现队列 堆空闲内存地址链表 堆最大可分配的内存 可用内存链表
1.调整visio的画布大小 按住Ctrl鼠标移动到画布边缘即可 2.两个栈实现一个队列 一个栈用于入队,一个用于出队 #include<iostream> #include<sta ...
- 数据整理B
- 数据整理A
- hdu6341 /// 模拟 DFS+剪枝
题目大意: 将16行16列的矩阵分成四行四列共16块 矩阵的初始状态每行及每列都不会出现重复的元素 给定一个已旋转过某些块的矩阵 判断其是由初始状态最少经过几次旋转得到的 DFS枚举16个块的旋转方式 ...
- JS对象 编程练习 某班的成绩出来了,现在老师要把班级的成绩打印出来。 效果图: XXXX年XX月X日 星期X--班级总分为:81
编程练习 某班的成绩出来了,现在老师要把班级的成绩打印出来. 效果图: XXXX年XX月X日 星期X--班级总分为:81 格式要求: 1.显示打印的日期. 格式为类似"XXXX年XX月XX日 ...
- API详解
- Bash 脚本 set 命令教程
http://www.ruanyifeng.com/blog/2017/11/bash-set.html set命令是 Bash 脚本的重要环节,却常常被忽视,导致脚本的安全性和可维护性出问题.本文介 ...