最近忙著用redis實現乙個訊息通知系統,今天大概總結了一下技術細節,其中演示**如果沒有特殊說明,使用的都是phpredis擴充套件來實現的。
比如要推送一條全域性訊息,如果真的給所有使用者都推送一遍的話,那麼會占用很大的記憶體,實際上不管粘性有多高的產品,活躍使用者同全部使用者比起來,都會小很多,所以如果只處理登入使用者的話,那麼至少在記憶體消耗上是相當划算的,至於未登入使用者,可以推遲到使用者下次登入時再處理,如果使用者一直不登入,就一了百了了。
當大量使用者同時登入的時候,如果全部都即時處理,那麼很容易就崩潰了,此時可以使用乙個佇列來儲存待處理的登入使用者,如此一來頂多是反應慢點,但不會崩潰。
redis的list資料型別可以很自然的建立乙個佇列,**如下:
<?php出於類似的原因,我們還需要乙個佇列來儲存待處理的訊息。當然也可以使用list來實現,但list只能按照插入的先後順序實現類似fifo或lifo形式的佇列,然而訊息實際上是有優先順序的:比如說個人訊息優先順序高,全域性訊息優先順序低。此時可以使用zset來實現,它裡面分數的概念很自然的實現了優先順序。$redis = new redis;
$redis->connect('/tmp/redis.sock');
$redis->lpush('usr', );
while ($usr = $redis->rpop('usr'))
?>
不過zset沒有原生的pop操作,所以我們需要模擬實現,**如下:
<?php模擬實現了pop操作後,我們就可以使用zset實現佇列了,**如下:class redisclient extends redis
public function zrevpop($zset)
private function zsetpop($zset, $position)
if ($this->multi()->zrem($zset, $element[0])->exec())
return $this->zsetpop($zset, $position);
}}?>
<?php以前微博架構中推拉選擇的問題已經被大家討論過很多次了。實際上訊息通知系統和微博差不多,也存在推拉選擇的問題,同樣答案也是類似的,那就是應該推拉結合。具體點說:在登陸使用者獲取訊息的時候,就是乙個拉訊息的過程;在把訊息傳送給登陸使用者的時候,就是乙個推訊息的過程。$redis = new redisclient;
$redis->connect('/tmp/redis.sock');
$redis->zadd('msg', , );
while ($msg = $redis->zrevpop('msg'))
?>
假設要推送一百萬條訊息的話,那麼最直白的實現就是不斷的插入,**如下:
<?php說明:這裡我使用了set資料型別,當然你也可以視需求換成list或者zset。for ($msgid = 1; $msgid <= 1000000; $msgid++)
?>
redis的速度是很快的,但是借助pipeline,會更快,**如下:
<?php說明:所謂pipeline,就是省略了無謂的折返跑,把命令打包給服務端統一處理。for ($i = 1; $i <= 100; $i++)
$redis->exec();
}?>
前後兩段**在我的測試裡,使用pipeline的速度大概是不使用pipeline的十倍。
我們用redis命令列來演示一下使用者是如何查詢訊息的。
先插入三條訊息,其分別是1,2,3:
redis> hmset msg:1 title title1 content content1再把這三條訊息傳送給某個使用者,其是123:redis> hmset msg:2 title title2 content content2
redis> hmset msg:3 title title3 content content3
redis> sadd usr:123:msg 1此時如果簡單查詢使用者有哪些訊息的話,無疑只能查到一些:redis> sadd usr:123:msg 2
redis> sadd usr:123:msg 3
redis> smembers usr:123:msg如果還需要用程式根據再來一次查詢無疑有點低效,好在redis內建的sort命令可以達到事半功倍的效果,實際上它類似於sql中的join:1) "1"
2) "2"
3) "3"
redis> sort usr:123:msg get msg:*->titlesort的缺點是它只能get出字串型別的資料,如果你想要多個資料,就要多次get:1) "title1"
2) "title2"
3) "title3"
redis> sort usr:123:msg get msg:*->content
1) "content1"
2) "content2"
3) "content3"
redis> sort usr:123:msg get msg:*->title get msg:*->content很多情況下這顯得不夠靈活,好在我們可以採用其他一些方法平衡一下利弊,比如說新加乙個字段,冗餘儲存完整訊息的序列化,接著只get這個欄位就ok了。1) "title1"
2) "content1"
3) "title2"
4) "content2"
5) "title3"
6) "content3"
實際暴露查詢介面的時候,不會使用php等程式來封裝,因為那會成倍降低rps,推薦使用webdis,它是乙個redis的web**,效率沒得說。
…最近tumblr發表了一篇類似的文章:staircar: redis-powered notifications,介紹了他們使用redis實現訊息通知系統的一些情況,有興趣的不妨一起看看。
Redis訊息通知系統的實現
posted on 2012 02 29 by 老王 最近忙著用redis實現乙個訊息通知系統,今天大概總結了一下技術細節,其中演示 如果沒有特殊說明,使用的都是phpredis擴充套件來實現的。比如要推送一條全域性訊息,如果真的給所有使用者都推送一遍的話,那麼會占用很大的記憶體,實際上不管粘性有多...
Redis訊息通知系統的實現
最近忙著用redis實現乙個訊息通知系統,今天大概總結了一下技術細節,其中演示 如果沒有特殊說明,使用的都是phpredis擴充套件來實現的。比如要推送一條全域性訊息,如果真的給所有使用者都推送一遍的話,那麼會占用很大的記憶體,實際上不管粘性有多高的產品,活躍使用者同全部使用者比起來,都會小很多,所...
Redis訊息通知系統的實現
posted on 2012 02 29 by 老王 最近忙著用redis實現乙個訊息通知系統,今天大概總結了一下技術細節,其中演示 如果沒有特殊說明,使用的都是phpredis擴充套件來實現的。比如要推送一條全域性訊息,如果真的給所有使用者都推送一遍的話,那麼會占用很大的記憶體,實際上不管粘性有多...