Рубрики
Без рубрики

Подкачка и асинхронные вызовы с помощью API Kubernetes

Автор оригинала: Philippe Sevestre.

1. введение

В этом уроке мы продолжим изучение API Kubernetes для Java . На этот раз мы сосредоточимся на двух его функциях: подкачка и асинхронные вызовы .

2. Пейджинг

В двух словах, подкачка позволяет нам перебирать большой результирующий набор кусками , т. е. страницами – отсюда и название этого метода. В контексте Java API Kubernetes эта функция доступна во всех методах, возвращающих список ресурсов . Эти методы всегда включают два необязательных параметра, которые мы можем использовать для итерации результатов:

  • limit : максимальное количество элементов, возвращаемых в одном вызове API
  • continue : Токен продолжения , который сообщает серверу начальную точку для возвращаемого результирующего набора

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

Теперь давайте посмотрим, как использовать эти параметры, чтобы получить список всех доступных модулей в кластере с помощью этого метода:

ApiClient client = Config.defaultClient();
CoreV1Api api = new CoreV1Api(client);
String continuationToken = null;
do {
    V1PodList items = api.listPodForAllNamespaces(
      null,
      continuationToken, 
      null,
      null, 
      2, 
      null, 
      null,
      null,
      10,
      false);
    continuationToken = items.getMetadata().getContinue();
    items.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
} while (continuationToken != null);

Здесь второй параметр модуля list Для всех пространств имен() API вызова содержит маркер продолжения, а пятый-параметр limit . В то время как limit обычно является просто фиксированным значением, continue требует немного дополнительных усилий.

Для первого вызова мы отправляем значение null , сигнализируя серверу, что это первый вызов последовательности запросов подкачки . После получения ответа мы получаем новое значение для следующего continue значения для использования из соответствующего поля метаданных списка.

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

2.1. Разбиение на страницы

Механизм подкачки довольно прост, но есть несколько деталей, которые мы должны иметь в виду:

  • В настоящее время API не поддерживает сортировку на стороне сервера. Учитывая нынешнее отсутствие поддержки сортировки на уровне хранилища, это вряд ли изменится в ближайшее время
  • Все параметры вызова, за исключением continue , должны быть одинаковыми между вызовами
  • Значение continue должно рассматриваться как непрозрачный дескриптор. Мы никогда не должны делать никаких предположений о его ценности
  • Итерация является односторонней . Мы не можем вернуться в результирующий набор, используя ранее полученный продолжить | токен Несмотря на то, что возвращаемые метаданные списка содержат поле
  • количество оставшихся элементов , его значение не является надежным и не поддерживается всеми реализациями

2.2. Согласованность Данных Списка

Поскольку кластер Kubernetes является очень динамичной средой, существует вероятность того, что результирующий набор, связанный с разбитой на страницы последовательностью вызовов, будет изменен во время чтения клиентом . Как ведет себя API Kubernetes в этом случае?

Как поясняется в документации Kubernetes , API-интерфейсы списков поддерживают параметр resource Version , который вместе с параметром resourceVersionMatch определяет , как выбрана конкретная версия для включения. Однако для случая выгружаемого результирующего набора поведение всегда одно и то же: “Токен продолжения, точный”.

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

3. Асинхронные вызовы

До сих пор мы использовали API Kubernetes синхронно, что хорошо для простых программ, но не очень эффективно с точки зрения использования ресурсов, поскольку он блокирует вызывающий поток до тех пор, пока мы не получим ответ от кластера и не обработаем его. Такое поведение сильно повредит отзывчивости приложения, если, например, мы начнем выполнять эти вызовы в потоке графического интерфейса.

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

Проверяя класс Core V1 Api , мы заметим, что для каждого синхронного метода xxx() существует также вариант xxxAsync () . Например, асинхронный метод для list Pod Для всех пространств имен() является listPodForAllNamespacesAsync() . Аргументы те же, с добавлением дополнительного параметра для реализации обратного вызова.

3.1. Детали обратного вызова

Объект параметра обратного вызова должен реализовать универсальный интерфейс Api обратного вызова, который содержит всего четыре метода:

  • onSuccess: Вызывается тогда и только тогда, когда вызов удался. Первый тип аргумента тот же, который будет возвращен синхронной версией
  • при сбое: Вызывается ошибка вызова сервера или ответ содержит код ошибки
  • onUploadProgress : Вызывается во время загрузки. Мы можем использовать этот обратный вызов для предоставления обратной связи пользователю во время длительной операции
  • onDownloadProgress : То же самое , что и onUploadProgress , но для загрузок

Асинхронные вызовы также не возвращают обычный результат. Вместо этого они возвращают экземпляр OkHttp (базовый клиент REST, используемый API Kubernetes) Call , который работает как дескриптор текущего вызова. Мы можем использовать этот объект для опроса состояния завершения или, если мы хотим, отменить его до завершения.

3.2. Пример Асинхронного вызова

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

// Start async call
CompletableFuture p = AsyncHelper.doAsync(api,(capi,cb) ->
  capi.listNodeAsync(null, null, null, null, null, null, null, null, 10, false, cb)
);
p.thenAcceptAsync((nodeList) -> {
    nodeList.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
});
// ... do something useful while we wait for results

Здесь помощник оборачивает вызов асинхронного вызова и адаптирует его к более стандартному CompletableFuture . Это позволяет нам использовать его с другими библиотеками, такими как библиотеки из проекта Reactor . В этом примере мы добавили этап завершения, который выводит все метаданные в стандартный вывод.

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

  • основной поток , который запускает асинхронный вызов
  • Потоки Ofhttps, используемые для выполнения фактического HTTP-вызова
  • Поток завершения, в котором обрабатываются результаты

4. Заключение

В этой статье мы рассмотрели, как использовать подкачку и асинхронные вызовы с помощью API Java Kubernetes.

Как обычно, полный исходный код примеров можно найти на GitHub .