Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Дополнительные
темы

Дмитрий Смаль
Мы рассмотрим
•

Запуск фоновых процессов. Cron.

•

Очереди сообщений и задач. RabbitMQ, Celery.

•

Real-time сообщения. Comet.

•

Полнотекстовый поиск. Sphinx.

•

Использование NoSQL хранилищ. Memcached, Redis,
Tarantool.

2
Типичные задачи для Web
 Массовый импорт данных

 Массовый экспорт данных
 Расчет рейтингов (например, лучшие вопросы)
 Очистка устаревших данных

 И вообще все периодические работы
 Статистика, статистика, статистика

3
Cron

Cron – стандартный планировщик задач в linux
Задача = команда оболочки (bash) в linux
4
Crontab
MAILTO=mialinx@gmail.com
PATH=/bin:/usr/bin:/home/project/bin
SHELL=/bin/bash
1
*/3 *
*
*
indexer --rotate --all
1
1
*
*
*
backup.sh
1
3
7
*
*
big_backup_all.sh
1
*
*
*
*
calc_best.py
*
*
*
*
*
send_mail.py

Команда
Час

День

Минута
5
Проблемы
1. Скрипт может работать долго

2. Скрипт может в принципе не успевать
обработать нужный объем данных
3. Скрипт может потреблять много ресурсов

4. В каждом скрипте нужно настраивать
окружение: соединение с базой, пути к файлам
и т.п.
5. Не чаще раза в минуту

6
Решения
1. Использовать блокировку файлов (flock)

2. Распараллелить обработку. Запускать
несколько процессов
3. Запускать скрипты на отдельной машине.
Использовать scribe для перемещения логов
4. Использовать management command (в
Django), писать весь код в ./lib
Cron плохо масштабируется, дает задержки 
7
Очереди сообщений

8
BuzzWords

9
Архитектура очередей

10
Преимущества очередей
1. Асинхронная связь и буферизация

2. Слабая связанность
3. Масштабируемость
4. Балансировка и эластичность нагрузки

5. Гарантированная доставка

11
Hello World (sender)
##

ваш скрипт-отправитель, например views.py

import pika
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": "hello world!"}'
)
connection.close()
12
Hello World (consumer)
##

consumer.py – ваш скрипт обработчик

import pika
params = pika.ConnectionParameters(host='localhost')
connection = pika.BlockingConnection()
channel = connection.channel()

channel.queue_declare(queue='hello')
def callback(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()
13
Exchanges

fanout – отправляет во все очереди
direct - по совпадению routing_key
topic – по совпадению (c шаблонами)
headers – на основании аттрибутов
14
Что нужно еще ?
1. Протокол передачи параметров задач

2. Получение результатов
3. Слежение за worker – процессами


Нужное количество



Ограничение нагрузки



Борьба с падениями



Борьба с утечками памяти

 Удобный мониторинг

15
Очередь задач - Celery
1. Работает поверх RabbitMQ, MongoDB, Redis, DB

2. Задача = функция python
3. Удобный интерфейс запуска задач
4. Замена Cron

16
Архитектура Celery

17
Задачи Celery
## tasks.py – файл с функциями-задачами
from celery import Celery
app = Celery('tasks', broker='amqp://guest@localhost//')
def _poor_fib(x):
## глупая реализация фибоначи
@app.task
def fib(x):
print _poor_fib(x)
@app.periodic_task(run_every=timedelta(seconds=60))
def cronlike():
print "Look at me! I'm a cron“
18
Настройки Celery
## celeryconfig.py – файл с настройками celery
import tasks

## Важно! Загрузить задачи в celery

BROKER_URL = 'amqp://guest@localhost//‘

СELERY_RESULT_BACKEND = 'amqp'
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_ACCEPT_CONTENT=['json']
CELERY_TIMEZONE = 'Europe/Moscow'
CELERY_ENABLE_UTC = True

19
Запуск задания
## views.py – код вашего приложения
## Нам нужны обертки для выполнения задач
from tasks import fib

fib.delay(10)
result = fib.delay(20)
print result.get(timeout=10)

PROFIT!!!
20
Доставка сообщений
пользователю
Как сообщить пользователю о событии ?
 Polling (каждые 10 секунд…)
 LongPolling (Comet)

 ServerPush
 WebSockets

21
Отличия

22
Nginx mod_push
location /publish/ {
set $push_channel_id $arg_cid; # id канала
push_store_messages off;

# не храним сообщения

push_publisher;

# включаем отправку

allow

127.0.0.1;

deny

all;

}
location /listen/ {
push_subscriber_concurrency broadcast;

# всем!

set $push_channel_id $arg_cid; # id канала
default_type application/json; # MIME тип сообщения
push_subscriber;

# включаем доставку

}
23
Получение сообщений
function comet (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);
});
24
Отправка сообщений
import urllib2
request = urllib2.Request(
'http://localhost/publish/',
'{"hello": 1}',

{}
)
# Может занять много времени
response = urllib2.urlopen(request)
print response

25
Архитектура

Queue

Sender task

Application

Nginx +
mod_push

26
Полнотекстовый поиск

27
Основы Sphinx
Document – единица информационного
поиска, содержит id, поля
(полнотекстовые), атрибуты (не полнотекстовые).
Index – множество документов, по которым идет
поиск, можно считать «таблицей», физически
набор файлов на диске.
Source – способ пополнения индекса

28
Варианты установки
Stand-alone server (API, SphinxQL)
SQL

MySQL
SQL

Application

API, SphinxQL

Sphinx

MySQL storage engine (SphinxSE)
SQL
Application

MySQL

Sphinx
29
Пример конфигурации
searchd {
listen = 127.0.0.1:3334
log = /var/lib/sphinxsearch/sphinx.log
}
indexer {
mem_limit = 100M
max_xmlpipe2_field = 32M
}
index ask_question_idx {
source = ask_question_source
path = /var/lib/sphinxsearch/ask_idx
morphology = libstemmer_ru, libstemmer_en
html_strip = 1
}
30
Пример конфигурации (source)
source ask_question_source {
type = mysql
sql_host = localhost ## и остальные опции
sql_query_range = SELECT MIN(id), MAX(id)
FROM questions
sql_range_step = 1000
sql_query = SELECT id, title, text, rating
FROM questions WHERE id>=$start AND id<=$end
sql_attr_uint = id
sql_field_string = title
sql_field_string = text
sql_attr_uint = rating
}
31
Использование API
use Sphinx::Search;
my $sph = Sphinx::Search->new();
$sph->SetMatchMode(SPH_MATCH_ALL);
$sph->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
$sph->SetSortMode(SPH_SORT_RELEVANCE);
$sph->SetFieldWeights({ title => 100, text => 50 });
$sph->SetLimits(0, 10);
$results = $sph->Query("search terms");
my @ids = map { $_->{id} } @{ $results->{matches} };
print @ids;
32
Интеграция с Django
from djangosphinx import SphinxSearch

class Question(models.Model):
title = models.CharField(max_length=100)
text = models.CharField(max_length=1000)
rating = models.IntegerField()
search = SphinxSearch(
index='ask_question_idx',
weights={ 'title': 100, 'text': 50, }
)
def search_question(req):
results = Question.search.query(reg.GET['query'])
.order_by('@weight')

...

33
Индексация
1. Запускать индексатор по cron
/usr/bin/indexer --rotate --all
--config=/path/to/sphinx.conf

2. Иcпользовать real-time индексы

34
Ключевые особенности
1. Легкая интеграция с базами и приложениями

2. Batch и real-time индексы
3. Гибкий язык поисковых запросов
4. SQL – like syntax

5. Хорошие функции релевантности
6. Масштабирование

35
In-Memory storage

36
Memcached
import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
mc.set("some_key", "Some value")
value = mc.get("some_key")
mc.set("another_key", 3)
mc.delete("another_key")
mc.set("key", "1")
mc.incr("key")
mc.decr("key")

37
Архитектура Memcached

38
Типичный пример использования
import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
def heavy_func(arg1):
result = db.get('complex sql') ** 100500;
result = template(result)
# some very heavy computation :)
return result
def fast_func(arg1):
result = mc.get(str(arg1))
if mc is None:
result = heavy_func(arg1)
mc.set(str(arg1), result)
return result
39
Литература
1. http://www.rabbitmq.com/getstarted.html - RabbitMQ
2. http://docs.celeryproject.org/en/latest/getting-started/first-steps-withcelery.html - Celery
3. http://sphinxsearch.com/docs/2.0.9/ - Sphinx
4. http://justcramer.com/2009/03/25/setting-up-django-and-sphinx-full-textsearch-django-sphinx/ - django-sphinx

40
Спасибо за внимание
Дмитрий Смаль,
smal@corp.mail.ru

More Related Content

Web осень 2013 лекция 9

  • 2. Мы рассмотрим • Запуск фоновых процессов. Cron. • Очереди сообщений и задач. RabbitMQ, Celery. • Real-time сообщения. Comet. • Полнотекстовый поиск. Sphinx. • Использование NoSQL хранилищ. Memcached, Redis, Tarantool. 2
  • 3. Типичные задачи для Web  Массовый импорт данных  Массовый экспорт данных  Расчет рейтингов (например, лучшие вопросы)  Очистка устаревших данных  И вообще все периодические работы  Статистика, статистика, статистика 3
  • 4. Cron Cron – стандартный планировщик задач в linux Задача = команда оболочки (bash) в linux 4
  • 5. Crontab MAILTO=mialinx@gmail.com PATH=/bin:/usr/bin:/home/project/bin SHELL=/bin/bash 1 */3 * * * indexer --rotate --all 1 1 * * * backup.sh 1 3 7 * * big_backup_all.sh 1 * * * * calc_best.py * * * * * send_mail.py Команда Час День Минута 5
  • 6. Проблемы 1. Скрипт может работать долго 2. Скрипт может в принципе не успевать обработать нужный объем данных 3. Скрипт может потреблять много ресурсов 4. В каждом скрипте нужно настраивать окружение: соединение с базой, пути к файлам и т.п. 5. Не чаще раза в минуту 6
  • 7. Решения 1. Использовать блокировку файлов (flock) 2. Распараллелить обработку. Запускать несколько процессов 3. Запускать скрипты на отдельной машине. Использовать scribe для перемещения логов 4. Использовать management command (в Django), писать весь код в ./lib Cron плохо масштабируется, дает задержки  7
  • 11. Преимущества очередей 1. Асинхронная связь и буферизация 2. Слабая связанность 3. Масштабируемость 4. Балансировка и эластичность нагрузки 5. Гарантированная доставка 11
  • 12. Hello World (sender) ## ваш скрипт-отправитель, например views.py import pika 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": "hello world!"}' ) connection.close() 12
  • 13. Hello World (consumer) ## consumer.py – ваш скрипт обработчик import pika params = pika.ConnectionParameters(host='localhost') connection = pika.BlockingConnection() channel = connection.channel() channel.queue_declare(queue='hello') def callback(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() 13
  • 14. Exchanges fanout – отправляет во все очереди direct - по совпадению routing_key topic – по совпадению (c шаблонами) headers – на основании аттрибутов 14
  • 15. Что нужно еще ? 1. Протокол передачи параметров задач 2. Получение результатов 3. Слежение за worker – процессами  Нужное количество  Ограничение нагрузки  Борьба с падениями  Борьба с утечками памяти  Удобный мониторинг 15
  • 16. Очередь задач - Celery 1. Работает поверх RabbitMQ, MongoDB, Redis, DB 2. Задача = функция python 3. Удобный интерфейс запуска задач 4. Замена Cron 16
  • 18. Задачи Celery ## tasks.py – файл с функциями-задачами from celery import Celery app = Celery('tasks', broker='amqp://guest@localhost//') def _poor_fib(x): ## глупая реализация фибоначи @app.task def fib(x): print _poor_fib(x) @app.periodic_task(run_every=timedelta(seconds=60)) def cronlike(): print "Look at me! I'm a cron“ 18
  • 19. Настройки Celery ## celeryconfig.py – файл с настройками celery import tasks ## Важно! Загрузить задачи в celery BROKER_URL = 'amqp://guest@localhost//‘ СELERY_RESULT_BACKEND = 'amqp' CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_ACCEPT_CONTENT=['json'] CELERY_TIMEZONE = 'Europe/Moscow' CELERY_ENABLE_UTC = True 19
  • 20. Запуск задания ## views.py – код вашего приложения ## Нам нужны обертки для выполнения задач from tasks import fib fib.delay(10) result = fib.delay(20) print result.get(timeout=10) PROFIT!!! 20
  • 21. Доставка сообщений пользователю Как сообщить пользователю о событии ?  Polling (каждые 10 секунд…)  LongPolling (Comet)  ServerPush  WebSockets 21
  • 23. Nginx mod_push location /publish/ { set $push_channel_id $arg_cid; # id канала push_store_messages off; # не храним сообщения push_publisher; # включаем отправку allow 127.0.0.1; deny all; } location /listen/ { push_subscriber_concurrency broadcast; # всем! set $push_channel_id $arg_cid; # id канала default_type application/json; # MIME тип сообщения push_subscriber; # включаем доставку } 23
  • 24. Получение сообщений function comet (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); }); 24
  • 25. Отправка сообщений import urllib2 request = urllib2.Request( 'http://localhost/publish/', '{"hello": 1}', {} ) # Может занять много времени response = urllib2.urlopen(request) print response 25
  • 28. Основы Sphinx Document – единица информационного поиска, содержит id, поля (полнотекстовые), атрибуты (не полнотекстовые). Index – множество документов, по которым идет поиск, можно считать «таблицей», физически набор файлов на диске. Source – способ пополнения индекса 28
  • 29. Варианты установки Stand-alone server (API, SphinxQL) SQL MySQL SQL Application API, SphinxQL Sphinx MySQL storage engine (SphinxSE) SQL Application MySQL Sphinx 29
  • 30. Пример конфигурации searchd { listen = 127.0.0.1:3334 log = /var/lib/sphinxsearch/sphinx.log } indexer { mem_limit = 100M max_xmlpipe2_field = 32M } index ask_question_idx { source = ask_question_source path = /var/lib/sphinxsearch/ask_idx morphology = libstemmer_ru, libstemmer_en html_strip = 1 } 30
  • 31. Пример конфигурации (source) source ask_question_source { type = mysql sql_host = localhost ## и остальные опции sql_query_range = SELECT MIN(id), MAX(id) FROM questions sql_range_step = 1000 sql_query = SELECT id, title, text, rating FROM questions WHERE id>=$start AND id<=$end sql_attr_uint = id sql_field_string = title sql_field_string = text sql_attr_uint = rating } 31
  • 32. Использование API use Sphinx::Search; my $sph = Sphinx::Search->new(); $sph->SetMatchMode(SPH_MATCH_ALL); $sph->SetRankingMode(SPH_RANK_PROXIMITY_BM25); $sph->SetSortMode(SPH_SORT_RELEVANCE); $sph->SetFieldWeights({ title => 100, text => 50 }); $sph->SetLimits(0, 10); $results = $sph->Query("search terms"); my @ids = map { $_->{id} } @{ $results->{matches} }; print @ids; 32
  • 33. Интеграция с Django from djangosphinx import SphinxSearch class Question(models.Model): title = models.CharField(max_length=100) text = models.CharField(max_length=1000) rating = models.IntegerField() search = SphinxSearch( index='ask_question_idx', weights={ 'title': 100, 'text': 50, } ) def search_question(req): results = Question.search.query(reg.GET['query']) .order_by('@weight') ... 33
  • 34. Индексация 1. Запускать индексатор по cron /usr/bin/indexer --rotate --all --config=/path/to/sphinx.conf 2. Иcпользовать real-time индексы 34
  • 35. Ключевые особенности 1. Легкая интеграция с базами и приложениями 2. Batch и real-time индексы 3. Гибкий язык поисковых запросов 4. SQL – like syntax 5. Хорошие функции релевантности 6. Масштабирование 35
  • 37. Memcached import memcache mc = memcache.Client(['127.0.0.1:11211'], debug=0) mc.set("some_key", "Some value") value = mc.get("some_key") mc.set("another_key", 3) mc.delete("another_key") mc.set("key", "1") mc.incr("key") mc.decr("key") 37
  • 39. Типичный пример использования import memcache mc = memcache.Client(['127.0.0.1:11211'], debug=0) def heavy_func(arg1): result = db.get('complex sql') ** 100500; result = template(result) # some very heavy computation :) return result def fast_func(arg1): result = mc.get(str(arg1)) if mc is None: result = heavy_func(arg1) mc.set(str(arg1), result) return result 39
  • 40. Литература 1. http://www.rabbitmq.com/getstarted.html - RabbitMQ 2. http://docs.celeryproject.org/en/latest/getting-started/first-steps-withcelery.html - Celery 3. http://sphinxsearch.com/docs/2.0.9/ - Sphinx 4. http://justcramer.com/2009/03/25/setting-up-django-and-sphinx-full-textsearch-django-sphinx/ - django-sphinx 40