首先是注册处理器;
开启循环监听端口,每监听到一个连接就会创建一个 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 中所谓 Reactor 模式就是通过 epoll 来监听多个 fd,每当这些 fd 有响应的时候会以事件的形式通知 epoll 进行回调,每一个事件都有一个对应的事件处理器。
如: accept 对应 acceptTCPHandler 事件处理器、read & write 对应readQueryFromClient 事件处理器等,然后通过事件的循环派发的形式将事件分配给事件处理器进行处理。
所以说上面的这个 Reactor 模式都是通过 epoll 来实现的,对于 epoll 来说主要有这三个方法:
//创建一个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:
// 创建监听
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 实例在收到客户端请求后,会在处理客户端命令后,将要返回的数据写入客户端输出缓冲区中而不是立马返回;
Service深入分析 WORD版
本文档主要讲述的是Service深入分析;我们还是从Service的根本意义分析入手,服务的本质就是响应客户端请求。要提供服务,就必须建立接收请求,处理请求,应答客服端的框架。我想在Android Service设计者也会无时不刻把这个服务本质框图挂在脑海中。从程序的角度,服务一定要存在一个闭合循环框架和请求处理框架。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
0
查看详情
然后在 aeMain 函数每次循环时都会调用 beforeSleep 函数将缓冲区中的数据写回客户端;
上面的整个事件循环的过程实际上代码步骤已经写的非常清晰,网上也有很多文章介绍,我就不多讲了。
下面我们讲点网上很多文章都没提及的,看看 Redis 是如何执行命令,然后存入缓存,以及将数据从缓存写回 Client 这个过程。

在前一节我们也提到了,如果有网络事件过来的时候会调用到 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 将要返回的数据写入客户端输出缓冲区。
在将命令写入缓冲区后,还需要从缓冲区取出数据并返回给客户端。对于数据回写客户端这个流程来说,其实也是在服务端的事件循环中完成的。

首先 Redis 会在 main 函数中调用 aeSetBeforeSleepProc 函数将回写包的函数 beforeSleep 注册到 eventLoop 中去;
然后 Redis 在调用 aeMain 函数进行事件循环的时候都会判断一下 beforesleep 有没有被设值,如果有,那么就会进行调用;
beforesleep 函数里面会调用到 handleClientsWithPendingWrites 函数,它会调用 writeToClient 将数据从缓冲区中回写给客户端。
以上就是Redis请求处理的流程是什么的详细内容,更多请关注其它相关文章!
# 来实现
# seo与smo
# 兴义seo
# 海珠品牌网站推广
# 淘宝标题相关seo
# 青岛建设网站公司
# 网站 SEO什么意思
# 山西企业营销推广流程
# 店面下乡推广营销
# 万年网站推广公司
# 常德seo公司选择火星
# redis
# 它是
# 区中
# 也就
# 多线程
# 就会
# 会在
# 句柄
# 回调
# 客户端
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
2025考研成绩查询时间入口分享
江苏大剧院会员卡购买步骤
高德地图导航路线偏差报警频繁怎么办 高德地图路线偏差修复与优化方法
iPhone14开启Apple TV遥控设置
iPhone14无法连接蓝牙设备如何解决
如何在Podman容器中运行Composer_Docker替代品Podman的PHP与Composer容器化实践
快递查询,一键速查
如何在CSS中使用伪类:valid实现表单验证提示_结合:valid改变边框颜色
抖音号显示企业机构号是什么意思?企业机构号申请条件是什么?
C++二维数组动态分配方法_C++指针与数组内存布局
Windows 11怎么删除恢复分区_Windows 11使用Diskpart命令强行删除分区
抖音小程序怎么开通?小程序开通条件是什么?
Apple Music无故扣费引质疑
发博客与长微博技巧
《广发易淘金》国债逆回购操作教程
以下哪一项是古代兵书三十六计中的计谋
教育查询官方网站入口 教育个人档案查询免费官网
掌握CSS :has() 选择器:父选择器、嵌套限制与常见陷阱解析
PHP中获取HTTP响应状态消息:方法与限制
《理想汽车》权限管理设置方法
CDR如何复制交互式填充色
C++ virtual析构函数作用_C++基类虚析构函数防止内存泄漏
如何查询国外邮政编码_国外邮政编码查询的多种有效途径
Yandex浏览器官方入口_Yandex搜索引擎中文版
如何在Python中安全地将环境变量转换为整数并满足Mypy类型检查
国际经济与贸易就业方向解析
小米倒班助手添加日历提醒
J*aScript:从子元素中批量移除特定CSS类
QQ网页版入口导航 QQ网页版在线访问通道
PPT智能排版生成入口 免费PPT内容自动生成平台
Win11怎么录屏_Windows 11自带Xbox Game Bar录制视频
mysql如何回滚事务_mysql ROLLBACK事务回滚方法
小红书网页版怎么进 小红书网页版通用入口
BunnyStream TUS视频上传指南:解决401认证错误与参数配置
163邮箱网页版入口 163邮箱在线使用
一点万象签到领积分指南
纯CSS实现自适应宽度与响应式布局的水平按钮组
Yandex世界探索 最新官方免登录入口全知道
银信通自动开通原因揭秘
C++怎么解决数值计算中的精度问题_C++浮点数误差与数值稳定性分析
使用 J*aScript 随机化 CSS Grid 布局中的元素顺序
4399造梦西游3无敌版_4399游戏入口
《糖豆》添加舞曲方法
J*aScript模块加载器_RequireJS原理分析
狙击外星人小游戏在线链接_狙击外星人小游戏网页链接
C++如何将字符串转换为大写或小写_C++ transform函数的使用技巧
cad视图选项卡不见了怎么办_cad视图标签恢复显示方法
《大润发优鲜》充值方法介绍
Sublime怎么格式化HTML代码_Sublime前端代码美化插件使用指南
中通快递官网指定查询 中通快递单号查询平台入口
2023-06-01
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。