secon'2014 - Сергеев Антон - Асинхронные задачи в ifunny
DESCRIPTION
Я расскажу про то, как выполнять тяжелые задачи, требующие значительного количества серверных ресурсов и времени так, чтобы это оставалось незаметным для пользователя. Какую архитектуру выбрали мы, каким инструментом пользуемся для решения асинхронных задач в iFunny. Поделюсь опытом: какие трудности были встречены на пути перехода к асинхронным задачам, какие решения выбрали и как мы видим наше светлое будущее. Доклад будет интересен всем, кто интересуется сложными распределенными системами.TRANSCRIPT
Асинхронные задачи в iFunny :)
Сергеев Антон@hackPNZ
Про что все это?
Когда и для чего могут понадобиться асинхронные задачи; Существующие готовые решения;
Что выбрали мы, наша архитектура, наш опыт.
iFunny :)Юмористический сервис;
Суточная аудитория:4 000 000 пользователей;
Около 250 000 работпубликуется ежедневно;
Около 1 000 000 000 просмпросмотров контента каждый день.
РазработкаiFunny :)
iFunny 2.4Возможность добавлять GIF
Тяжелый контент (до 10Mb);
Конвертация в видео при добавлении (формат webm);
Нарезка тумбов, форматы jpegи webp.
Q2 2013
iFunny 3.0Подписки, возможностьрепубликации работ
Подписки, добавление работв ленты пользователей;
РепуРепубликации, добавление републикаций в ленты пользователей.
Q4 2013
Asynchronoustask queue
Поддержка множества языков для реализации воркеров(в том числе, PHP); Очень большой лимит размера сообщения (4GB);
Большое количество мануалов в сети.
www.gearman.org
Python в качестве основного поддерживаемого языка; Проект быстро развивается, активное сообщество;
Легко развертывается, минимум конфигов и лишнего кода;
Интеграция с Django.
www.celeryproject.orgCelery
Мы выбрали Celery
Python :)
Гибкий, поддержка множества различных брокеров;
Отказоустойчивый, легко масштабируется;
Высокопроизводительный;
Вдохновляющий опыт Instagram.
Celerybrokers
RabbitMQ
Стабильный; Рекомендуют по-умолчанию.
Cложность тонкой настройки;
Плюс еще один компонент инфраструктуры.
Redis
Давно и успешно используем в продакшне; Быстрые запись/чтение (задачи хранятся в памяти);
Легко поднять с нуля.
Дорого для больших данных;
Failover: можно, но сложно.
Experimental brokers
MongoDB; | ограничение размера документа 16Mb
Beanstalk;
Amazon SQS;
CouchDB;
SQLAlchemy;
...другие.
Сельдерей + редис!
Архитектура
API Instances
Celery gates
Celeryworkers
Redis storage
2x uWSGI gate instances
2x AWS EC2 c3.xlarge(4 vCPU, 7.5Gb RAM, 2x 40Gb SSD)
8x Worker instances
8x AWS EC2 c3.2xlarge(8 vCPU, 15Gb RAM, 2x 80Gb SSD)
rpm
Задачи в iFunny :)
Публикация контента;
Fanout;
Revoke fanout;
Рассылка email-уведомлений.
Конфигурация Celerycelery = Celery('celerytasks')
celery.config_from_object(‘celeryconfig')
@celery.task()
def test_task(foo):
result = tasks.test.execute(foo)
return result
Broker configBROKER_URL = "redis://:12345@localhost:6379/0"
CELERY_RESULT_BACKEND = "redis://:12345@localhost:6379/0"
API entry pointfrom flask import g...
@app.route(“/test/task", methods=[‘GET'])
def test_task():
foo = g.params.get('foo')
async_result = celerytasks.test_task.delay(foo)
return async_result
Разделение очередейCELERY_ROUTES = {
'celerytasks.test_task': {'queue': 'test_foo'}, 'celerytasks.foo_task': {'queue': 'test_foo'}, 'celerytasks.bar_task': {'queue': 'test_bar'},
}
Task callingfeatures
Linking
test_task.apply_async((foo), link=test_task.s(bar))
Custom error handler
test_task.apply_async((foo), link_error=error_handler.s())
Countdown
result = test_task.apply_async((foo), countdown=3)rresult.get() # вернет результат как минимум через 3 секунды
Expiration
test_task.apply_async((foo), expires=60) # задача истечет через 60 секунд от текущего времени (получает статус REVOKED)
Retry
test_task.apply_async((foo), retry=True, retry_policy={
'max_retries': 3, 'interval_start': 0, 'interval_step': 0.2, 'interval_max': 0.2,
})
Serializers
test_task.apply_async((foo), serializer=‘json') # pickle, JSON, YAML, msgpack etc
Compression
test_task.apply_async((foo), compression=‘zlib') # gzip, bzip2
Routing to queue
test_task.apply_async(queue=‘test_queue')test_task.apply_async(queue=‘test_queue')
Логирование
Graylog2
Open source система аналитики данных; Собственный формат сообщений GELF;
Дополнительно берем сообщения из syslog.
Иногда падает при использовании GELF.
Мониторинг
Celery events;
Flower;
Datadog;
New Relic;
...другие.
FlowerReal-time tasks monitoring; Workers pool restart;
Web-interface;
Основан на celery events.
Постоянно падал в пике нагрузки;
Мало сМало статистики.
Datadogfrom statsd import statsd
...
@app.route("/foo", methods=['POST'])
def foo():
statsd.increment('ifunny.tasks.received', 1, tags=[“task:foo”])
New RelicСреднее время выполнения задачи; Детальная статистика по типам задач;
Реалтайм мониторинг ошибок;
Количество запросов в минуту;
Футболка в подарок :)
ССтоит недешево.
Среднее время выполнения задач
Мониторинг ошибок
Что дальше?
Celery 3.1 — течет память;
Failover гейтов пришлось сделали руками в коде API;
Гейты сами по себе — тяжелые и избыточные;
Добавлять задачи напрямую из Redis;
Думаем насчет RabbitMQ.
Спасибо за внимание!
Вопросы?
Сергеев Антон@hackPNZgithub.com/hackpnz