Использование SOA для
построения сложных веб
проектов
Виталий Глибин, HeadHunter
Что такое SOA?
Обычный сайт (монолитная архитектура)
/index
View
DB
Controller
Получить сессию
Загрузить отклики
…
Загрузить список резюм...
Проблемы
1. Один репозиторий, много разработчиков
2. Рост кодовой базы
3. Ограничения в использовании языка программирован...
Сервис-ориентированная архитектура
(SOA)
модульный подход к разработке программного обеспечения,
основанный на использован...
SOA-based сайт
/index
Controller
View
Сессия
Сервис откликов
Сервис резюме
DB1
DB2
DB3
protobuf
JSON
XML
Cloud Service
6
Компонент системы в разработке
• Один сервис - один репозиторий
• Свобода выбора языка программирования
• Возможность отда...
Контролируемая деградация
• Лучше показать часть информации, чем упасть с 500
• Лучше показать самое важное за 100мс—1с, ч...
Почему это важно
• 47% людей ожидают, что страница будет загружаться менее двух
секунд
• 40% закрывают страницу, если она ...
Разные требования к сервисам
• Не все сервисы должны работать под большой нагрузкой
• Некоторые могут и "полежать"
• Оптим...
Виртуализация
• На одной машине один сервис
• Легче мониторить
• Легче деплоить
• Можно добавлять/удалять машины по необхо...
Дополнительный слой кеширования
/index
Controller
View
Сессия
Сервис откликов
Сервис резюме
DB1
DB2
DB3
protobuf
JSON
XML
...
Много платформ
Основной сайт
hh.ru
API
api.hh.ru
Мобильный сайт
m.hh.ru
Сессия
Сервис откликов
Сервис резюме
Облако сервис...
Проблемы
Сеть
• TCP / HTTP overhead
• Задержки между виртуальными машинами (используем SR-IOV)
18
Эксплуатация
• Больше конфигов
• Больше деплоя
• Больше мониторинга
19
Разработка
• Стало проще?
• Усложнение поддержки тестовых и development стендов
• Был один лог - стало много
20
Уникальный идентификатор запроса
(request_id)
• Генерируется на nginx (ngx_http_requestid_module)
• Пробрасывается http-за...
Немного про
Python
Frontik
backend1
backend2
backend3
Templating
User request
HTML/JSON
Request Handler
23
Frontik
class Page(page.PageHandler):
@page.user_page(xsl='pages/index.xsl')
def get_page(self):
self.doc.put(
self.get_ur...
25
26
27
28
Frontik
• Изначально был заточен под XML/XSLT (сейчас умеет и JSON)
• Работает на нашей патченной Tornado 2.0
(https://git...
Что хотелось
• Уменьшить количество многоуровневых запросов
• Иметь бо́льшую свободу в выборе шаблонизатора
• Чтобы работа...
Tortik
Preprocessors
backend1
backend2
backend3
Postprocessors
User request
HTML/JSON or cool debug page
Request Handler
32
Preprocessors
• Набор асинхронных обработчиков (например, получение сессии)
• Выполняются до начала выполнения обработчика...
Preprocessors
class MobilePageHandler(tortik.page.RequestHandler):
preprocessors = [
session,
pagedata
]
34
Request Handler
• Стандартные обработчики Tornado (get, post, ...) с @asynchronous
• Выходные данные формируются через
sel...
Postprocessors
• Набор последовательных обработчиков над выходными данными
• Выполняются после выполнения обработчика запр...
Postprocessors
class MobilePageHandler(tortik.page.RequestHandler):
postprocessors = [
template,
translation
]
37
Отправка запросов на бэкенды
self.fetch_requests([
self.make_request(
name='vacancies',
method='GET',
full_url='https://ap...
Отправка запросов на бэкенды
self.fetch_requests([
('vacancies', 'https://api.hh.ru/vacancies/8252535'),
...
], callback=_...
Отправка запросов на бэкенды
def _cb():
response = self.responses['vacancies']
data = response.data # json или xml
# плюс ...
Этапы обработки запроса
• получение сессии
• обработка запроса (отправка запросов на сервисы и т.п.)
• шаблонизация
41
Этапы обработки запроса
handler.log.stage_started('render')
# do some hard render stuff
handler.log.stage_complete('render...
Этапы обработки запроса
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....
Этапы обработки запроса
MONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21....
Этапы обработки запроса
MONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21....
Этапы обработки запроса
MONIK handler=hhmobile.pages.Page method=GET code=200
total=93 session=13.98 page=47.14 render=21....
Мониторинг
48
Мониторинг
49
Спасибо!
Виталий Глибин, HeadHunter
@glibin, glibin.ru
https://github.com/hhru/tortik
https://github.com/hhru/frontik
50
Использование SOA для построения сложных веб проектов - Виталий Глибин, PyCon RU 2014
Использование SOA для построения сложных веб проектов - Виталий Глибин, PyCon RU 2014
Использование SOA для построения сложных веб проектов - Виталий Глибин, PyCon RU 2014
Upcoming SlideShare
Loading in …5
×

Использование SOA для построения сложных веб проектов - Виталий Глибин, PyCon RU 2014

850 views

Published on

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
850
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
10
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Использование SOA для построения сложных веб проектов - Виталий Глибин, PyCon RU 2014

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

×