Статьи

Доступ к API без снятия вашего собственного приложения

Ах, мир API! Вы добавляете в свое приложение волшебный порошок, созданный из пюре из функций curl _ * (), и он волшебным образом интегрируется с внешними системами, предоставляя вам доступ ко всем интересным данным в мире. Иногда приходится проклинать 400 неверных запросов, показывая HTML вместо запроса Content-Type во время разработки, но это происходит из-за того, что в коде отсутствует какой-либо параметр. Однако после развертывания кода, выполняющего HTTP-запрос, все в порядке, верно? На самом деле, нет.

Распределенные системы

Как только вы вызываете внешний API с ваших производственных серверов, ваше приложение становится частью распределенных систем. К сожалению, эта система имеет часть вне вашего административного и географического контроля. На самом деле приложения всегда выполняются на множестве компьютеров, таких как веб-сервер и база данных; Тем не менее, эффекты интеграции смягчаются:

  • машины находятся в одной локальной сети и в центре обработки данных. Таким образом, задержка между TCP-соединениями между ними на порядок ниже, чем при подключении к другим системам.
  • Программное обеспечение, работающее на некоторых из этих машин, является надежным и стабильным, и его не требуется обновлять чаще, чем раз в год. Реляционные (и другие виды) баз данных следуют этим принципам, как и большинство инфраструктурных сервисов (кэши, мониторинг).
  • Все машины находятся под вашим контролем, поэтому вы можете вмешаться в них, когда возникнет ошибка, чтобы восстановить сервис.

При интеграции с внешним сервисом сценарий отличается:

  • фактические узлы, с которыми вы разговариваете, могут быть на другом континенте.
  • У них могут возникнуть проблемы с доступностью в любое время (ни у кого нет 100% доступности, а число 9 мало для многих веб-сервисов, не принадлежащих Google и Facebook).
  • Эти проблемы не находятся под вашим контролем, так как вы не можете откатить внешние узлы, и их состояние не зависит от ваших развертываний.

Вероятность отказа

Но являются ли эти зависимости проблемой? Разве Facebook API не должен быть всегда доступен?
Kaizumeus радикальн   в том смысле, что он никогда не должен вызывать внешнюю систему в том же процессе, который обслуживает HTTP-запрос от клиента. Причина заключается в том, чтобы избежать замыслов и задержек, связанных с доступностью внешних служб. Предположим, вы интегрируете с N API, каждый из которых имеет доступность 1-P: это означает, что вероятность того, что первый из них не работает или не ответит в любой момент времени, равна P. Предполагая, что API независимы, вероятность отсутствия один из них, который был отключен за один раз, равен (1-P) ^ N: если 1-P явно меньше 1, эта вероятность очень быстро уменьшается с увеличением N, даже для небольших значений P (вероятность отказа).

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

Стратегии

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

Выбор стратегии зависит от сценария: извлечение данных из хранилища обычно происходит синхронно, а уведомления, отправляемые письма и обновления в другие согласованные хранилища могут быть асинхронными.

Запросы, удовлетворяющие асинхронной модели, всегда должны выгружаться в очередь (ActiveMQ) или систему заданий (Gearman), которая выполняет их в другом процессе: поскольку вам не нужен ответ, вы можете выполнять их, не вешая клиента HTTP. Синхронизация пользователя с этими заданиями может быть осуществлена ​​с помощью модели извлечения (опрос системы) или с помощью операции выталкивания (уведомление о завершении).

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

Более того, задержка начальных взаимодействий уменьшается, поскольку вы можете вернуться сразу после создания задания. Это особенно важно, поскольку HTTP-запросы вкладываются друг в друга:

[A]  ->  [B]  ->  [C] 
[A]  <-  [B]  <-  [C] 
  200 OK    200 OK

становится

[A]->[B]
[A]<-[B] 202 Accepted
  [B]->[C]
    [B]<-[C] 202 Accepted

Эта модель хранения и пересылки типична для системы обмена сообщениями, но это не означает, что ее нельзя реализовать с помощью HTTP-запросов, возвращающих 202 Принято. Шаблон не является его реализацией. Существует механизм повторных попыток, который должен быть установлен как на стороне инициатора (в случае, если принимающая сторона недоступна), так и на принимающей стороне (чтобы всегда принимать входящие запросы и гарантировать доступность, но иметь возможность обрабатывать запросы позже и закрывать соединение немедленно).

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

Выводы

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