想从Redis订阅源码里搞懂它到底咋运作,边看边学那种深入理解的感觉
- 问答
- 2026-01-26 09:30:56
- 6
打开Redis的源码,想搞懂订阅是咋运作的,那种边看边学的感觉就像自己动手拆开一个钟表,看看里面的齿轮是怎么转的,Redis的代码放在GitHub上(引用来源:Redis官方GitHub仓库),我直接下载下来,从主文件开始翻,一开始可能有点晕,因为全是C语言写的,但别担心,慢慢看就能摸出门道。
订阅功能在Redis里就是Pub/Sub,简单说就是有人发消息、有人收消息,我先从命令入手,在源码里搜“subscribe”,找到了处理订阅命令的函数,叫subscribeCommand,这个函数不长,它调用了另一个函数pubsubSubscribeChannel,这里才是真正干活的地方,看进去,Redis用了一个叫字典的结构(其实就是个高级点的表格),来存每个频道和谁订阅了它,你订阅了一个叫“新闻”的频道,Redis就把你的连接信息加到“新闻”这个频道的名单里,代码里用链表来存这些名单,想象一下,就像在一个本子上记名字,每个频道一页纸,谁订阅了就写上去。
接着看发布消息的部分,找publishCommand函数,它处理发布命令,这里挺直接的:先根据频道名找到那个订阅者名单,然后一个个通知,通知的方式是把消息数据塞到每个客户端的输出缓冲区里,等会儿再发出去,Redis是单线程的,所以它不马上发,而是等系统有空了再处理,这样不会卡住其他操作,边看边想,我意识到这就像邮差送信,把信放进每个人的邮箱,而不是当场喊人出来拿。
再往下挖,我发现订阅还有模式匹配的功能,比如用通配符订阅多个频道,代码里用了另一个字典来存这些模式,当发布消息时,除了找精确匹配的频道,还得把所有模式过一遍,看看有没有匹配的,匹配的逻辑就是简单的字符串比较,支持星号和问号,代码在pubsubPublishMessage函数里,读到这里,我有点感慨:Redis没搞太复杂的东西,就用基础方法实现了灵活的功能。
边看边学,我还会注意一些细节,当客户端断开连接时,Redis怎么清理订阅关系?在freeClient函数里,它会从所有频道的订阅名单中移除这个客户端,避免垃圾数据,这让我明白订阅是临时性的,一断开就没了,所以不适合存重要消息,客户端结构体里有专门字段记录订阅了哪些频道和模式,这样服务器能快速查找。

网络传输部分也挺有意思,在事件循环文件(比如ae.c)里,Redis用非阻塞IO来处理读写,当有消息要发送给订阅者时,数据先缓存在内存里,等网络可写了再发出去,这让我理解了为什么Redis能同时处理那么多连接——它不干等,而是把任务安排好,轮流做。
通过阅读源码,我还看到了Redis如何避免内存问题,发布消息时,消息内容会被复制给每个订阅者,而不是共享同一份数据,这样可以防止意外修改,代码里用了复制函数,确保数据安全,这种设计虽然耗点内存,但简单可靠。
整个过程里,我经常自己写点小测试来验证,修改源码加几行打印日志,看看执行顺序,这样一动手,理解就深了,我发现订阅命令执行后,客户端会被标记为订阅状态,这样服务器就知道这个连接在等消息,而不是发普通命令,这种状态管理在代码里通过标志位实现,挺巧妙的。

越看越觉得,Redis的代码就像一本开源的书,没太多注释,但结构清晰,函数名和变量名起得直白,比如pubsub_channels一看就知道存频道订阅关系,我边看边画流程图,把数据怎么流动的理清楚:从命令解析,到更新订阅字典,再到发布时遍历发送,最后网络传输,一环扣一环,简洁有力。
也有费解的时候,模式订阅的匹配算法,一开始我没看懂怎么遍历的,后来多看了几遍,发现它用了两层循环:先处理普通频道,再扫描所有模式,这可能会慢点,但代码写得好懂,牺牲点效率换可维护性。
读源码到后来,我甚至能猜出一些设计选择,Redis的Pub/Sub不持久化,可能是因为它想保持快速轻量,代码里没看到存磁盘的部分,全是内存操作,这让我理解为什么它适合实时消息,不适合重要数据备份。
边看Redis订阅源码边学,那种深入理解的感觉就像自己变成了代码的一部分,能摸到每个逻辑的脉搏,从开始的一头雾水,到慢慢看清全貌,这种体验不是读文档能比的,它教会我,系统设计不一定要复杂,把基础做好就行,C语言的代码虽然底层,但反而更容易看到本质,没有太多隐藏的魔法,通过这种探索,我不仅搞懂了Redis订阅咋运作,还学到了怎么读大型项目源码——耐心点,从入口跟踪,多思考为什么这样写,这就像一场探险,每一步都有新发现,最终让你有种“原来如此”的畅快感。
本文由召安青于2026-01-26发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://grpk.haoid.cn/wenda/86129.html
