Skip to content

Instantly share code, notes, and snippets.

@sma
Created June 19, 2010 20:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sma/445239 to your computer and use it in GitHub Desktop.
Save sma/445239 to your computer and use it in GitHub Desktop.

Ein Twitter-Clone mit Redis

Angenommen, wir wollen ein System modellieren, bei dem User kurze Informationsschnipsel (Posts) an einen Server schicken können, der diese an alle User verteilt, die vorher angegeben haben, besagten Users zu folgen.

Der Redis-Treiber über Python heißt redis und wird so importiert und initialisiert. In der globalen Variable r halte ich eine persistente Datenbank-Verbindung, die ich in allen weiteren Funktionen benutzen werde:

import redis
r = redis.Redis(db=1)

Beginnen wir mit den Usern. Jeder soll einen Namen und Kennwort haben und die Liste der User haben, die ihm folgen (followers) und die, denen er folgt (following). Das ist offensichtlich redundant, aber einfacher zu verwalten. Eine ID identifiziert einen User eindeutig. Diese erzeuge ich mit einem globalen Counter.

class User:
    @classmethod
    def next_uid(cls):
        return r.incr("ids:user")

    @classmethod
    def create(cls, name, passwd):
        uid = cls.next_uid()
        r.set("user:%s:name" % uid, name)
        r.set("user:%s:passwd" % uid, passwd)
        return uid
    
    def __init__(self, uid):
        self.uid = uid
        self.name = r.get("user:%s:name" % uid)
        self.passwd = r.get("user:%s:passwd" % uid)
    
    def __repr__(self):
        return "%s [%d]" % (self.name, self.uid)

Die folgenden zwei Methoden implementieren das Verfolgen:

class User:
    def follow(self, user):
        r.sadd("user:%s:following" % self.uid, user.uid)
        r.sadd("user:%s:followers" % user.uid, self.uid)
    
    def unfollow(self, user):
        r.srem("user:%s:following" % self.uid, user.uid)
        r.srem("user:%s:followers" % user.uid, self.uid)

Nun möchte ich sehen, wer einem User folgt und wem er oder sie folgt:

class User:
    def get_followers(self):
        return set(User(uid) for uid in r.smembers("uid:%s:followers" % self.uid))
    
    def get_following(self):
        return set(User(uid) for uid in r.smembers("uid:%s:following" % self.uid))

Als nächstes implementieren wir die Posts. Sie haben einen Autor, ein Datum und einen Text. Ich speichere sie ebenfalls unter einer ID, die mit einem globalen Counter erzeugt wird. Da ich die Attribute nie einzeln brauche, speichere ich sie kompakt (in der Annahme, ein Name enthält nie ein |) in nur einem String.

class Post:
    @classmethod
    def next_uid(cls):
        return r.incr("ids:post")
    
    @classmethod
    def create(cls, author, date, text):
        uid = cls.next_uid()
        r.set("post:%s" % uid, "%s|%s|%s" % (author.name, date, text))
        return uid
    
    def __init__(self, uid):
        self.uid = uid
        self.author, self.date, self.text = r.get("post:%s" % uid).split("|", 2)

Damit ich effizient die Liste aller Posts der User anzeigen kann, denen ein User folgt (manchmal auch Freunde genannt), berechne ich sie ein Erzeugen von Posts und nicht erst beim Anzeigen. Jeder User hat eine Timeline der ich neue Posts am Anfang hinzufüge. Außerdem hat jeder User noch ein Archiv mit den eigenen Posts, wo ich ebenfalls neue Posts am Anfang hinzufüge. In beiden Fällen sind die Listen so von neu nach alt sortiert.

class User:
    def post(self, text):
        post_uid = Post.create(self, datetime.now(), text)
        r.lpush("user:%s:archive" % self.uid, post_uid)
        for uid in r.smembers("user:%s:followers" % self.uid):
            r.lpush("user:%s:timeline" % uid, post_uid)
    
    def get_archive(self, limit=20, offset=0):
        return [Post(uid) for uid in r.lrange("user:%s:archive" % self.uid, offset, limit)]
    
    def get_timeline(self, limit=20, offset=0):
        return [Post(uid) for uid in r.lrange("user:%s:timeline" % self.uid, offset, limit)]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment