Инфраструктура веб-
приложений
СергейЛихобабин
Техносфера.2014
Запуск фоновых процессов. Cron.
Очереди сообщений и задач. RabbitMQ, Celery.
Real-time сообщения.
Полнотекстовый поиск. Sphinx.
Использование NoSQL хранилищ. Memcached,
Redis, Tarantool.
Типичные задачи
Массовый импорт данных
Массовый экспорт данных
Расчет рейтингов (например, лучшие вопросы)
Очистка устаревших данных
И вообще все периодические работы
Статистика, статистика, статистика
Cron – стандартный планировщик задачв linux
Задача = команда оболочки (bash) в linux
Проблемы
Скрипт может работать долго
Скрипт может в принципе не успевать
обработать нужный объем данных
Скрипт может потреблять много ресурсов
В каждом скрипте нужно настраивать
окружение: соединение с базой, пути к файлам
и т.п.
Не чаще раза в минуту
Решения
Использовать блокировку файлов (flock)
Распараллелить обработку. Запускать
несколько процессов
Запускать скрипты на отдельной машине.
Использовать scribe для перемещения логов
Использовать managementcommand (в Django)
Cron плохо масштабируется, дает задержки
Очереди сообщений
Архитектура очередей
Преимущества очередей
Асинхронная связь и буферизация
Масштабируемость
Балансировка и эластичность нагрузки
Гарантированная доставка
Пример (отправка
событий)
importpika
params=pika.ConnectionParameters(host='localhost')
connection=pika.BlockingConnection(params)
channel=connection.channel()
channel.queue_declare(queue='hello')
channel.basic_publish(
exchange='',
routing_key='hello',
body='{"id":10,"msg":"helloworld!"}'
)
connection.close()
Пример (получение
событий)
importpika
params=pika.ConnectionParameters(host='localhost')
connection=pika.BlockingConnection()
channel=connection.channel()
channel.queue_declare(queue='hello')
defcallback(ch,method,properties,body):
print"[x]Received%r"%(body,)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(callback,queue='hello')
channel.start_consuming()
Что еще нужно для
хорошей очереди
Протокол передачи параметров задач
Получение результатов
Слежение за worker – процессами
Нужное количество
Ограничение нагрузки
Борьба с падениями
Борьба с утечками памяти
Удобный мониторинг
Celery
Работает поверх RabbitMQ, MongoDB, Redis, DB
Задача = функция python
Удобный интерфейс запуска задач
Замена Cron
Архитектура Celery
Описание задач Celery
fromceleryimportCelery
app=Celery('tasks',broker='amqp://guest@localhost//'
def_poor_fib(x):
##глупаяреализацияфибоначи
@app.task
deffib(x):
print_poor_fib(x)
@app.periodic_task(run_every=timedelta(seconds=60))
defcronlike():
print"Lookatme!I'macron“
Вызов задач Celery
##views.py–кодвашегоприложения
fromtasksimportfib
fib.delay(10)
result=fib.delay(20)
printresult.get(timeout=10)
Асинхронная доставка
сообщений
Polling(каждые 10 секунд…)
LongPolling(Comet)
ServerPush
WebSockets
Nginx mod_push
location/publish/{
set$push_channel_id$arg_cid;#idканала
push_store_messagesoff; #нехранимсообщени
push_publisher; #включаемотправку
allow 127.0.0.1;
deny all;
}
location/listen/{
push_subscriber_concurrencybroadcast; #всем!
set$push_channel_id$arg_cid;#idканала
default_typeapplication/json;#MIMEтипсообщения
push_subscriber; #включаемдоставку
}
Comet client-side
functioncomet(id,onmessage){
$.get('http://host/listen/',{cid:id})
.done(function(data){
onmessage(data);
comet(id,onmessage);
})
.fail(function(data){
comet(id,onmessage);
});
}
comet(123,function(data){
console.log(data);
});
Отправка сообщений
importurllib2
request=urllib2.Request(
'http://localhost/publish/',
'{"hello":1}',
{}
)
#Можетзанятьмноговремени
response=urllib2.urlopen(request)
printresponse
Полнотекстовый поиск
Особенности Sphinx
Легкая интеграция с базами и приложениями
Batch и real-time индексы
Гибкий язык поисковых запросов
SQL – like syntax
Хорошие функции релевантности
Масштабирование
In-memory storage
Memcached
importmemcache
mc=memcache.Client(['127.0.0.1:11211'],debug=0)
mc.set("some_key","Somevalue")
value=mc.get("some_key")
mc.set("another_key",3)
mc.delete("another_key")
mc.set("key","1")
mc.incr("key")
mc.decr("key")
Memcached
importmemcache
mc=memcache.Client(['127.0.0.1:11211'],debug=0)
defheavy_func(arg1):
result=db.get('complexsql')**100500;
result=template(result)
#someveryheavycomputation:)
returnresult
deffast_func(arg1):
result=mc.get(str(arg1))
ifresultisNone:
result=heavy_func(arg1)
mc.set(str(arg1),result)
returnresult
Полезные ссылки
http://www.rabbitmq.com/getstarted.html-
RabbitMQ
http://docs.celeryproject.org/en/latest/getting-
started/first-steps-with-celery.html-Celery
http://sphinxsearch.com/docs/2.0.9/-Sphinx
Спасибо за внимание
СергейЛихобабин
s.lihobabin@corp.mail.ru

Л9: Взаимодействие веб-приложений