web осень 2013 лекция 9
TRANSCRIPT
![Page 1: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/1.jpg)
Дополнительные темы
Дмитрий Смаль
![Page 2: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/2.jpg)
2
• Запуск фоновых процессов. Cron.
• Очереди сообщений и задач. RabbitMQ, Celery.
• Real-time сообщения. Comet.
• Полнотекстовый поиск. Sphinx.
• Использование NoSQL хранилищ. Memcached, Redis, Tarantool.
Мы рассмотрим
![Page 3: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/3.jpg)
3
Типичные задачи для Web
Массовый импорт данных Массовый экспорт данных Расчет рейтингов (например, лучшие
вопросы) Очистка устаревших данных И вообще все периодические работы Статистика, статистика, статистика
![Page 4: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/4.jpg)
4
Cron
Cron – стандартный планировщик задач в linux
Задача = команда оболочки (bash) в linux
![Page 5: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/5.jpg)
5
Crontab
[email protected]=/bin:/usr/bin:/home/project/binSHELL=/bin/bash1 */3 * * * indexer --rotate --all1 1 * * * backup.sh 1 3 7 * * big_backup_all.sh1 * * * * calc_best.py* * * * * send_mail.py
Команда
Минута
ЧасДень
![Page 6: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/6.jpg)
6
Проблемы
1. Скрипт может работать долго
2. Скрипт может в принципе не успевать обработать нужный объем данных
3. Скрипт может потреблять много ресурсов
4. В каждом скрипте нужно настраивать окружение: соединение с базой, пути к файлам и т.п.
5. Не чаще раза в минуту
![Page 7: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/7.jpg)
7
Решения
1. Использовать блокировку файлов (flock)
2. Распараллелить обработку. Запускать несколько процессов
3. Запускать скрипты на отдельной машине. Использовать scribe для перемещения логов
4. Использовать management command (в Django), писать весь код в ./lib
Cron плохо масштабируется, дает задержки
![Page 8: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/8.jpg)
8
Очереди сообщений
![Page 9: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/9.jpg)
9
BuzzWords
![Page 10: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/10.jpg)
10
Архитектура очередей
![Page 11: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/11.jpg)
11
Преимущества очередей
1. Асинхронная связь и буферизация
2. Слабая связанность
3. Масштабируемость
4. Балансировка и эластичность нагрузки
5. Гарантированная доставка
![Page 12: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/12.jpg)
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()
![Page 13: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/13.jpg)
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()
![Page 14: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/14.jpg)
14
Exchanges
fanout – отправляет во все очередиdirect - по совпадению routing_keytopic – по совпадению (c шаблонами)headers – на основании аттрибутов
![Page 15: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/15.jpg)
15
Что нужно еще ?
1. Протокол передачи параметров задач
2. Получение результатов
3. Слежение за worker – процессами Нужное количество Ограничение нагрузки Борьба с падениями Борьба с утечками памяти
Удобный мониторинг
![Page 16: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/16.jpg)
16
Очередь задач - Celery
1. Работает поверх RabbitMQ, MongoDB, Redis, DB
2. Задача = функция python
3. Удобный интерфейс запуска задач
4. Замена Cron
![Page 17: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/17.jpg)
17
Архитектура Celery
![Page 18: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/18.jpg)
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“
![Page 19: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/19.jpg)
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
![Page 20: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/20.jpg)
20
Запуск задания
## views.py – код вашего приложения
## Нам нужны обертки для выполнения задач
from tasks import fib
fib.delay(10)
result = fib.delay(20)
print result.get(timeout=10)
PROFIT!!!
![Page 21: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/21.jpg)
21
Доставка сообщений пользователю
Как сообщить пользователю о событии ?
Polling (каждые 10 секунд…) LongPolling (Comet) ServerPush WebSockets
![Page 22: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/22.jpg)
22
Отличия
![Page 23: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/23.jpg)
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; # включаем доставку
}
![Page 24: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/24.jpg)
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);
});
![Page 25: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/25.jpg)
25
Отправка сообщений
import urllib2
request = urllib2.Request(
'http://localhost/publish/',
'{"hello": 1}',
{}
)
# Может занять много времени
response = urllib2.urlopen(request)
print response
![Page 26: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/26.jpg)
26
Архитектура
Application
Nginx + mod_pushSender
task
Queue
![Page 27: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/27.jpg)
27
Полнотекстовый поиск
![Page 28: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/28.jpg)
28
Основы Sphinx
Document – единица информационного поиска, содержит id, поля (полнотекстовые), атрибуты (не полнотекстовые).
Index – множество документов, по которым идет поиск, можно считать «таблицей», физически набор файлов на диске.
Source – способ пополнения индекса
![Page 29: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/29.jpg)
29
Варианты установки
Stand-alone server (API, SphinxQL)
MySQL storage engine (SphinxSE)
MySQL Sphinx
Application
SQL
Application
MySQL
Sphinx
SQL
SQL
API, SphinxQL
![Page 30: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/30.jpg)
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
}
![Page 31: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/31.jpg)
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
}
![Page 32: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/32.jpg)
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;
![Page 33: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/33.jpg)
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')
...
![Page 34: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/34.jpg)
34
Индексация
1. Запускать индексатор по cron/usr/bin/indexer --rotate --
all --config=/path/to/sphinx.conf
2. Иcпользовать real-time индексы
![Page 35: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/35.jpg)
35
Ключевые особенности
1. Легкая интеграция с базами и приложениями
2. Batch и real-time индексы
3. Гибкий язык поисковых запросов
4. SQL – like syntax
5. Хорошие функции релевантности
6. Масштабирование
![Page 36: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/36.jpg)
36
In-Memory storage
![Page 37: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/37.jpg)
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")
![Page 38: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/38.jpg)
38
Архитектура Memcached
![Page 39: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/39.jpg)
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
![Page 40: Web осень 2013 лекция 9](https://reader035.vdocuments.site/reader035/viewer/2022062419/557f1c81d8b42aea318b4cae/html5/thumbnails/40.jpg)
40
Литература
1. http://www.rabbitmq.com/getstarted.html - RabbitMQ2. http://
docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html - Celery
3. http://sphinxsearch.com/docs/2.0.9/ - Sphinx4. http://
justcramer.com/2009/03/25/setting-up-django-and-sphinx-full-text-search-django-sphinx/ - django-sphinx