Принцип единственной ответственности помог нам исправить Icebreaker, нашу чат-службу. Вот как мы это сделали.

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

Около года назад мы выпустили новую службу чата (которую мы назвали Icebreaker). Это позволило нашим пользователям общаться с водителями через само приложение, а не использовать SMS (что стоит денег как водителю, так и клиенту).

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

Проблемы)

Короче говоря, Icebreaker зависел от слишком многих других сервисов, чтобы функционировать должным образом. Давайте посмотрим на некоторые задачи, которые выполняла Icebreaker для создания канала:

  1. Авторизовать вызов API: это вызвало нашу службу аутентификации.
  2. Получите профиль клиента: для этого потребовался HTTP-звонок в нашу службу поддержки.
  3. Получение профиля драйверов: для этого потребовался HTTP-вызов нашей службы драйверов.
  4. Убедитесь, что пара клиент-водитель находится в активном порядке: это позвонило в нашу службу хранения активных бронирований.
  5. Создайте канал.

Если какая-либо из этих служб выйдет из строя, произойдет сбой и Icebreaker.

Даже если мы сможем обеспечить 99% времени безотказной работы для всех рассматриваемых сервисов, это все равно означает, что шансы на то, что все они будут запущены одновременно, составят 96%.

P(icebreaker active) = P(customer service active) * P(driver service active) * P(authentication service active) * P(active booking storage active) = 0.99 * 0.99 * 0.99 * 0.99 ~= 0.96

Это означает, что время простоя увеличилось в четыре раза (4% вместо 1%).

Не моя работа

Когда сервис начинает делать слишком много вещей, рано или поздно он обязательно выйдет из строя. В данном случае задача Icebreaker заключалась в создании канала между клиентом и водителем. Однако он делал все эти дополнительные вещи: например, аутентификацию, проверку и получение профиля. 🤦‍♂

Давайте посмотрим на изменения, которые мы внесли, чтобы избавиться от каждой зависимости:

Аутентификация

Каждый вызов API, поступающий в Icebreaker, сопровождался токеном API, который требовал аутентификации. Чтобы решить эту проблему, мы добавили шлюз API Kong. Это подтвердило все запросы и добавило информацию об аутентифицированном пользователе в заголовки API.

Теперь каждый запрос, поступающий на «Ледокол», был подтвержден.

Вывод: говорите, а не спрашивайте. Запросы, поступающие от шлюза API, сообщали службе, что они аутентифицированы, а не Icebreaker, чтобы запрашивать другую службу.

Получение профиля

Чтобы создать канал, нам потребовалась информация, называемая «токеном чата» для каждого пользователя. Это хранилось в службе поддержки клиентов и в службе водителя для водителя.

Поскольку Icebreaker был единственной службой, использующей этот токен, мы переместили эти токены в него и удалили их из служб клиентов и водителей.

Теперь у Icebreaker была вся необходимая информация в собственной базе данных, которая была более надежным источником правды по сравнению со всем другим HTTP-сервисом.

Важный вывод: если ваша служба - единственная, использующая какую-либо информацию, она должна находиться внутри самой службы

Активное хранилище бронирования

Icebreaker создавал канал по запросу каждый раз, когда пользователь нажимает на его API создания канала. Это создание по запросу требовало от нас проверки наличия активного бронирования, для которого пользователю необходимо было создать канал. В конце концов, не имело смысла создавать канал, если у сторон не было порядка друг с другом.

Чтобы исправить это, мы перешли на асинхронную архитектуру создания каналов. Вместо создания канала по запросу мы использовали конвейер данных GO-JEK, который публиковал события каждый раз, когда было сделано бронирование. Icebreaker теперь состоял из двух компонентов: рабочего и сервера.

  1. Рабочий потреблял события бронирования каждый раз, когда они создавались. Затем он создал канал между клиентом и водителем в бронировании и сохранил информацию о канале в кеше Redis.
  2. Сервер, как и раньше, обслуживал запросы на создание канала. Только на этот раз каналы уже были созданы и кешированы вместе с порядковым номером.

Таким образом, вместо создания каналов по запросу, каналы были созданы и сохранены заранее. Поскольку мы использовали события бронирования из нашего собственного конвейера данных, не было необходимости проверять, было ли бронирование подлинным или нет.

Ключевой вывод: снова говорите, а не спрашивайте. События, поступающие из нашего конвейера данных, показали Icebreaker, что заказы были подлинными. Это означало, что он мог создать канал вместо обращения к сервису для проверки подлинности бронирования.

Полученные результаты

Теперь Icebreaker делал только то, для чего он действительно был предназначен: создавал каналы.

Поскольку мы удалили зависимости от большинства внешних систем, нам больше не приходилось беспокоиться об одном системном сбое, вызывающем сбои в работе Icebraker. Также снизилась нагрузка на внешние сервисы, поскольку Icebreaker больше не использовал свои конечные точки для создания каналов.

Переход на асинхронную архитектуру также привел к резкому сокращению времени отклика с ~ 200 мс до ~ 10 мс, поскольку мы заранее создавали и кэшировали каналы для каждого заказа.

Выводы, которые мы извлекли из этого опыта, соответствуют принципу единой ответственности. В конце концов, всегда лучше спросить себя: «Может ли эта услуга сделать меньше?»

Как и в случае с Icebreaker, наши инженеры делают больше с меньшими затратами. Мы зарегистрировали 6600-кратный рост общего количества выполненных заказов с командой из 250+ инженеров. Хотите стать частью команды, которая меняет определение бережливого инжиниринга? Воспользуйтесь своим шансом поработать с GOJEK, поскольку мы расширяемся в Сингапуре, Таиланде, Вьетнаме и Филиппинах. Посетите gojek.jobs, чтобы узнать о возможностях.