概述

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

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

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

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

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

/*

  • 可以理解为,增删改 fd 需要监听的事件
  • epfd 是 epoll_create() 创建的句柄。
  • op 表示 增删改
  • epoll_event 表示需要监听的事件,Redis 只用到了可读,可写,错误,挂断 四个状态
    */
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

/*

  • 可以理解为查询符合条件的事件
  • epfd 是 epoll_create() 创建的句柄。
  • epoll_event 用来存放从内核得到事件的集合
  • maxevents 获取的最大事件数
  • timeout 等待超时时间
    */
    int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    所以我们可以根据这三个方法实现一个简单的 server:

Copy
// 创建监听
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;

注册 fd 到 epoll 中,并设置回调函数 acceptTcpHandler,如果有新连接那么会调用回调函数;
启动一个死循环调用 epoll_wait 等待并持续处理事件,待会我们回到 aeMain 函数中循环调 aeProcessEvents 函数;
当有网络事件过来的时候,会顺着回调函数 acceptTcpHandler 一路调用到 readQueryFromClient 进行数据的处理,readQueryFromClient 会解析 client 的数据,找到对应的 cmd 函数执行;
Redis 实例在收到客户端请求后,会在处理客户端命令后,将要返回的数据写入客户端输出缓冲区中而不是立马返回;
然后在 aeMain 函数每次循环时都会调用 beforeSleep 函数将缓冲区中的数据写回客户端;
上面的整个事件循环的过程实际上代码步骤已经写的非常清晰,网上也有很多文章介绍,我就不多讲了。

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

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

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

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

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

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

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

聊聊 Redis 是如何进行请求处理相关推荐

  1. 聊聊redis分布式锁的8大坑

    在分布式系统中,由于redis分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中. 但不是说用了redis分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些 ...

  2. 聊聊 Redis 使用场景 1

    原文地址:Redis实战(五) 聊聊Redis使用场景 博客地址:http://blog.720ui.com/ 使用场景说明 计数器 数据统计的需求非常普遍,通过原子递增保持计数.例如,点赞数.收藏数 ...

  3. 聊聊redis分布式锁的8大坑!

    聊聊redis分布式锁的8大坑!不是说用了redis分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些意想不到的问题.https://mp.weixin.qq.com/s/8NFqfsQ8 ...

  4. 聊聊Redis的数据热点问题

      前两天,我们使用的某云厂商服务挂了,而且一挂就是挂大半天,我们的服务强依赖于他们,所以我们也跟着一起挂.然而我们却无能为力,只能等他们恢复.事故原因中听他们提到Redis有个热key,正好我在上家 ...

  5. 聊聊redis的HealthIndicator

    序 本文主要研究一下redis的HealthIndicator RedisHealthIndicator spring-boot-actuator-2.0.4.RELEASE-sources.jar! ...

  6. 聊聊 Redis 使用场景

    随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求.因此,Redis 基于内存存储数据,可以极大的提高查询性能,对产品在架构上很好的补充.在某些场景下,可以充分的利用 Redis 的特性 ...

  7. lua运行外部程序_一起聊聊redis(5)——c#的lua脚本应用实例之高并发抢口罩

    本节内容 展示Redis数据库在高并发下实现抢口罩的例子,文章分3个实现的模式 不加锁 乐观锁 Lua脚本 前言 写于2020.2.8日疫情严重之日,抢口罩成为每晚8点档黄金时间必备节目,在报道上看到 ...

  8. @cacheable 设置过期时间_缓存面试三连击——聊聊Redis过期策略?内存淘汰机制?再手写一个LRU 吧!...

    大家好,今天我和大家想聊一聊有关redis的过期策略的话题. 听到这里你也许会觉得:"我去,我只是个日常搬砖的,这种偏底层的知识点,我需要care吗?" 话虽如此·,但是兄die, ...

  9. 今天来聊聊 Redis 的主从复制

    作者 | 阿Q 来源 | 阿Q说代码 今天我们就从配置文件.设计原理.面试真题三个方面来聊一聊 Redis 的主从复制. 在 Redis 复制的基础上,使用和配置主从复制非常简单,能使得从 Redis ...

  10. 服务端指南 数据存储篇 | 聊聊 Redis 使用场景(转)

    作者:梁桂钊 本文,是升级版,补充部分实战案例.梳理几个场景下利用 Redis 的特性可以大大提高效率. 随着数据量的增长,MySQL 已经满足不了大型互联网类应用的需求.因此,Redis 基于内存存 ...

最新文章

  1. springboot 集成jpa_基于Spring Boot+JPA Restful 风格的数据
  2. 《Generative Face Completion》论文笔记
  3. 算法学习之路|数位dp简要分析
  4. 自动ping博客服务程序
  5. dsp调音一次多少钱_DSP调音中EQ使用技巧
  6. SAS︱数据索引、数据集常用操作(set、where、merge、append)
  7. Ubuntu中安装网易云音乐(可以直接打开的最简单的方法)
  8. android计算器编程思路,android计算器---思路以及计算器功能梳理(未完成)(示例代码)...
  9. google 常用的技术搜索关键词
  10. Android 自动检测版本更新(包含强制更新)并安装
  11. PMP项目管理是什么意思?
  12. python读书笔记—读写文件
  13. 【强化学习】手把手教你实现游戏通关AI(1)——游戏界面实现
  14. 运营技巧︱用户运营中,如何提高用户转化率
  15. 数据库(MYSQL)之元数据
  16. mysql历史数据自动归档
  17. 虚拟机安装windows10
  18. Java中如何创建一个文件或者文件夹
  19. maya 表达式编辑器无法正常打开
  20. 串口通信之————IIC(软件驱动)

热门文章

  1. 谷歌学术首页url爬取
  2. 笔记本w ndows未能启动,手把手教你windows无法启动怎么办
  3. 去掉右键的“使用skype共享”
  4. 又一个程序员突然倒地,身体这件事一定要警钟长鸣!
  5. js实现复制input隐藏域的取巧做法
  6. 干货!推荐系统中的异构关系学习
  7. LeetCode——5731. 座位预约管理系统(Seat Reservation Manager)[中等]——分析及代码(Java)
  8. ROVIO mobile webcam 路威机器人
  9. Excel 2010 VBA 入门 128 在窗体中插入控件
  10. 蓝桥本第九届省赛刷题记录