1.redis源码解读(一):事件驱动的命码w命令io模型,为什么,令源是命码w命令什么,怎么做
2.优秀程序员必备的令源14款效率工具
redis源码解读(一):事件驱动的io模型,为什么,命码w命令是令源中通打印 源码什么,怎么做
Redis作为一个高性能的命码w命令内存数据库,因其出色的令源读写性能和丰富的数据结构支持,已成为互联网应用不可或缺的命码w命令中间件之一。阅读其源码,令源可以了解其内部针对高性能和分布式做的命码w命令种种设计,包括但不限于reactor模型(单线程处理大量网络连接),令源定时任务的命码w命令实现(面试常问),分布式CAP BASE理论的令源实际应用,高效的命码w命令数据结构的实现,其次还能够通过大神的代码学习C语言的编码风格和技巧,让自己的代码更加优雅。
下面进入正题:为什么需要事件驱动的io模型
我们可以简单地将一个服务端程序拆成三部分,接受请求->处理请求->返回结果,其中接收请求和处理请求便是我们常说的网络io。那么网络io如何实现呢,首先我们介绍最基础的io模型,同步阻塞式io,也是很多同学在学校所学的“网络编程”。
使用同步阻塞式io的单线程服务端程序处理请求大致有以下几个步骤
其中3,4步都有可能使线程阻塞(6也会可能阻塞,这里先不讨论)
在第3步,如果没有客户端请求和服务端建立连接,那么服务端线程将会阻塞。如果redis采用这种io模型,那主线程就无法执行一些定时任务,比如过期key的她说 交友小程序源码清理,持久化操作,集群操作等。
在第4步,如果客户端已经建立连接但是没有发送数据,服务端线程会阻塞。若说第3步所提到的定时任务还可以通过多开两个线程来实现,那么第4步的阻塞就是硬伤了,如果一个客户端建立了连接但是一直不发送数据,服务端便会崩溃,无法处理其他任何请求。所以同步阻塞式io肯定是不能满足互联网领域高并发的需求的。
下面给出一个阻塞式io的服务端程序示例:
刚才提到,阻塞式io的主要问题是,调用recv接收客户端请求时会导致线程阻塞,无法处理其他客户端请求。那么我们不难想到,既然调用recv会使线程阻塞,那么我们多开几个几个线程不就好了,让那些没有阻塞的线程去处理其他客户端的请求。
我们将阻塞式io处理请求的步骤改造下:
改造后,我们用一个线程去做accept,也就是获取已经建立的连接,我们称这个线程为主线程。然后获取到的每个连接开一个新的线程去处理,这样就能够将阻塞的部分放到新的线程,达到不阻塞主线程的目的,主线程仍然可以继续接收其他客户端的连接并开新的线程去处理。这个方案对高并发服务器来说是一个可行的方案,此外我们还可以使用线程池等手段来继续优化,减少线程建立和销毁的电子合同源码github开销。
将阻塞式io改为多线程io:
我们刚才提到多线程可以解决并发问题,然而redis6.0之前使用的是单线程来处理,之所以用单线程,官方给的答复是redis的瓶颈不在cpu,既然不在cpu那么用单线程可以降低系统的复杂度,避免线程同步等问题。如何在一个线程中非阻塞地处理多个socket,进而实现多个客户端的并发处理呢,那就要借助io多路复用了。
io多路复用是操作系统提供的另一种io机制,这种机制可以实现在一个线程中监控多个socket,返回可读或可写的socket,当一个socket可读或可写时再去操作它,这样就避免了对某个socket的阻塞等待。
将多线程io改为io多路复用:
什么是事件驱动的io模型(Reactor)
这里只讨论redis用到的单线程Reactor模型
事件驱动的io模型并不是一个具体的调用,而是高并发服务器的一种抽象的编程模式。
在Reactor模型中,有三种事件:
与这三种事件对应的,有三种handler,负责处理对应的事件。我们在一个主循环中不断判断是否有事件到来(一般通过io多路复用获取事件),有事件到来就调用对应的handler去处理时间。
听着玄乎,实际上也就这一张图:
事件驱动的io模型在redis中的实现
以下提及的源码版本为 5.0.8
文字的苍白的,建议参照本文最后的方法下载代码,自己调试下
整体框架
redis-server的main方法在 src/server.c 最后,在main方法中,首先进行一系列的初始化操作,最后进入进入Reactor模型的主循环中:
主循环在aeMain函数中,aeMain函数传入的智能搜索系统平台源码参数 server.el ,是一个 aeEventLoop 类型的全局变量,保存了主循环的一些状态信息,包括需要处理的读写事件、时间事件列表,epoll相关信息,回调函数等。
aeMain函数中,我们可以看到当 eventLoop->stop 标志位为0时,while循环中的内容会被重复执行,每次循环首先会调用beforesleep回调函数,然后处理时间。beforesleep函数在main函数中被注册,会进行集群状态更新、AOF落盘等任务。
之所以叫beforesleep,是因为aeProcessEvents函数中包含了获取事件和处理事件的逻辑,其中获取读写事件时通过epoll_wait实现,会将线程阻塞。
在aeProcessEvents函数中,处理读写事件和时间事件,参数flags定义了需要处理的事件类型,我们可以暂时忽略这个参数,认为读写时间都需要处理。
aeProcessEvents函数的逻辑可以分为三个部分,首先获取距离最近的时间事件,这一步的目的是为了确定epoll_wait的超时时间,并不是实际处理时间事件。
第二个部分为获取读写事件并处理,首先调用epoll_wait,获取需要处理的苹果电话助手插件源码读写事件,超时时间为第一步确定的时间,也就是说,如果在超时时间内有读写事件到来,那么处理读写时间,如果没有读写时间就阻塞到下一个时间事件到来,去处理时间事件。
第三个部分为处理时间事件。
事件注册与获取
上面我们讲了整体框架,了解了主循环的大致流程。接下来我们来看其中的细节,首先是读写事件的注册与获取。
redis将读、写、连接事件用结构aeFileEvent表示,因为这些事件都是通过epoll_wait获取的。
事件的具体类型通过mask标志位来区分。aeFileEvent还保存了事件处理的回调函数指针(rfileProc、wfileProc)和需要读写的数据指针(clientData)。
既然读写事件是通过epoll io多路复用实现,那么就避不开epoll的三部曲 epoll_create epoll_ctrl epoll_wait,接下来我们看下redis对epoll接口的封装。
我们之前提到aeMain函数的参数是一个 aeEventLoop 类型的全局变量,aeEventLoop中保存了epoll文件描述符和epoll事件。在aeApiCreate函数(src/ae_epoll.c)中,会调用epoll_create来创建初始化epoll文件描述符和epoll事件,调用关系为 main -> initServer -> aeCreateEventLoop -> aeApiCreate
调用epoll_create创建epoll后,就可以添加需要监控的文件描述符了,需要监控的情形有三个,一是监控新的客户端连接连接请求,二是监控客户端发送指令,也就是读事件,三是监控客户端写事件,也就是处理完了请求写回结果。
这三种情形在redis中被抽象为文件事件,文件事件通过函数aeCreateFileEvent(src/ae.c)添加,添加一个文件事件主要包含三个步骤,通过epoll_ctl添加监控的文件描述符,指定回调函数和指定读写缓冲区。
最后是通过epoll_wait来获取事件,上文我们提到,在每次主循环中,首先根据最近到达的时间事件来计算epoll_wait的超时时间,然后调用epoll_wait获取事件,再处理事件,其中获取事件在函数aeApiPoll(src/ae_epoll.c)中。
获取到事件后,主循环中会逐个调用事件的回调函数来处理事件。
读写事件的实现
写累了,有空补上……
如何使用vscode调试redis源码
编译出二进制程序
这一步有可能报错:
jemalloc是内存分配的一种更高效的实现,用于代替libc的默认实现。这里报错找不到jemalloc,我们只需要将其替换成libc默认实现就好:
如果报错:
我们可以在src目录找到一个脚本名为mkreleasehdr.sh,其中包含创建release.h的逻辑,将报错信息网上翻可以发现有一行:
看来是这个脚本没有执行权限,导致release.h没有成功创建,我们需要给这个脚本添加执行权限然后重新编译:
2. 创建调试配置(vscode)
创建文件 .vscode/launch.json,并填入以下内容:
然后就可以进入调试页面打断点调试了,main函数在 src/server.c
优秀程序员必备的款效率工具
在提高程序员的工作效率方面,选择合适的工具至关重要。以下是一些优秀程序员建议收藏的款工具,它们能够帮助提升做事效率,尤其适合新手程序员。
1. **Source Insight** - 作为一款编辑器,Source Insight 支持多种开发语言,提供强大的查找、定位、彩色显示功能。对于嵌入式开发,它能帮助理解庞大的内核、u-boot 源码内容,自动创建并维护高性能的符号数据库,提高开发效率。
2. **Beyond Compare** - 这是一款功能强大的文件对比软件,支持文件夹、文本、表格、等对比。它能够高效对比整个驱动器、文件夹,并集成 FTP 站点、云存储和压缩文件,提供强大的过滤功能,使文件管理更方便。
3. **MobaXterm** - MobaXterm 是一个集合了增强型终端、X 服务器和 Unix 命令集的工具箱,适合远程管理 Linux 服务器。它提供了一整套远程连接工具,包括 SSH、X、RDP 等,便于在本地 Windows 操作系统上使用各类 Unix 命令。
4. **Typora** - Typora 是一个功能强大的文档编辑器,支持 Markdown 语法,适用于写博客、读书笔记、会议纪要等。它提供简洁的界面,支持插入数学表达式、表情、表格,并能导出 PDF 和 HTML 文件。
5. **PicGo** - PicGo 是一款上传工具,可以自动将本地上传至图床,生成 URL 链接,方便在不同平台发布文章时插入。它提供简易的图床相册管理和丰富的上传方式。
6. **utools** - utools 是一个跨平台的现代桌面软件,通过自由选择丰富的插件,提供定制化的工具集合。它能通过快捷键快速呼出,支持文本、、文件等的直接操作,显著提升工作效率。
7. **QTTabBar** - QTTabBar 是一个资源管理器增强工具,允许用户通过 Ctrl+Tab 快速切换标签,支持文件预览和文件夹预览,使文件操作更加方便快捷。
8. **Internet Download Manager (IDM)** - IDM 是一款多线程下载工具,支持多媒体下载、自动捕获链接等功能,能够极大地提升下载速度。它与浏览器集成使用,支持静默下载、批量下载、计划下载任务等功能。
9. **MindMaster** - MindMaster 是一款思维导图工具,拥有丰富的主题样式和功能,包括头脑风暴、甘特图、幻灯片等,支持全平台使用,提供实时同步和导出功能。
. **draw.io** - draw.io 是一款在线绘制工具,支持流程图、UML 等多种绘图需求,拥有强大的图形库和导出功能,支持多种格式导出和云存储。
. **Snipaste** - Snipaste 是一款高效截图工具,支持屏幕截图、标注、贴图功能,提供精准的截图和自定义截图选项,以及手势操作快捷键。
. **Ditto** - Ditto 是一款剪切板增强工具,提供一键复制、粘贴、文字转等功能,支持自定义快捷键,提高日常操作效率。
. **WGestures** - WGestures 是一款鼠标手势软件,能够执行各种操作,如前进、后退、关闭窗口等,提供全局或针对特定软件的手势操作。
. **ScreenToGif** - ScreenToGif 是一款轻便的屏幕动态图捕获软件,用于快速录制屏幕动画,支持编辑功能,如删除不合适的帧、添加滤镜等。
选择合适的工具,能够极大地提升编程效率。这些工具覆盖了从代码编辑、文件管理、远程连接、下载管理、思维导图、绘图、截图、剪切板管理到手势操作等多个方面,为程序员提供了全面的支持。希望这篇文章能够帮助你发现更多适合自己的工具,从而提升工作效率。