BLOG
Enjoy when you can, and endure when you must.
DEC 04, 2013/数据库
构建基于Redis的简单社交网络(四)

本文部分翻译自Josiah L Carlson的《Redis in Action》,CHAPTER 8: Building a simple social network。

除了关注功能外,另外一个最基本的功能要数发布状态消息,这是用户发布分享的入口。当别人对你的内容感兴趣时,你就会受到关注。之前我们已经阐述了如何创建消息,但仅仅是局限在创建而并未更新用户自己和其粉丝的时间轴内以供展示。这正是我们这一部分重点讨论的内容。

更新时间轴的操作取决于一个用户的粉丝数。如果当前用户的粉丝数较少(例如1000以内),则可以对所有相关用户的时间轴进行即时的更新。而如果该用户的粉丝团很庞大,那恐怕一个同步的更新操作在时间的消耗上是用户不能承受的。

为了折中,我们将其拆分成两个步骤。首先对用户自己以及前1000个粉丝的时间轴进行即时更新。剩下的交由服务器在之后进行异步的操作以尽快将结果返回给用户(具有庞大粉丝团的用户毕竟属于少数中的少数,因此这样的操作在实际中的影响会很小)。以下是代码实现:

def post_status(conn, uid, message, **data):
    # 创建一条状态消息
    id = create_status(conn, uid, message, **data)
    if not id:
        return None

    posted = conn.hget('status:%s'%id, 'posted')
    if not posted:
        return None

    # 更新当前用户的时间轴
    post = {str(id): float(posted)}
    conn.zadd('profile:%s'%uid, **post)

    # 推送给粉丝
    syndicate_status(conn, uid, post)
    return id

create_status在之前已经实现,具体可以查看这里。这个函数本身主要实现了更新当前用户的时间轴。而调用的syndicate_status函数实际完成了推送给粉丝的操作,它的代码如下:

POSTS_PER_PASS = 1000  # 限制同步更新的数量
def syndicate_status(conn, uid, post, start=0):
    followers = conn.zrangebyscore('followers:%s'%uid, start, 'inf',
        start=0, num=POSTS_PER_PASS, withscores=True)
    
    pipeline = conn.pipeline(False)
    for follower, start in followers:
        pipeline.zadd('home:%s'%follower, **post)
        pipeline.zremrangebyrank(
            'home:%s'%follower, 0, -HOME_TIMELINE_SIZE-1)
    pipeline.execute()
    
    if len(followers) >= POSTS_PER_PASS:
        # 如果有更多的分许,则需后续的操作
        execute_later(conn, 'default', 'syndicate_status',
            [conn, uid, post, start])

新增功能就简单实现了,而删除操作实际会更为简单。从Redis查找一条状态消息时,如果该条记录不存在,那Python会返回空。因此利用该特点,删除操作只需将该状态消息删除并更新用户的相关计数即可:

def delete_status(conn, uid, status_id):
    key = 'status:%s'%status_id
    # 请求锁
    lock = acquire_lock_with_timeout(conn, key, 1)
    if not lock:
        return None
    
    if conn.hget(key, 'uid') != str(uid):
        return None

    # 删除操作
    pipeline = conn.pipeline(True)
    pipeline.delete(key)
    pipeline.zrem('profile:%s'%uid, status_id)
    pipeline.zrem('home:%s'%uid, status_id)
    pipeline.hincrby('user:%s'%uid, 'posts', -1)
    pipeline.execute()

    release_lock(conn, key, lock)
    return True

不过这只是一个不严谨的方法,如果真正做到完美,要考虑的还有很多很多。

经过以上几个功能的实现,一个类似于Twitter的社交网站所需的最基本功能就有了。不过这还只能说是一个开始。

COMMENTS
LEAVE COMMNT