一、概述
- 在之前的文章分析过,Redis的消息发布与订阅支持基于频道channel的精确订阅与基于模式pattern的模糊订阅,并且是实时的消息传输,不会进行消息存储,如下从源代码来分析Redis的订阅与发布功能的实现,主要在Redis源码的pubsub.c文件定义。
二、消息订阅
1. 客户端和服务端的订阅存储结构
- 消息订阅主要从客户端client和服务端server两个角度来分析。首先在客户端client和服务端server都会维护一个字典类型dict的pubsub_channels来维护频道channel的订阅关系,维护一个列表类型的pubsub_patterns来维护模式pattern的订阅关系,如下redisServer为服务端数据结构,client为客户端数据结构:
![2eed55e753dc08a0fdb4c69e03686dce.png](https://i-blog.csdnimg.cn/blog_migrate/826bd676b0b7e450b912e7a1734ebb1d.jpeg)
2. 频道channel的订阅数据结构
- 服务端redisServer维护的频道channel订阅字典pubsub_channels和客户端client维护的频道channel的订阅字典pubsub_channels所存储的数据的方式不一样:
- 服务端存储的是channel到所有订阅了这个频道的客户端,即channel -> clients的映射,即字典的key为频道channel,值为客户端列表clients;
- 客户端存储的是该客户端所订阅过的频道channel,即channel -> NULL,字典的key为频道channel,而值为NULL。
3. 模式pattern的订阅数据结构
- 模式pattern的订阅在服务端和客户端都是使用列表类型的pubsub_patterns来维护,内部存在的模式订阅数据如下:包含客户端的引用client和模式pattern。
typedef struct pubsubPattern { // 客户端引用 client *client; // 该 robj *pattern;} pubsubPattern;
4. 订阅实现
频道channel订阅的源码实现
- Redis服务端redisServer接收客户端订阅某个频道的方法实现如下:在服务端redisServer和客户端client的pubsub_channels都需要添加这个映射关系,核心方法为其中dictAdd函数的参数列表为(dict,key,value),所以从以下代码可知服务端value是客户端引用集合clients,客户端的value为NULL。
![2f5832d518c3804b4ed89506ce3c1308.png](https://i-blog.csdnimg.cn/blog_migrate/108ca4ee4b8791d183a72240c9f6efee.jpeg)
模式pattern订阅的源码实现
- 源码实现如下:创建模式pattern的订阅pubsubPattern的对象实例pat,pubsubPattern包含客户端引用client和模式字符串pattern,然后将pat对象分别添加到服务端的pubsub_patterns列表和客户端的pubsub_patterns列表。
![64d17f53073ab5e1f157bb7d4243d61a.png](https://i-blog.csdnimg.cn/blog_migrate/5bf430b5881c5ebc1480038e5e1e2475.jpeg)
三、消息发布
- 消息发布源码实现如下:方法参数为channel和message,即将消息message发往频道channel。在内部实现当中:
- 首先从服务端的pubsub_channels字典中查找订阅了这个频道channel的所有客户端,然后发送该消息message给这些客户端;
- 然后从服务端的pubsub_patterns列表中查找是否存在匹配这个channel的模式pattern,如果存在则将消息发送给对应的客户端client。
![79f6eaf636e715553924ebd7c60b5492.png](https://i-blog.csdnimg.cn/blog_migrate/66130e1c771e27aa44467273745be3f1.jpeg)
- 所以Redis的消息发布订阅其实就是通过方法调用指定消息内容message和该消息所属的频道channel,将该消息发送给所有订阅了这个channel的所有客户端,不会进行消息存储。