多线程模式中,在main函数中会执行InitServerLast

void InitServerLast() {
bioInit();
// 关键一步, 这里启动了多条线程,用于执行命令,redis起名为IO 线程
initThreadedIO();
set_jemalloc_bg_thread(server.jemalloc_bg_thread);
server.initial_memory_usage = zmalloc_used_memory();
}

1. 看initThreadedIO

networking.c

void initThreadedIO(void) {
server.io_threads_active = 0; /* We start with threads not active. */ /* Indicate that io-threads are currently idle */
io_threads_op = IO_THREADS_OP_IDLE; /* Don't spawn any thread if the user selected a single thread:
* we'll handle I/O directly from the main thread. */
if (server.io_threads_num == 1) return; if (server.io_threads_num > IO_THREADS_MAX_NUM) {
serverLog(LL_WARNING,"Fatal: too many I/O threads configured. "
"The maximum number is %d.", IO_THREADS_MAX_NUM);
exit(1);
} /* Spawn and initialize the I/O threads. */
for (int i = 0; i < server.io_threads_num; i++) {
/* Things we do for all the threads including the main thread. */
// 为线程创建一个 list,并将该list 的指针加入到io_threads_list中
io_threads_list[i] = listCreate();
if (i == 0) continue; /* Thread 0 is the main thread. */ /* Things we do only for the additional threads. */
pthread_t tid;
pthread_mutex_init(&io_threads_mutex[i],NULL);
setIOPendingCount(i, 0);
pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */
// 这里创建线程,并将线程id 假如到io_threads数组中
if (pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) != 0) {
serverLog(LL_WARNING,"Fatal: Can't initialize IO thread.");
exit(1);
}
io_threads[i] = tid;
}
}
这样在该函数中,创建了线程 并 为该线程分配了一个 list

2. 当client 发命令过来时,会调用到readQueryFromClient

void readQueryFromClient(connection *conn) {
client *c = connGetPrivateData(conn);
int nread, big_arg = 0;
size_t qblen, readlen; /* Check if we want to read from the client later when exiting from
* the event loop. This is the case if threaded I/O is enabled. */
// 多线程模式时,在这里会return
if (postponeClientRead(c)) return;
......
}

3. 看postponeClientRead

int postponeClientRead(client *c) {
if (server.io_threads_active &&
server.io_threads_do_reads &&
!ProcessingEventsWhileBlocked &&
!(c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_BLOCKED)) &&
io_threads_op == IO_THREADS_OP_IDLE)
{
// 把该client 假如到server的clients_pending_read中,放到list头
listAddNodeHead(server.clients_pending_read中,c);
// 互相引用一下
c->pending_read_list_node = listFirst(server.clients_pending_read);
return 1;
} else {
return 0;
}
}

4. IO线程创建后 持续干活,会调用IOThreadMain

void *IOThreadMain(void *myid) {
/* The ID is the thread number (from 0 to server.iothreads_num-1), and is
* used by the thread to just manipulate a single sub-array of clients. */
// myid 也是对应的list数组的id
long id = (unsigned long)myid;
char thdname[16]; ...... while(1) {
...... /* Process: note that the main thread will never touch our list
* before we drop the pending count to 0. */
listIter li;
listNode *ln;
// 拿到对应的 list
listRewind(io_threads_list[id],&li);
// 遍历list
while((ln = listNext(&li))) {
client *c = listNodeValue(ln);
if (io_threads_op == IO_THREADS_OP_WRITE) {
writeToClient(c,0);
} else if (io_threads_op == IO_THREADS_OP_READ) {
// 执行读取操作
readQueryFromClient(c->conn);
} else {
serverPanic("io_threads_op value is unknown");
}
}
listEmpty(io_threads_list[id]);
setIOPendingCount(id, 0);
}
}
那么什么时候去将io_threads_op 设置为IO_THREADS_OP_READ
在eventloop 的 beforeSleep中, beforeSleep 会调用handleClientsWithPendingReadsUsingThreads
int handleClientsWithPendingReadsUsingThreads(void) {
......
/* Distribute the clients across N different lists. */
listIter li;
listNode *ln;
// 获取之前postponeClientRead中添加到list中 的所有元素list
listRewind(server.clients_pending_read,&li);
int item_id = 0;
while((ln = listNext(&li))) {
client *c = listNodeValue(ln);
int target_id = item_id % server.io_threads_num;
// 将元素即对应的client,添加到对应的线程的list中去
listAddNodeTail(io_threads_list[target_id],c);
item_id++;
} /* Give the start condition to the waiting threads, by setting the
* start condition atomic var. */
// 将io线程标志 置为 IO_THREADS_OP_READ
io_threads_op = IO_THREADS_OP_READ;
// 设置所有 IO线程 待处理的请求的个数,求和
for (int j = 1; j < server.io_threads_num; j++) {
int count = listLength(io_threads_list[j]);
setIOPendingCount(j, count);
} /* Also use the main thread to process a slice of clients. */
listRewind(io_threads_list[0],&li);
// 主线程也有自己的 list,即index == 0 的位置的list, 在这里也参与IO操作,不浪费一点性能
while((ln = listNext(&li))) {
client *c = listNodeValue(ln);
readQueryFromClient(c->conn);
}
listEmpty(io_threads_list[0]); /* Wait for all the other threads to end their work. */
// 这里是再次求和,知道发现所有的客户端请求完成才退出循环
while(1) {
unsigned long pending = 0;
for (int j = 1; j < server.io_threads_num; j++)
pending += getIOPendingCount(j);
if (pending == 0) break;
} // 这里都执行完成了,再讲IO线程标志位职位IDLE
io_threads_op = IO_THREADS_OP_IDLE;
......
}
从源码中的解释也可以看出,虽然加入了多线程,加快了命令处理的速度,但是如果某个命令特别耗时,
会影响 beforeSleep函数一直卡住,导致后续的命令一直pending,不能加入到io线程的list中

所以还是要注意命令的执行效率

5. 最后每个线程单独调用 readQueryFromClient, 由上面分析,当前io_threads_op 为 read 状态
void readQueryFromClient(connection *conn) {
client *c = connGetPrivateData(conn);
int nread, big_arg = 0;
size_t qblen, readlen; /* Check if we want to read from the client later when exiting from
* the event loop. This is the case if threaded I/O is enabled. */
// 此时不会return, 会往下执行命令操作了
if (postponeClientRead(c)) return;
......
} int postponeClientRead(client *c) {
// io线程来的, io_threads_op 为 read,返回0
if (server.io_threads_active &&
server.io_threads_do_reads &&
!ProcessingEventsWhileBlocked &&
!(c->flags & (CLIENT_MASTER|CLIENT_SLAVE|CLIENT_BLOCKED)) &&
io_threads_op == IO_THREADS_OP_IDLE)
{
listAddNodeHead(server.clients_pending_read,c);
c->pending_read_list_node = listFirst(server.clients_pending_read);
return 1;
} else {
return 0;
}
}

到此分析结束

多线程模型为 主线程接收 accept 和 read, write, 但不操作,将对应的client 假如到对应iothread的list中,然后由子线程处理。

redis7源码分析:redis 多线程模型解析的更多相关文章

  1. Memcached源码分析之线程模型

    作者:Calix 一)模型分析 memcached到底是如何处理我们的网络连接的? memcached通过epoll(使用libevent,下面具体再讲)实现异步的服务器,但仍然使用多线程,主要有两种 ...

  2. Spring源码分析之AOP从解析到调用

    正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...

  3. springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)

    在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...

  4. spring源码分析系列 (15) 设计模式解析

    spring是目前使用最为广泛的Java框架之一.虽然spring最为核心是IOC和AOP,其中代码实现中很多设计模式得以应用,代码看起来简洁流畅,在日常的软件设计中很值得借鉴.以下是对一些设计模式的 ...

  5. 【Redis】事件驱动框架源码分析(多线程)

    IO线程初始化 Redis在6.0版本中引入了多线程,提高IO请求处理效率. 在Redis Server启动函数main(server.c文件)中初始化服务之后,又调用了InitServerLast函 ...

  6. jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...

  7. Spring源码分析(九)解析默认标签中的自定义标签元素

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 到这里我们已经完成了分析默认标签的解析与提取过程,或许涉及的内容太多,我 ...

  8. Spring源码分析(六)解析和注册BeanDefinitions

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 当把文件转换为Document后,接下来的提取及注册bean就是我们的重 ...

  9. mybatis源码分析之02配置文件解析

    该篇正式开始学习mybatis的源码,本篇主要学习mybatis是如何加载配置文件mybatis-config.xml的, 先从测试代码入手. public class V1Test { public ...

  10. trinitycore 魔兽服务器源码分析(三) 多线程相关

    先看LockedQueue.h template <class T, typename StorageType = std::deque<T> >class LockedQue ...

随机推荐

  1. 人大金仓学习之一_kwr的简单学习

    人大金仓学习之一_kwr的简单学习 摘要 周末在家想着学习一下数据库相关的内容. 网上找了不少资料, 想着直接在本地机器上面进行一下安装与验证 理论上linux上面应该更加简单. windows 上面 ...

  2. [转帖]金仓数据库KingbaseES V8R6索引坏块故障处理

    案例说明: 在执行表数据查询时,出现下图所示错误,索引故障导致表无法访问,后重建索引问题解决.本案例复现了此类故障解决过程. 适用版本: KingbaseES V8R3/R6 一.创建测试环境 # 表 ...

  3. CPU实际频率查看

    cat /sys/devices/system/cpu/cpu3/cpufreq/cpuinfo_cur_freq

  4. [转帖]天行健,国产CPU当自强不息

      https://baijiahao.baidu.com/s?id=1699201892754975586 本页面的文字和图像允许在CC-BY-SA 3.0协议四和GNU自由文档许可证下修改和再使用 ...

  5. Redis 的简单学习与整理

    Redis 的简单学习与整理 背景 最近一直进行Redis性能调优和高可用的课题 但是不管什么课题,第一步应该是学习与使用redis 之前总结过 rdb 与 rdr 分析 键值对等内容. 但是发现想要 ...

  6. PHP伪协议与文件包含漏洞

    https://www.cnblogs.com/weak-chicken/p/12275806.html 1 file:// - 访问本地文件系统 2 http:// - 访问 HTTP(s) 网址 ...

  7. 消息队列RabbitMQ教程

    RabbitMQ教程 翻译自RabbitMQ Tutorials. 0. 准备 前期准备 1. Hello World 最简入门教程 2. 工作队列 竞争消费者模式 3. 发布/订阅 同时发送消息给多 ...

  8. [1] 以逆向的角度来看流程控制语句——if

    [1] 以逆向的角度来看流程控制语句--if 1. if语句(单分支) ​ if语句转换的条件跳转指令与if语句的判断结果是相反的, 因为C语言是根据代码行的位置决定编译后二进制代码地址高低的,即低行 ...

  9. 强大的AWS lambda

    AWS强大的lambda 自从几年前换工作后,我所参与的项目一直都是基于AWS云服务的架构,我慢慢对serverless的相关基础建设有了一定了解和实践经验.其中lambda是我心中最强大的serve ...

  10. JavaScript获取浏览器的显示区域大小测试

    JavaScript获取浏览器的显示区域大小测试 Now we get the screen size about this browser 网页可见区域宽 document.body.clientW ...