Использование soa для построения сложных веб проектов -...
DESCRIPTION
TRANSCRIPT
Что такое SOA?
Обычный сайт (монолитная архитектура)
/index
View
DB
ControllerПолучить сессию
Загрузить отклики…
Загрузить список резюме
3
Проблемы1. Один репозиторий, много разработчиков
2. Рост кодовой базы
3. Ограничения в использовании языка программирования
4. Внешние подрядчики
5. ... to be counted
4
Сервис-ориентированная архитектура(SOA)модульный подход к разработке программного обеспечения,
основанный на использовании распределённых, слабо связанных
заменяемых компонентов со стандартизированными интерфейсами и
протоколам взаимодействия.
Источник: Wikipedia
5
SOA-based сайт
/index
Controller
View
Сессия
Сервис откликов
Сервис резюме
DB1
DB2
DB3
protobuf
JSON
XML
Cloud Service
6
Компонент системы в разработке• Один сервис - один репозиторий
• Свобода выбора языка программирования
• Возможность отдать на аутсорс
7
Контролируемая деградация• Лучше показать часть информации, чем упасть с 500
• Лучше показать самое важное за 100мс—1с, чем все за 10 секунд
8
Почему это важно• 47% людей ожидают, что страница будет загружаться менее двух
секунд
• 40% закрывают страницу, если она загружается более трех секунд
• 52% считают важным скорость загрузки страницы
Источник: Akamai
11
Разные требования к сервисам• Не все сервисы должны работать под большой нагрузкой
• Некоторые могут и "полежать"
• Оптимизируем только там, где это нужно
13
Виртуализация• На одной машине один сервис
• Легче мониторить
• Легче деплоить
• Можно добавлять/удалять машины по необходимости
14
Дополнительный слой кеширования
/index
Controller
View
Сессия
Сервис откликов
Сервис резюме
DB1
DB2
DB3
protobuf
JSON
XML
memcached, redis, …varnish, nginx (proxy_cache)
Cloud Service
15
Много платформ
Основной сайт hh.ru
API api.hh.ru
Мобильный сайт m.hh.ru
Сессия Сервис откликов
Сервис резюме
Облако сервисов
16
Проблемы
Сеть• TCP / HTTP overhead
• Задержки между виртуальными машинами (используем SR-IOV)
18
Эксплуатация• Больше конфигов
• Больше деплоя
• Больше мониторинга
19
Разработка• Стало проще?
• Усложнение поддержки тестовых и development стендов
• Был один лог - стало много
20
Уникальный идентификатор запроса(request_id)
• Генерируется на nginx (ngx_http_requestid_module)
• Пробрасывается http-заголовком (X-Request-Id) на все сервисы
• Каждая запись в логе содержит request_id
• Все логи сливаем на graylog2
21
Немного про
Python
Frontik
backend1
backend2
backend3
Templating
User request
HTML/JSON
Request Handler
23
Frontikclass Page(page.PageHandler):
@page.user_page(xsl='pages/index.xsl')
def get_page(self):
self.doc.put(
self.get_url('{}/resumes'.format(resumeHost)),
self.get_url('{}/negotiations'.format(negHost))
)
24
25
26
27
28
Frontik• Изначально был заточен под XML/XSLT (сейчас умеет и JSON)
• Работает на нашей патченной Tornado 2.0
(https://github.com/hhru/tornado)
• Требует libcurl, собранный с c-ares
(https://github.com/tornadoweb/tornado/pull/1017)
• Мало документации и примеров
https://github.com/hhru/frontik
29
Что хотелось• Уменьшить количество многоуровневых запросов
• Иметь бо́льшую свободу в выборе шаблонизатора
• Чтобы работало и на upstream Tornado
30
Tortik
Preprocessors
backend1
backend2
backend3
Postprocessors
User request
HTML/JSON or cool debug page
Request Handler
32
Preprocessors• Набор асинхронных обработчиков (например, получение сессии)
• Выполняются до начала выполнения обработчика запроса
def session(handler, callback):
def _session_callback(session):
handler.session = session
callback()
session_client.get_session(_session_callback, ...)
33
Preprocessorsclass MobilePageHandler(tortik.page.RequestHandler):
preprocessors = [
session,
pagedata
]
34
Request Handler• Стандартные обработчики Tornado (get, post, ...) с @asynchronous
• Выходные данные формируются через
self.add('name', data) - аналог self.doc.put() во Frontik'е
• Можно и сразу self.complete({'name': data})
def get(self):
self.add('server_time', int(time.time))
self.add('data', {...})
self.complete()
35
Postprocessors• Набор последовательных обработчиков над выходными данными
• Выполняются после выполнения обработчика запроса
(RequestHandler)
def template(handler, data, callback):
out = template_engine.render(handler.template_name,
data=handler.get_data())
callback(handler, out)
36
Postprocessorsclass MobilePageHandler(tortik.page.RequestHandler):
postprocessors = [
template,
translation
]
37
Отправка запросов на бэкендыself.fetch_requests([
self.make_request(
name='vacancies',
method='GET',
full_url='https://api.hh.ru/vacancies/8252535'),
...
], callback=self.complete)
38
Отправка запросов на бэкендыself.fetch_requests([
('vacancies', 'https://api.hh.ru/vacancies/8252535'),
...
], callback=_cb)
39
Отправка запросов на бэкендыdef _cb():
response = self.responses['vacancies']
data = response.data # json или xml
# плюс автоматический
# self.add('vacancies', response.data)
self.complete()
40
Этапы обработки запроса• получение сессии
• обработка запроса (отправка запросов на сервисы и т.п.)
• шаблонизация
41
Этапы обработки запросаhandler.log.stage_started('render')
# do some hard render stuff
handler.log.stage_complete('render')
42
Этапы обработки запросаhandler.fetch_requests(
...
callback=_cb,
stage='session'
)
43
Этапы обработки запросаMONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21.77
translation=7.30
44
Этапы обработки запросаMONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21.77
translation=7.30
45
Этапы обработки запросаMONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21.77
translation=7.30
46
Этапы обработки запросаMONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21.77
translation=7.30
47
Мониторинг
48
Мониторинг
49
Спасибо!Виталий Глибин, HeadHunter
@glibin, glibin.ru
https://github.com/hhru/tortik
https://github.com/hhru/frontik
50