Принцип работы системы метапоиска
Aviasales cегодня 
● 800 000 запросов поиска авиабилетов в сутки 
● 6 000 000 уникальных пользователей в месяц 
● 35% от всех авиабилетов, проданных в России 
через интернет за 2013 год.
Эволюция системы метапоиска 
SEO 
client request Landing
Эволюция системы метапоиска 
SEO 
client request Landing redirect RoR Application
Эволюция системы метапоиска
Эволюция системы метапоиска 
SEO 
client request Landing 
Search 
redirect Client API 
Search 
Engine 
Minimal 
prices 
queue Statistic 
queue 
Travel 
Payouts client request API
Эволюция системы метапоиска 
SEO 
client request Landing 
Search 
redirect Client API 
Search 
Engine 
Minimal 
prices 
queue Statistic 
queue 
Travel 
Payouts client request API 
client request Mobile 
API 
Notifications 
queue
Архитектура поискового движка 
request 
request 
HAProxy 
request 
request 
request 
request 
NODE 1 
NGINX + PASSENGER 
Ruby on Rails 
NODE 2 
NGINX + PASSENGER 
Ruby on Rails 
... 
MYSQL 
MASTER/SLAVE 
REPLICATED 
request 
request 
NODE N 
NGINX + PASSENGER 
Ruby on Rails
Потребляемые ресурсы 
● 8 машин 8 ядер Xeon 2GHz 16 GB RAM. 
● 40-50 passenger процессов на каждой 
машине.
Проблемы данной реализации 
поискового движка 
1. Высокий уровень потребления ресурсов; 
2. Зависимость от базы данных; 
3. Сложность управления потоком 
выполнения задач; 
4. Сложность выкатки обновлений.
Проблемы разработки 
● Высокий порог вхождения в проект; 
● Скорость разработки; 
● Объектно-ориентированный подход при 
декомпозиции кода программы.
Конфигурируемость 
Набор сайтов-партнеров (гейтов) для опроса определяется в 
зависимости от: 
● Геолокации пользователя; 
● Пунктов вылета и назначения; 
● Набора пассажиров; 
● Источника траффика; 
● Локали пользователя; 
А также многих других параметров во всевозможных сочетаниях. 
Новые агентства добавлются в систему несколько раз в неделю. 
Правила выбора изменются несколько раз в час.
Новый поисковый движок “Ясень”
Ясень. DSL 
Используем функциональный подход при декомпозиции кода. 
params_validator 
ozon_gate 
onetwotrip_gate merge 
nabortu_gate 
Строим систему из независимых компонентов - юнитов, которые: 
● Имеют один входной аргумент и один выходной аргумент; 
● Изолированы друг от друга; 
● Не имеют зависимостей от среды исполнения; 
● Могут быть легко протестированы.
Ясень. Юниты
Ясень. Юниты 
Взаимодействие с юнитом осуществляется по HTTP. 
Данные и файлы конфигурации закодированы в JSON. 
Каждый юнит поддерживает API: 
● Вызов; 
● Загрузка конфигурации; 
● Выгрузка конфигурации. 
{"value": 100} 
call 
MultiplicationUnit 
{"multiplier": 5} set_config 
result 
{"value": 500}
Ясень. Цепочки юнитов 
Последовательные цепочки юнитов. 
● Входной аргумент цепочки подается в первый юнит цепочки; 
● Если юнит возвращает None, последующие юниты не выполняются, None объявляется 
результатом работы цепочки; 
● Результат работы юнита N передается в юнит N + 1; 
● Результатом работы цепочки является значение, возвращаемое последним юнитом. 
params_validator ozon_gate add_airlines
Ясень. Цепочки юнитов 
Параллельные цепочки юнитов. 
● Входной аргумент цепочки подается во все юниты цепочки; 
● Результаты работы всех юнитов собираются в массив; 
● Массив с результатами всех юнитов цепочки является результатом работы цепочки. 
ozon_gate 
trip_ru_gate 
nabortu_gate
Ясень. Цепочки юнитов 
Отложенные цепочки. 
● Выходным аргументом цепочки является входной аргумент цепочки; 
● Входной аргумент цепочки подается в первый юнит цепочки; 
● Результат работы юнита N подается на вход юниту N + 1. 
save_search_params send_in_rabbitmq
Ясень. Цепочки юнитов 
Пример композитной цепочки 
ozon_gate 
validator nabortu_gate 
merge 
sindbad_gate 
send_to_queue 
Parallel Sequential 
Delayed
Ясень. Работа с данными 
Типы данных в системе: 
● Справочники - часто читаются, редко обновляются; 
○ Информация об аэропортах; 
○ Информация об авиакомпаниях; 
○ Курсы валют. 
● Логи - часто пишутся, редко читаются; 
○ Информация о поведении пользователей; 
○ Поиски; 
○ Переходы на покупку; 
● Динамические данные - часто читаются, часто пишутся; 
○ Ссылки для переходов на страницу покупки; 
○ Результаты поиска;
Ясень. Работа с данными 
Справочники. 
● Небольние справочники (< 1mb) храним в памяти каждого рабочего 
процесса; 
● Большие справочники храним в файловой базе данных kyotocabinet. 
Файлы базы данных отображаются через mmap в адресное 
пространство рабочего процесса; 
● Информация для справочников хранится в файловой системе. При 
обновлении файлов inotify уведомляет о необходимости обновить 
данные в рабочих процессах; 
● Между нодами кластера файлы справочников распространяются 
lsyncd при помощи inotify.
Ясень. Работа с данными 
Логи. 
● Рабочие процессы не могут писать данные в глобальное хранилище 
непосредственно. В этом случае отказ глобального хранилища 
приведет к потере данных или повредит работоспособности 
приложения; 
● Рабочие процессы складывают данные в локальное хранилище. 
Переносом данных в глобальное хранилище занимаются 
вспомогательные независимые процессы.
Ясень. Работа с данными 
Динамические данные. 
● Скорость доступа на чтение и запись критичны. Поэтому используем 
in memory key-value хранилище; 
● Данные должны быть доступны со всех нод кластера; 
● Избыточность для обеспечения отказоустойчивости.
Ясень. Кластерная нода 
Local Storage 
REDIS REDIS 
MYSQL 
RabbitMQ
Ясень. Кластерная нода 
Детали реализации. 
● Каждый рабочий процесс - это отдельный Tornado сервер (python 3, 
tornado 3.2). Занимает в памяти 60mb; 
● Одна нода кластера 16 ядер Xeon 2GHz 16 GB RAM обрабатывает до 
300 одновременных поисковых запросов; 
● Local storage - в первых версиях in memory redis. Сейчас kyotocabinet, 
все данные на жестком диске.
Ясень. Кластер 
Local Storage 
REDIS REDIS 
MYSQL 
RabbitMQ 
Local Storage
Ясень. Кластер 
● 4 машины 16 ядер Xeon 2GHz 8GB RAM 
● Запас на случай пиковых нагрузок 40-50% уже в 
указанной конфигурации 
● Потребяет в 4 раза меньше оперативной памяти
Ясень. Аварийные ситуации 
Local Storage 
REDIS REDIS
Ясень. Аварийные ситуации 
Local Storage 
REDIS 
MYSQL 
RabbitMQ
Ясень. Аварийные ситуации 
Local Storage 
MYSQL 
RabbitMQ
Ясень. Аварийные ситуации 
Local Storage 
REDIS REDIS 
MYSQL 
RabbitMQ
Выводы 
1. Реализованная в поисковом движке “Ясень” архитектура лучше подходит для 
решаемых задач с точки зрения надежности, потребления ресурсов и 
конфигурируемости; 
2. Данная схема успешно используется в других проектах компании, таких как 
система аналитики партнерской сети TravelPayouts, движок рекламы 
мобильного приложения и прочих; 
Но, необходимо помнить: 
1. Данная схема не является универсальной и применима далеко не для всех 
задач; 
2. Отклоняясь от типовых решений и конфигураций, вы остаетесь один на один с 
возникающими проблемами; 
3. Платой за надежность почти всегда служит усложнение инфраструктуры.
Спасибо за 
внимание

Антон Щербаков, Отказоустойчивость на примере aviasales — почему даже если наши сервера падают, пользователи продолжают покупать билеты

  • 2.
  • 3.
    Aviasales cегодня ●800 000 запросов поиска авиабилетов в сутки ● 6 000 000 уникальных пользователей в месяц ● 35% от всех авиабилетов, проданных в России через интернет за 2013 год.
  • 4.
  • 5.
    Эволюция системы метапоиска SEO client request Landing redirect RoR Application
  • 6.
  • 7.
    Эволюция системы метапоиска SEO client request Landing Search redirect Client API Search Engine Minimal prices queue Statistic queue Travel Payouts client request API
  • 8.
    Эволюция системы метапоиска SEO client request Landing Search redirect Client API Search Engine Minimal prices queue Statistic queue Travel Payouts client request API client request Mobile API Notifications queue
  • 9.
    Архитектура поискового движка request request HAProxy request request request request NODE 1 NGINX + PASSENGER Ruby on Rails NODE 2 NGINX + PASSENGER Ruby on Rails ... MYSQL MASTER/SLAVE REPLICATED request request NODE N NGINX + PASSENGER Ruby on Rails
  • 10.
    Потребляемые ресурсы ●8 машин 8 ядер Xeon 2GHz 16 GB RAM. ● 40-50 passenger процессов на каждой машине.
  • 11.
    Проблемы данной реализации поискового движка 1. Высокий уровень потребления ресурсов; 2. Зависимость от базы данных; 3. Сложность управления потоком выполнения задач; 4. Сложность выкатки обновлений.
  • 12.
    Проблемы разработки ●Высокий порог вхождения в проект; ● Скорость разработки; ● Объектно-ориентированный подход при декомпозиции кода программы.
  • 13.
    Конфигурируемость Набор сайтов-партнеров(гейтов) для опроса определяется в зависимости от: ● Геолокации пользователя; ● Пунктов вылета и назначения; ● Набора пассажиров; ● Источника траффика; ● Локали пользователя; А также многих других параметров во всевозможных сочетаниях. Новые агентства добавлются в систему несколько раз в неделю. Правила выбора изменются несколько раз в час.
  • 14.
  • 15.
    Ясень. DSL Используемфункциональный подход при декомпозиции кода. params_validator ozon_gate onetwotrip_gate merge nabortu_gate Строим систему из независимых компонентов - юнитов, которые: ● Имеют один входной аргумент и один выходной аргумент; ● Изолированы друг от друга; ● Не имеют зависимостей от среды исполнения; ● Могут быть легко протестированы.
  • 16.
  • 17.
    Ясень. Юниты Взаимодействиес юнитом осуществляется по HTTP. Данные и файлы конфигурации закодированы в JSON. Каждый юнит поддерживает API: ● Вызов; ● Загрузка конфигурации; ● Выгрузка конфигурации. {"value": 100} call MultiplicationUnit {"multiplier": 5} set_config result {"value": 500}
  • 18.
    Ясень. Цепочки юнитов Последовательные цепочки юнитов. ● Входной аргумент цепочки подается в первый юнит цепочки; ● Если юнит возвращает None, последующие юниты не выполняются, None объявляется результатом работы цепочки; ● Результат работы юнита N передается в юнит N + 1; ● Результатом работы цепочки является значение, возвращаемое последним юнитом. params_validator ozon_gate add_airlines
  • 19.
    Ясень. Цепочки юнитов Параллельные цепочки юнитов. ● Входной аргумент цепочки подается во все юниты цепочки; ● Результаты работы всех юнитов собираются в массив; ● Массив с результатами всех юнитов цепочки является результатом работы цепочки. ozon_gate trip_ru_gate nabortu_gate
  • 20.
    Ясень. Цепочки юнитов Отложенные цепочки. ● Выходным аргументом цепочки является входной аргумент цепочки; ● Входной аргумент цепочки подается в первый юнит цепочки; ● Результат работы юнита N подается на вход юниту N + 1. save_search_params send_in_rabbitmq
  • 21.
    Ясень. Цепочки юнитов Пример композитной цепочки ozon_gate validator nabortu_gate merge sindbad_gate send_to_queue Parallel Sequential Delayed
  • 22.
    Ясень. Работа сданными Типы данных в системе: ● Справочники - часто читаются, редко обновляются; ○ Информация об аэропортах; ○ Информация об авиакомпаниях; ○ Курсы валют. ● Логи - часто пишутся, редко читаются; ○ Информация о поведении пользователей; ○ Поиски; ○ Переходы на покупку; ● Динамические данные - часто читаются, часто пишутся; ○ Ссылки для переходов на страницу покупки; ○ Результаты поиска;
  • 23.
    Ясень. Работа сданными Справочники. ● Небольние справочники (< 1mb) храним в памяти каждого рабочего процесса; ● Большие справочники храним в файловой базе данных kyotocabinet. Файлы базы данных отображаются через mmap в адресное пространство рабочего процесса; ● Информация для справочников хранится в файловой системе. При обновлении файлов inotify уведомляет о необходимости обновить данные в рабочих процессах; ● Между нодами кластера файлы справочников распространяются lsyncd при помощи inotify.
  • 24.
    Ясень. Работа сданными Логи. ● Рабочие процессы не могут писать данные в глобальное хранилище непосредственно. В этом случае отказ глобального хранилища приведет к потере данных или повредит работоспособности приложения; ● Рабочие процессы складывают данные в локальное хранилище. Переносом данных в глобальное хранилище занимаются вспомогательные независимые процессы.
  • 25.
    Ясень. Работа сданными Динамические данные. ● Скорость доступа на чтение и запись критичны. Поэтому используем in memory key-value хранилище; ● Данные должны быть доступны со всех нод кластера; ● Избыточность для обеспечения отказоустойчивости.
  • 26.
    Ясень. Кластерная нода Local Storage REDIS REDIS MYSQL RabbitMQ
  • 27.
    Ясень. Кластерная нода Детали реализации. ● Каждый рабочий процесс - это отдельный Tornado сервер (python 3, tornado 3.2). Занимает в памяти 60mb; ● Одна нода кластера 16 ядер Xeon 2GHz 16 GB RAM обрабатывает до 300 одновременных поисковых запросов; ● Local storage - в первых версиях in memory redis. Сейчас kyotocabinet, все данные на жестком диске.
  • 28.
    Ясень. Кластер LocalStorage REDIS REDIS MYSQL RabbitMQ Local Storage
  • 29.
    Ясень. Кластер ●4 машины 16 ядер Xeon 2GHz 8GB RAM ● Запас на случай пиковых нагрузок 40-50% уже в указанной конфигурации ● Потребяет в 4 раза меньше оперативной памяти
  • 30.
  • 31.
    Ясень. Аварийные ситуации Local Storage REDIS MYSQL RabbitMQ
  • 32.
  • 33.
    Ясень. Аварийные ситуации Local Storage REDIS REDIS MYSQL RabbitMQ
  • 34.
    Выводы 1. Реализованнаяв поисковом движке “Ясень” архитектура лучше подходит для решаемых задач с точки зрения надежности, потребления ресурсов и конфигурируемости; 2. Данная схема успешно используется в других проектах компании, таких как система аналитики партнерской сети TravelPayouts, движок рекламы мобильного приложения и прочих; Но, необходимо помнить: 1. Данная схема не является универсальной и применима далеко не для всех задач; 2. Отклоняясь от типовых решений и конфигураций, вы остаетесь один на один с возникающими проблемами; 3. Платой за надежность почти всегда служит усложнение инфраструктуры.
  • 35.