php + Redis 寫的類似於新浪微博的feed系統
最近接了一個feed系統的外包,類似於微博那種!客戶端是ios和android,服務器用的php,數據庫用的是redis。分享下服務器和數據庫部分的功能!希望對大家有幫助。
關於redis的介紹,大家可以看這個百度百科 !
首先是用戶基本信息部分,包含賬號,昵稱,簽名,公司還有頭像,我們使用redis的hash結構(一種類似於map鍵值對的數據結構)結構如下:(大家在做的時候,還是用hgetAll的命令,這樣只會有一次的網絡請求),注意只是基本信息,諸如玩家的粉絲,關注和帖子,我們采取其他的數據結構體來存儲!
public function updateUInfo($name,$sign,$head,$from)
{
$redisCli = new Redis(ServerConfig::$redisAddr);
$redisCli->hSet("user:$this->uid",'sign',$sign);
$redisCli->hSet("user:$this->uid","name",$name);
$redisCli->hSet("user:$this->uid","head",$head);
$redisCli->hSet("user:$this->uid","from",$from);
$redisCli->set("account:$name:uid",$this->uid);
}
核心1:關注和粉絲系統!每個用戶都要維護自己的關注和粉絲系統!分別用user:$uid:followings 和 user:$uid:followers兩個字段體現,使用的是redis的集合類型(相當於java裡面的hashSet和stl中的set,集合中的元素唯一)!
收聽某用戶(mutli是redis提供的一種事務,這樣,就可以避免產生諸如,你收聽了某人,而猶豫異常,別人的粉絲中缺沒有你的現象)
public function addFollowing($tid)
{
$redisCli = new Redis(ServerConfig::$redisAddr);
if($redisCli->sismember("user:$this->uid:followings",$tid) == 1)
{
return ;
}
$redisCli->multi();
$redisCli->sAdd("user:$this->uid:followings",$tid);
$redisCli->sAdd("user:$tid:followers",$this->uid);
$redisCli->exec();
}
取消收聽某用戶:
public function removeFollowings($tid)
{
$redisCli = new Redis(ServerConfig::$redisAddr);
if($redisCli->sismember("user:$this->uid:followings",$tid) == 0)
{
return ;
}
$redisCli->multi();
$redisCli->sRem("user:$this->uid:followings",$tid);
$redisCli->sRem("user:$tid:followers",$this->uid);
$redisCli->exec();
}
核心2:下面談談帖子的更新:
首先是帖子的基本數據結構,同上面用戶的基本數據結構,采用redis的hash結構:
class post {
//put your code here
private $postId = 0;
private $timeStamp = 0;
private $uid = 0;
private $content = "";
private $subPosts = array();
private $originPostId = -1;
private $tags = array();
public function __construct($id,$time,$content,$postId) {
$this->uid = $id;
$this->timeStamp = $time;
$this->content = $content;
$this->postId = $postId;
}
public function setOriginPostId($postId)
{
$this->originPostId = $postId;
}
public function getPostId()
{
return $this->postId;
}
public function getTimeStamp()
{
return $this->timeStemp;
}
public function getUid()
{
return $this->uid;
}
public function getContent()
{
return $this->content;
}
public function getWebTitle()
{
return $this->webTitle;
}
public function pushPostId($postId)
{
$this->subPosts[] = $postId;
}
public function saveToDb()
{
$redisCli = new Redis(ServerConfig::$redisAddr);
$redisCli->hSet("post:$this->postId","uid",$this->uid);
$redisCli->hSet("post:$this->postId","time",$this->timeStamp);
$redisCli->hSet("post:$this->postId","content",$this->content);
$redisCli->hSet("post:$this->postId","originPostId",$this->originPostId);
$redisCli->set("post:$this->webTitle:$this->postId",$this->postId);
foreach($this->tags as $tag)
{
$redisCli->sAdd("post:$this->postId:tags",$tag);
}
foreach($this->subPosts as $postId)
{
$redisCli->lPush("post:$this->postId:subPost",$postId);
}
}
}
每當用戶發布一個帖子的時候,他的所有的粉絲必須看得到他的帖子才可以!這裡我們有推和拉兩種方式,對於現在的新浪微博,據說是采取推和拉結合的兩個方式,我們的小系統。推:每當用戶發表一個新貼子的時候,就將他的帖子的id主動告知他所有的粉絲。拉:用戶發布一個新的帖子什麼都不要做,而當他的粉絲請求最新的內容的時候,則需要便利他所有的關注人,取得最新的帖子,然後按照時間排序取出來。推比較消耗內存,拉則是比較消耗cpu。據說新浪微博采用推和拉結合的方式,比如當一個普通用戶則是推,但是如果是一個大明星有著幾千萬粉絲的大號,當發布一個帖子的時候,則是需要他的粉絲通過拉的方式來獲取!
用戶發布一個新帖子的操作:
public function post($title,$content,$tags)
{
$redisCli = new Redis(ServerConfig::$redisAddr);
$postId = postUtil::generateNewPostId();
$redisCli->Set("user:$this->uid:postTime",time());
$redisCli->lPush("user:$this->uid:news",$post->getPostId());
$followers = $redisCli->sMembers("user:$this->uid:followers");
foreach($followers as $follower )
{
$redisCli->lPush("user:$follower:news",$post->getPostId());
}
}
我們將所有的帖子id推到粉絲的("user:$uid:news")中,這裡采用的是順序隊列結構體!基本也就是按照時間進行了排序(最新的帖子總是左邊)!我們不會將帖子的內容全部到放到這個字段裡,而是值存放了帖子的id,用戶請求新鮮事的時候,自己再去拉取帖子的內容!
熱門用戶/熱門帖子,Redis提供了一種有序集合類型,這樣我們利用這種有序集合類型可以做熱門,熱門用戶排行和熱門帖子排行!比如我們可以根據用戶的粉絲數量做排行,很容易得到前二十名熱門用戶,根據帖子的閱讀量做熱門帖子的排行了!
這樣一個簡單的feed系統就算是完成了!但是如果要做大,確實還是有很多系統要做!