secon'2014 - Сергеев Антон - Асинхронные задачи в ifunny

Post on 16-Jun-2015

143 Views

Category:

Presentations & Public Speaking

0 Downloads

Preview:

Click to see full reader

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

top related