標籤:
雖然Redis有訂閱功能,但是訂閱功能是即時的,過了這個點,就接收不到訊息了。
同時,如果訂閱的用戶端因為某些特殊原因shutdown了,那也就找不回未處理完整的訂閱事件了。
但好在,Redis還有一個訊息佇列,通過訊息佇列,我們不僅可以把發布提交的更快速(發布會遍曆所有訂閱者,並通知到所有訂閱者),又可能不用擔心訂閱者遺漏掉發生過異常的通知。
我將在後續隨筆通過swoole,phpredis,yii三者來協調訂閱者,郵局,發行者角色。
PUBLISH 命令的實際實現由 pubsubPublishMessage 函數完成,它的完整定義如下:
// 發送訊息 int pubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; struct dictEntry *de; listNode *ln; listIter li; /* Send to clients listening for that channel */ // 向所有頻道的訂閱者發送訊息 de = dictFind(server.pubsub_channels,channel); if (de) { list *list = dictGetVal(de); // 取出所有訂閱者 listNode *ln; listIter li; // 遍曆所有訂閱者, 向它們發送訊息 listRewind(list,&li); while ((ln = listNext(&li)) != NULL) { redisClient *c = ln->value; addReply(c,shared.mbulkhdr[3]); addReply(c,shared.messagebulk); addReplyBulk(c,channel); // 列印頻道名 addReplyBulk(c,message); // 列印訊息 receivers++; // 更新接收者數量 } } /* Send to clients listening to matching channels */ // 向所有被匹配模式的訂閱者發送訊息 if (listLength(server.pubsub_patterns)) { listRewind(server.pubsub_patterns,&li); // 取出所有模式 channel = getDecodedObject(channel); while ((ln = listNext(&li)) != NULL) { pubsubPattern *pat = ln->value; // 取出模式 // 如果模式和 channel 匹配的話 // 向這個模式的訂閱者發送訊息 if (stringmatchlen((char*)pat->pattern->ptr, sdslen(pat->pattern->ptr), (char*)channel->ptr, sdslen(channel->ptr),0)) { addReply(pat->client,shared.mbulkhdr[4]); addReply(pat->client,shared.pmessagebulk); addReplyBulk(pat->client,pat->pattern); // 列印被匹配的模式 addReplyBulk(pat->client,channel); // 列印頻道名 addReplyBulk(pat->client,message); // 列印訊息 receivers++; // 更新接收者數量 } } decrRefCount(channel); // 釋放用過的 channel } return receivers; // 返回接收者數量 }
|
Redis 實現高效不遺漏的事件封裝