comparison content/Coding/010-redis-whos-online-return.rst @ 4:7ce6393e6d30

Adding converted blog posts from old blog.
author Brian Neal <bgneal@gmail.com>
date Thu, 30 Jan 2014 21:45:03 -0600
parents
children
comparison
equal deleted inserted replaced
3:c3115da3ff73 4:7ce6393e6d30
1 Who's Online with Redis & Python, a slight return
2 #################################################
3
4 :date: 2011-12-17 19:05
5 :tags: Redis, Python
6 :slug: who-s-online-with-redis-python-a-slight-return
7 :author: Brian Neal
8
9 In a `previous post`_, I blogged about building a "Who's Online" feature using
10 Redis_ and Python_ with redis-py_. I've been integrating Celery_ into my
11 website, and I stumbled across this old code. Since I made that post, I
12 discovered yet another cool feature in Redis: sorted sets. So here is an even
13 better way of implementing this feature using Redis sorted sets.
14
15 A sorted set in Redis is like a regular set, but each member has a numeric
16 score. When you add a member to a sorted set, you also specify the score for
17 that member. You can then retrieve set members if their score falls into a
18 certain range. You can also easily remove members outside a given score range.
19
20 For a "Who's Online" feature, we need a sorted set to represent the set
21 of all users online. Whenever we see a user, we insert that user into the set
22 along with the current time as their score. This is accomplished with the Redis
23 zadd_ command. If the user is already in the set, zadd_ simply updates
24 their score with the current time.
25
26 To obtain the curret list of who's online, we use the zrangebyscore_ command to
27 retrieve the list of users who's score (time) lies between, say, 15 minutes ago,
28 until now.
29
30 Periodically, we need to remove stale members from the set. This can be
31 accomplished by using the zremrangebyscore_ command. This command will remove
32 all members that have a score between minimum and maximum values. In this case,
33 we can use the beginning of time for the minimum, and 15 minutes ago for the
34 maximum.
35
36 That's really it in a nutshell. This is much simpler than my previous
37 solution which used two sets.
38
39 So let's look at some code. The first problem we need to solve is how to
40 convert a Python ``datetime`` object into a score. This can be accomplished by
41 converting the ``datetime`` into a POSIX timestamp integer, which is the number
42 of seconds from the UNIX epoch of January 1, 1970.
43
44 .. sourcecode:: python
45
46 import datetime
47 import time
48
49 def to_timestamp(dt):
50 """
51 Turn the supplied datetime object into a UNIX timestamp integer.
52
53 """
54 return int(time.mktime(dt.timetuple()))
55
56 With that handy function, here are some examples of the operations described
57 above.
58
59 .. sourcecode:: python
60
61 import redis
62
63 # Redis set keys:
64 USER_SET_KEY = "whos_online:users"
65
66 # the period over which we collect who's online stats:
67 MAX_AGE = datetime.timedelta(minutes=15)
68
69 # obtain a connection to redis:
70 conn = redis.StrictRedis()
71
72 # add/update a user to the who's online set:
73
74 username = "sally"
75 ts = to_timestamp(datetime.datetime.now())
76 conn.zadd(USER_SET_KEY, ts, username)
77
78 # retrieve the list of users who have been active in the last MAX_AGE minutes
79
80 now = datetime.datetime.now()
81 min = to_timestamp(now - MAX_AGE)
82 max = to_timestamp(now)
83
84 whos_online = conn.zrangebyscore(USER_SET_KEY, min, max)
85
86 # e.g. whos_online = ['sally', 'harry', 'joe']
87
88 # periodically remove stale members
89
90 cutoff = to_timestamp(datetime.datetime.now() - MAX_AGE)
91 conn.zremrangebyscore(USER_SET_KEY, 0, cutoff)
92
93 .. _previous post: http://deathofagremmie.com/2011/04/25/a-better-who-s-online-with-redis-python/
94 .. _Redis: http://redis.io/
95 .. _Python: http://www.python.org
96 .. _redis-py: https://github.com/andymccurdy/redis-py
97 .. _Celery: http://celeryproject.org
98 .. _zadd: http://redis.io/commands/zadd
99 .. _zrangebyscore: http://redis.io/commands/zrangebyscore
100 .. _zremrangebyscore: http://redis.io/commands/zremrangebyscore