Блог gigimon'а

Социальные штуки в Django с Redis часть 1

Для одного из сайтов надо было писать начальные социальные штуки, типа количество пользователей онлайн и количество новых комментариев для постов. Т.к. реализация этого с sql базами достаточно затратно по ресурсам и не самое простое дело, то было выбрано nosql хранилище Redis. В нем легко хранить ключи и большое количество значений, которое легко можно получить для конкретных нужд.

Сначала рассмотрим реализацию индикатора пользователей онлайн вместе с Django.

Рассмотрим сначала теоретическую часть работы такого индикатора. Т.к. в вебе нет каких-либо четких механизмов узанавания, кто сейчас на сайте, то применяется простая практика. Каждую минуту записывается, кто из пользователей зашел на сайт, а раз в 5 минут делается выборка (группировка юзеров в каждую минуту) и вычисляется сколько их было. В моем конкретном примере, все пользователи зарегистрированы, т.е. учитывать анонимов не требуется. В Django для учета каждого пользователя и занесения его в базу очень подходит мидлварь (middleware). При заходе пользователя на любую страницу сайта, миддлварь его обрабатывает и заносит в базу со значением ключа указывающем время (например 23:10). А при запросе счетчиком, будут выбираться 5 ключей (23:10, 23:09, 23:08, 23:07, 23:06) объединяться и отдаваться. Общий процесс можно увидеть на картинке (не моей):

image0Начнем рассмотрение кода:

Рассмотрим код миддлвари:

class OnlineUsersMiddleware:
    def process_request(self, request):
        try:
            dbwork.set_to_key(request.user.id)
        except:
            pass

Как видно, вся работа миддлвари это получить id юзера и передать его в функцию работы с базой.

Рассмотрим саму функцию dbwork.set_to_key():

def current_key():
    return datetime.now().strftime('%H:%M')

def set_to_key(user_id):
    key = current_key()
    redis_db.sadd(key, user_id)
    redis_db.expire(key, 360)

Первая (current_key) - выдает нам просто время (ее можно и соввместить впринципе), вторая (set_to_key), получает ключ, добавляет в базу ключ set, в который добавляем пользовательский id и устанавливаем на него время жизни 6 минут.

Далее, рассмотри функцию, которая выводит количество пользователей онлайн:

def get_online():
    now = datetime.now()
    interval = [now - timedelta(minutes=x) for x in range(5)]
    interval = [i.strftime('%H:%M') for i in interval]
    try:
        online = len(redis_db.sunion(interval))
    except:
        online = 1
    return int(online)

тут interval - это список наших ключей, который получается получением дельты с текущим временем и 5 минут назад, потом по нему с помощью sunion() получаем все id пользователей, а len - количество (т.к. мне не нужны были имена, а только количество).

Для получения и выведения этого значения был написан контекст процессор, т.к. значение требуется во всех шаблонах:

def users_online(request):
    users = get_online()
    return {'count_users_online': users}

Во всех модулях, redis_db это конект к базе редиса через официальный модуль Redis, примерно так открывающий коннект:

try:
    redis_db = redis.Redis(host=getattr(settings, 'REDIS_HOST', 'localhost'),
                     port=getattr(settings, 'REDIS_PORT', 6379),
                     db=getattr(settings, 'REDIS_DB', 0),
                     password=getattr(settings, 'REDIS_PASS', ''),
                     socket_timeout=0.01)
except:
    pass

Все, после этого можно использовать. Как видно - делается легко и просто, к тому же не захламляет sql базу и не сказывается на производительности (nosql очень быстр). Вместо использования redis можно использовать тот же memcache.

2008 — 2014