我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Redis请求处理的流程分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Redis请求处理的流程分析

本文使用的Redis 5.0源码

感觉这部分的代码还是挺有意思的,我尽量用比较通俗的方式进行讲解

概述#

我记得我在 一文说透 Go 语言 HTTP 标准库 这篇文章里面解析了对于 Go 来说是如何创建一个 Server 端程序的:

  • 首先是注册处理器;
  • 开启循环监听端口,每监听到一个连接就会创建一个 Goroutine;
  • 然后就是 Goroutine 里面会循环的等待接收请求数据,然后根据请求的地址去处理器路由表中匹配对应的处理器,然后将请求交给处理器处理;

用代码表示就是这样:

func (srv *Server) Serve(l net.Listener) error { 
    ...
    baseCtx := context.Background()  
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        // 接收 listener 过来的网络连接
        rw, err := l.Accept()
        ... 
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) 
        // 创建协程处理连接
        go c.serve(connCtx)
    }
}

对于 Redis 来说就有些不太一样,因为它是单线程的,无法使用多线程处理连接,所以 Redis 选择使用基于 Reactor 模式的事件驱动程序来实现事件的并发处理。

Redis请求处理的流程分析

在 Redis 中所谓 Reactor 模式就是通过 epoll 来监听多个 fd,每当这些 fd 有响应的时候会以事件的形式通知 epoll 进行回调,每一个事件都有一个对应的事件处理器。

如: accept 对应 acceptTCPHandler 事件处理器、read & write 对应readQueryFromClient 事件处理器等,然后通过事件的循环派发的形式将事件分配给事件处理器进行处理。

所以说上面的这个 Reactor 模式都是通过 epoll 来实现的,对于 epoll 来说主要有这三个方法:

//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大
int epoll_create(int size);


int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);


int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

所以我们可以根据这三个方法实现一个简单的 server:

// 创建监听
int listenfd = ::socket();

// 绑定ip和端口
int r = ::bind();  
// 创建 epoll 实例
int epollfd = epoll_create(xxx); 
// 添加epoll要监听的事件类型
int r = epoll_ctl(..., listenfd, ...);
 
struct epoll_event* alive_events =  static_cast<epoll_event*>(calloc(kMaxEvents, sizeof(epoll_event)));

while (true) {
    // 等待事件
    int num = epoll_wait(epollfd, alive_events, kMaxEvents, kEpollWaitTime);
	// 遍历事件,并进行事件处理
    for (int i = 0; i < num; ++i) {
        int fd = alive_events[i].data.fd;
        // 获取事件
        int events = alive_events[i].events;
		// 进行事件的分发
        if ( (events & EPOLLERR) || (events & EPOLLHUP) ) {
            ...
        } else  if (events & EPOLLRDHUP) {
            ...
        } 
        ...
    }   
}

调用流程#

所以根据上面的介绍,可以知道对于 Redis 来说一个事件循环无非也就这么几步:

  • 注册事件监听及回调函数;
  • 循环等待获取事件并处理;
  • 调用回调函数,处理数据逻辑;
  • 回写数据给 Client;

Redis请求处理的流程分析

  • 注册 fd 到 epoll 中,并设置回调函数 acceptTcpHandler,如果有新连接那么会调用回调函数;
  • 启动一个死循环调用 epoll_wait 等待并持续处理事件,待会我们回到 aeMain 函数中循环调 aeProcessEvents 函数;
  • 当有网络事件过来的时候,会顺着回调函数 acceptTcpHandler 一路调用到 readQueryFromClient 进行数据的处理,readQueryFromClient 会解析 client 的数据,找到对应的 cmd 函数执行;
  • Redis 实例在收到客户端请求后,会在处理客户端命令后,将要返回的数据写入客户端输出缓冲区中而不是立马返回;
  • 然后在 aeMain 函数每次循环时都会调用 beforeSleep 函数将缓冲区中的数据写回客户端;

上面的整个事件循环的过程实际上代码步骤已经写的非常清晰,网上也有很多文章介绍,我就不多讲了。

命令执行过程 & 回写客户端#

命令执行#

下面我们讲点网上很多文章都没提及的,看看 Redis 是如何执行命令,然后存入缓存,以及将数据从缓存写回 Client 这个过程。

Redis请求处理的流程分析

在前一节我们也提到了,如果有网络事件过来的时候会调用到 readQueryFromClient 函数,它是真正执行命令的地方。我们也就顺着这个方法一直往下看:

  • readQueryFromClient 里面会调用 processInputBufferAndReplicate 函数处理请求的命令;
  • 在 processInputBufferAndReplicate 函数里面会调用 processInputBuffer 以及判断一下如果是集群模式的话,是否需要将命令复制给其他节点;
  • processInputBuffer 函数里面会循环处理请求的命令,并根据请求的协议调用 processInlineBuffer 函数,将 redisObject 对象后调用 processCommand 执行命令;
  • processCommand 在执行命令的时候会通过 lookupCommand 去 server.commands 表中根据命令查找对应的执行函数,然后经过一系列的校验之后,调用相应的函数执行命令,调用 addReply 将要返回的数据写入客户端输出缓冲区;

server.commands会在 populateCommandTable 函数中将所有的 Redis 命令注册进去,作为一个根据命令名获取命令函数的表。

比如说,要执行 get 命令,那么会调用到 getCommand 函数:

void getCommand(client *c) {
    getGenericCommand(c);
}

int getGenericCommand(client *c) {
    robj *o;
	// 查找数据
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
        return C_OK;
    ...
}

robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
    //到db中查找数据
    robj *o = lookupKeyRead(c->db, key);
    // 写入到缓存中
    if (!o) addReply(c,reply);
    return o;
}

在 getCommand 函数中查找到数据,然后调用 addReply 将要返回的数据写入客户端输出缓冲区。

数据回写客户端#

在上面执行完命令写入到缓冲区后,还需要从缓冲区取出数据返回给 Client。对于数据回写客户端这个流程来说,其实也是在服务端的事件循环中完成的。

Redis请求处理的流程分析

  • 首先 Redis 会在 main 函数中调用 aeSetBeforeSleepProc 函数将回写包的函数 beforeSleep 注册到 eventLoop 中去;
  • 然后 Redis 在调用 aeMain 函数进行事件循环的时候都会判断一下 beforesleep 有没有被设值,如果有,那么就会进行调用;
  • beforesleep 函数里面会调用到 handleClientsWithPendingWrites 函数,它会调用 writeToClient 将数据从缓冲区中回写给客户端;

总结#

这篇文章介绍了整个 Redis 的请求处理模型到底是怎样的。从注册监听 fd 事件到执行命令,到最后将数据回写给客户端都做了个大概的分析。当然这篇文章也和我以往的文章有点不同,没有长篇大论的贴代码,主要我觉得也没啥必要,感兴趣可以顺着流程图去看看代码。

Reference

http://www.dre.vanderbilt.edu/~schmidt/PDF/reactor-siemens.pdf

https://time.geekbang.org/column/article/408491

http://remcarpediem.net/article/1aa2da89/

https://github.com/Junnplus/blog/issues/37

https://www.cnblogs.com/neooelric/p/9629948.html

到此这篇关于Redis 是如何进行请求处理的文章就介绍到这了,更多相关Redis请求处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Redis请求处理的流程分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Redis请求处理的流程分析

目录概述#调用流程#命令执行过程 & 回写客户端#命令执行#数据回写客户端#总结#Reference本文使用的Redis 5.0源码感觉这部分的代码还是挺有意思的,我尽量用比较通俗的方式进行讲解概述#我记得我在 一文说透 Go 语言
2022-07-25

Tomcat9请求处理流程与启动部署过程的示例分析

这篇文章主要为大家展示了“Tomcat9请求处理流程与启动部署过程的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Tomcat9请求处理流程与启动部署过程的示例分析”这篇文章吧。Over
2023-06-02

springmvc处理请求的流程是什么

Spring MVC处理请求的流程如下:1. 客户端发送请求到DispatcherServlet,DispatcherServlet是前端控制器。2. DispatcherServlet根据请求的URL调用HandlerMapping,Ha
2023-08-18

Nginx请求处理流程是怎样的

这篇文章主要介绍“Nginx请求处理流程是怎样的”,在日常操作中,相信很多人在Nginx请求处理流程是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Nginx请求处理流程是怎样的”的疑惑有所帮助!接下来
2023-06-04

RocketMQ broker 消息投递流程处理PULL_MESSAGE请求解析

这篇文章主要为大家介绍了RocketMQ broker 消息投递流程处理PULL_MESSAGE请求源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-14

ASP.NET处理HTTP请求的流程是什么

这篇文章主要介绍“ASP.NET处理HTTP请求的流程是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ASP.NET处理HTTP请求的流程是什么”文章能帮助大家解决问题。一、ASP.NET处理管
2023-06-30

浏览器请求流程和PHP对请求的处理方法是什么

这篇“浏览器请求流程和PHP对请求的处理方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“浏览器请求流程和PHP对请求
2023-07-05

如何用源码分析Struts2请求处理及过程

这期内容当中小编将会给大家带来有关如何用源码分析Struts2请求处理及过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.1 Struts2请求处理1. 一个请求在Struts2框架中的处理步骤:a)
2023-06-17

netty服务端处理请求联合pipeline分析

这篇文章主要为大家介绍了netty服务端处理请求联合pipeline示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-17

Retrofit网络请求和响应处理源码分析

本篇内容主要讲解“Retrofit网络请求和响应处理源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Retrofit网络请求和响应处理源码分析”吧!网络请求在使用 Retrofit 发起网
2023-07-05

编程热搜

目录