Вадим Мамонтов
Технический руководитель, Dynatech

Я работаю в Dyninno Group — крупном международном холдинге с присутствием на рынке в 50 странах, который предоставляет продукты и услуги в сфере технологий для путешествий, финансов и развлечений. Наша компания была основана на продаже авиабилетов, и это до сих пор является наиболее значимым направлением нашей деятельности. Вот почему быстрый поиск, позволяющий найти как можно больше релевантных результатов поиска для клиентов, является краеугольным камнем нашего бизнеса. Сегодня давайте посмотрим, как работает наша поисковая система изнутри.

С точки зрения бизнес-операций — мы снабжаем наших агентов необходимыми инструментами для облегчения продажи авиабилетов по телефону. В процессе поиска рейса задействовано около десятка систем и сервисов, каждый из которых отвечает за небольшую часть всей операции. Но, в основе его лежит система FLST (Flight Search Tool), о которой хотелось бы рассказать подробнее.

Процесс поиска вариантов перелета в FLST

FLST — это набор масштабируемых сервисов, развернутых в Kubernetes. Задача этих сервисов — как можно быстрее найти все возможные варианты перелета. Запрос на поиск рейса обычно состоит из базовой информации, такой как дата, аэропорты вылета и назначения, количество пассажиров и класс (бизнес/эконом). Один входящий запрос на поиск рейса (мы называем его логическим поиском) обычно генерирует около 20 конфигураций верхнего уровня (или физических поисков). Количество таких конфигураций зависит от того, где (т.е. у каких провайдеров бронирования) и при каких условиях наши сервисы будут производить поиск, но обо всем по порядку.

Таким образом, каждый запрос обрабатывается следующим образом:

Шаг 1.Получив поисковый запрос, мы помещаем его в очередь (используем Amazon SQS). Валидатор — это первый сервис, который начинает работать с входящим запросом. Валидатор проверяет входящий запрос по его параметрам, и если все верно, запрос на основе набора заданных правил преобразуется в серию объектов конфигурации. Эти конфигурации, как упоминалось выше, содержат всю необходимую информацию, необходимую для отправки запроса глобальному провайдеру бронирования. Мы используем базу данных MariaDB для хранения этих правил, и эти правила формируются через отдельный пользовательский интерфейс. Правила поддерживают наши специалисты по продажам из бизнес-подразделений: они определяют, с какими авиакомпаниями мы работаем и на каких условиях, по каким направлениям мы можем продавать билеты, где лучше всего искать билеты и какие могут быть исключения.

Шаг 2.Как только Валидатор создаст эти конфигурации, он перенаправит их через KeyDB в следующую службу, службу регулирования. Основная задача сервиса Throttling — попытаться исключить часть конфигураций, которые не очень хорошо себя зарекомендовали в прошлом, на основе накопленной статистики (например, когда определенные авиакомпании редко выполняют рейсы по данному направлению или поиск таких вариантов занимал слишком много времени). давно в прошлом и др.). Чтобы не хранить гигабайты статистики, Throttling использует постоянно обновляемый фильтр Блума. Если какая-то конфигурация не проходит через фильтр, то мы не исключаем ее сразу, а включаем эту конфигурацию в небольшом количестве случаев, примерно в 10% из них.

Фильтр Блума — это вероятностная структура данных, позволяющая определить, принадлежит ли объект набору, без необходимости сохранять весь набор. В нашем случае это позволяет нам определить, рассматривался ли аналогичный запрос на рейс как неудовлетворительный в прошлом. Наш фильтр основан на статистике обо всех медленных (более 5 секунд) поисковых запросах и поисковых запросах, которые не дали никаких результатов, которые мы обновляем несколько раз в неделю. Применение фильтра Блума позволяет при сохранении гораздо меньшего объема информации получить практически такой же результат с небольшим процентом ложных срабатываний, что в нашем случае приемлемо.

Шаг 3.После обработки службой Throttling мы получаем список физических поисков (точнее, их конфигураций), которые мы уже можем выполнять. Эти конфигурации передаются через KeyDB и подхватываются следующей службой в цепочке, Search Workers, которая отправляет запросы на варианты полета в Global Distribution Systems (GDS) в формате, совместимом с конкретной GDS. GDS — это автоматизированные системы, которые служат связующим звеном между туристическими агентствами и поставщиками, такими как авиакомпании, отели и другие услуги, связанные с поездками.

GDS возвращает разное количество вариантов перелета для каждого запроса, но обычно это не более 300 (хотя не редкость и более 1000 предложений). Это финальный этап, на котором сервисы, отправившие нам первоначальный запрос, получают результат поиска (FLST либо помещает их в Amazon SQS, либо пересылает через вызов REST). На этом этапе мы также оцениваем скорость обработки каждого задания на поиск рейса (согласно нашим метрикам, все поиски, которые занимают более 5 секунд, считаются медленными), и мы используем эту информацию для последующего обновления фильтра Блума.

Это огромное количество вариантов полета обрабатывается другими нашими внутренними системами. Эти результаты формируются в список лучших вариантов (например, рейс с самой короткой пересадкой или самый дешевый рейс), которые мы готовы предложить клиенту, но это уже не задача для FLST. На данном этапе FLST выполнила свою задачу — максимально быстро нашла все возможные варианты перелетов. После этого задачей оптимизации этих вариантов полета будет заниматься другая система, не менее интересная с технологической точки зрения. Но это уже другая история.

Из чего состоит FLST

FLST изначально был написан на PHP. Но вскоре мы поняли, что PHP стало сложно обеспечивать высокую скорость обработки всех сопутствующих процессов. Масштабируемости и гибкости в организации параллелизма не хватало простоты. Нам нужен был способ разделить нагрузку, сделать поиск рейсов максимально масштабируемым и параллельным. Наш выбор состоял в том, чтобы выгрузить часть кода в отдельные сервисы, и язык программирования Go оказался для этого наиболее подходящим.

Сегодня Validator, Throttling и Search Workers — это наши сервисы, написанные на Go и развернутые в кластерах Kubernetes. Архитектура сервиса и взаимодействие через Amazon SQS и KeyDB позволяют нам гораздо проще масштабироваться и при необходимости сосредоточиться на повышении производительности отдельного звена во всей цепочке, если это необходимо.

Все наши сервисы масштабируются как по горизонтали (на основе собранных метрик), так и по вертикали (добавляя ресурсы, если мы считаем это целесообразным). Например, по умолчанию работает менее 20 поисковых служб (модулей); однако мы могли масштабировать их почти до 100 в пиковые периоды. У каждой службы поиска есть ограничение на количество одновременных поисков, и это управляемый параметр, основанный на текущих ресурсах ЦП/ОЗУ или других показателях, специфичных для службы, например, когда количество выполняемых физических поисков увеличивается, больше модулей автоматически запускаются для их обработки.

Важным моментом для каждого сервиса является мониторинг — мы собираем огромное количество метрик, которые визуализируются в нескольких дашбордах в Grafana, а также собираем логи всего процесса поиска в ELK. Для компиляции и докеризации сервисов мы используем пайплайны GitLab CI, а деплоем занимается Argo CD.

Выводы и планы

Подбор оптимальных вариантов перелета и продажа авиабилетов и сопутствующих услуг составляют основу бизнеса Dyninno Group. Сотрудничество с бизнес-подразделениями обеспечивает синергию и позволяет нам лучше настраивать доставку релевантных результатов поиска.

Нашим агентам важно получать ответы на свои запросы как можно быстрее. Поэтому мы постоянно работаем над оптимизацией нашей системы FLST.

Вадим Мамонтов, технический руководитель, Dynatech
https://github.com/kaytrance