多线程模式中,在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. [转帖]SSH交互式脚本StrictHostKeyChecking选项 benchmode=yes

    https://www.cnblogs.com/klb561/p/11013774.html SSH 公钥检查是一个重要的安全机制,可以防范中间人劫持等黑客攻击.但是在特定情况下,严格的 SSH 公钥 ...

  2. [转帖]java -d 参数(系统属性) 和 环境变量

    https://www.cnblogs.com/limeiyang/p/16565920.html 1. -d 参数说明 通过 java -h 查看可知: 注意:-D= : set a system ...

  3. [转帖]Nginx(3):上手Nginx,从配置文件开始

    https://cloud.tencent.com/developer/article/1886147?areaSource=&traceId=   其实吧,我配置 tcp 负载均衡的时候也就 ...

  4. 测试环境Nginx反向代理负载均衡模板说明

    公司里面为了验证 https 以及域名特点进行了相关的测试工作.  为了简单起见 将 安装文件执行了导出. 这样的话就比较简单了. 注意说明一点的是 我这边导出的工具都是 放到根目录下面 目录最简单. ...

  5. 【原创】linux为什么不是实时操作系统

    一.什么是实时操作系统(RTOS)? 可参见本博客之前的文章: 什么是实时 实时的分类 常见的RTOS latency和jitter 总结一下,实时其实说的是系统响应事件需要的时间的确定性,时间必须确 ...

  6. TS声明promise返回来的数据类型

    promise返回来的数据类型 interface backResult{ code: number, data: { name:string,age:number}[], //数组里面的对象类型,这 ...

  7. 从 WebStorm 转到 VSCode!使用一周体验报告

    前言 最近我的 Jetbrains 开源项目授权到期了,想要续订的时候发现 Jetbrains 提高了开源项目申请门槛,我的 StarBlog 项目因为名字里包含 blog 这个词无法申请,虽然我在 ...

  8. 7.3 C/C++ 实现顺序栈

    顺序栈是一种基于数组实现的栈结构,它的数据元素存储在一段连续的内存空间中.在顺序栈中,栈顶元素的下标是固定的,而栈底元素的下标则随着入栈和出栈操作的进行而变化.通常,我们把栈底位置设置在数组空间的起始 ...

  9. 本地Nuget包管理

    nuget.org有时候会抽风,VS无法自动下载程序包.这时,我们可以配置本地nuget包搜索路径. 1 下载Nuget package 以anycad rapid sdk为例,可以先从百度云盘下载最 ...

  10. 三星发布990 EVO SSD:同时支持PCIe 4.0和PCIe 5.0

    1月8日消息,三星发布了新款产品--990 EVO SSD,这是首款同时支持了PCIe 4.0 x4及PCIe 5.0 x2通道的SSD. 据了解,990 EVO面向中端市场,为2280 M.2规格, ...