Устранение неполадок при использовании пакета SDK Java для Azure Cosmos DB версии 4 с ПОМОЩЬЮ API для учетных записей NoSQL

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Внимание

В этой статье описывается устранение неполадок только для пакета SDK Java версии 4 для Azure Cosmos DB. Дополнительные сведения см. в заметках о выпуске, репозитории Maven и советах по повышению производительности для пакета SDK Java версии 4 для Azure Cosmos DB. Если вы используете более раннюю версию версии 4, ознакомьтесь с руководством по переходу на пакет SDK Java для Azure Cosmos DB версии 4, чтобы помочь выполнить обновление до версии 4 .

В этой статье рассматриваются распространенные проблемы, обходные пути, шаги диагностики и средства при использовании пакета SDK Java для Azure Cosmos DB версии 4 с учетными записями Azure Cosmos DB для NoSQL. Пакет SDK Java для Azure Cosmos DB версии 4 предоставляет логическое представление на стороне клиента для доступа к Azure Cosmos DB для NoSQL. В этой статье описываются средства и подходы, которые помогут вам, если вы столкнетесь с проблемами.

Начните с этого списка:

Сбор данных диагностики

В пакете SDK для Java версии 4 у баз данных, контейнеров, элементов и ответов на запросы есть свойство Diagnostics. В это свойство записываются все сведения, связанные с одним запросом, в том числе сведения о повторных попытках и временных сбоях.

Система возвращает данные свойства Diagnostics (Диагностика) в виде строки. От версии к версии содержимое этой строки изменяется. Это связано с совершенствованием программного обеспечения для улучшения процесса устранения неполадок в различных сценариях. В каждой версии пакета SDK строка может нарушить его формат. Не анализируйте эту строку, чтобы избежать проблем, связанных с критическими изменениями.

В приведенном ниже примере кода показано, как выполнять чтение данных из журналов диагностики с помощью пакета SDK для Java версии 4.

Внимание

Мы рекомендуем проверить минимальную рекомендуемую версию пакета SDK для Java версии 4 и убедиться, что вы используете эту версию или более позднюю. Рекомендации по версиям можно получить здесь.

Операции с базой данных

CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists(databaseName);
CosmosDiagnostics diagnostics = databaseResponse.getDiagnostics();
logger.info("Create database diagnostics : {}", diagnostics); 

Операции с контейнерами

CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerProperties,
                  throughputProperties);
CosmosDiagnostics diagnostics = containerResponse.getDiagnostics();
logger.info("Create container diagnostics : {}", diagnostics);

Операции с элементами

// Write Item
CosmosItemResponse<Family> item = container.createItem(family, new PartitionKey(family.getLastName()),
                    new CosmosItemRequestOptions());
        
CosmosDiagnostics diagnostics = item.getDiagnostics();
logger.info("Create item diagnostics : {}", diagnostics);
        
// Read Item
CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
        
CosmosDiagnostics diagnostics = familyCosmosItemResponse.getDiagnostics();
logger.info("Read item diagnostics : {}", diagnostics);

Операции запроса

String sql = "SELECT * FROM c WHERE c.lastName = 'Witherspoon'";
        
CosmosPagedIterable<Family> filteredFamilies = container.queryItems(sql, new CosmosQueryRequestOptions(),
                    Family.class);
        
//  Add handler to capture diagnostics
filteredFamilies = filteredFamilies.handle(familyFeedResponse -> {
    logger.info("Query Item diagnostics through handle : {}", 
    familyFeedResponse.getCosmosDiagnostics());
});
        
//  Or capture diagnostics through iterableByPage() APIs.
filteredFamilies.iterableByPage().forEach(familyFeedResponse -> {
    logger.info("Query item diagnostics through iterableByPage : {}",
    familyFeedResponse.getCosmosDiagnostics());
});

Исключения Azure Cosmos DB

try {
  CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
} catch (CosmosException ex) {
  CosmosDiagnostics diagnostics = ex.getDiagnostics();
  logger.error("Read item failure diagnostics : {}", diagnostics);
}

Ведение журнала диагностика

Версии пакета SDK java версии 4.43.0 и более поздних версий поддерживают автоматическое ведение журнала диагностики Cosmos для всех запросов или ошибок, если они соответствуют определенным критериям. Разработчики приложений могут определять пороговые значения задержки (для точки (создания, чтения, замены, обновления, исправления) или неточечных операций (запросов, канала изменений, массового и пакетного)), размера платы за запрос и полезные данные. Если запросы превышают эти определенные пороговые значения, то cosmos диагностика для этих запросов будет автоматически выдаваться.

По умолчанию пакет SDK java версии 4 регистрирует эти диагностика автоматически в определенном формате. Однако это можно изменить, реализуя CosmosDiagnosticsHandler интерфейс и предоставляя собственный настраиваемый обработчик диагностики.

CosmosDiagnosticsHandler Затем их CosmosDiagnosticsThresholds можно использовать в CosmosClientTelemetryConfig объекте, который необходимо передать во CosmosClientBuilder время создания синхронизации или асинхронного клиента.

ПРИМЕЧАНИЕ. Эти пороговые значения диагностика применяются для различных типов диагностика включая ведение журнала, трассировку и телеметрию клиента.

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

Определение настраиваемых пороговых значений диагностики

//  Create diagnostics threshold
CosmosDiagnosticsThresholds cosmosDiagnosticsThresholds = new CosmosDiagnosticsThresholds();
//  These thresholds are for demo purposes
//  NOTE: Do not use the same thresholds for production
cosmosDiagnosticsThresholds.setPayloadSizeThreshold(100_00);
cosmosDiagnosticsThresholds.setPointOperationLatencyThreshold(Duration.ofSeconds(1));
cosmosDiagnosticsThresholds.setNonPointOperationLatencyThreshold(Duration.ofSeconds(5));
cosmosDiagnosticsThresholds.setRequestChargeThreshold(100f);

Определение пользовательского обработчика диагностики

//  By default, DEFAULT_LOGGING_HANDLER can be used
CosmosDiagnosticsHandler cosmosDiagnosticsHandler = CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER;

//  App developers can also define their own diagnostics handler
cosmosDiagnosticsHandler = new CosmosDiagnosticsHandler() {
    @Override
    public void handleDiagnostics(CosmosDiagnosticsContext diagnosticsContext, Context traceContext) {
        logger.info("This is custom diagnostics handler: {}", diagnosticsContext.toJson());
    }
};

Определение CosmosClientTelemetryConfig

//  Create Client Telemetry Config
CosmosClientTelemetryConfig cosmosClientTelemetryConfig =
    new CosmosClientTelemetryConfig();
cosmosClientTelemetryConfig.diagnosticsHandler(cosmosDiagnosticsHandler);
cosmosClientTelemetryConfig.diagnosticsThresholds(cosmosDiagnosticsThresholds);

//  Create sync client
CosmosClient client = new CosmosClientBuilder()
    .endpoint(AccountSettings.HOST)
    .key(AccountSettings.MASTER_KEY)
    .clientTelemetryConfig(cosmosClientTelemetryConfig)
    .buildClient();

Повторная попытка разработки

Ознакомьтесь с нашим руководством по разработке устойчивых приложений с помощью пакетов SDK Для Azure Cosmos DB, чтобы узнать, как разрабатывать устойчивые приложения и узнать, какие являются семантикой повторных попыток пакета SDK.

Распространенные проблемы и обходные решения для них

Проверка метрик портала

Проверка метрик портала поможет определить, связана ли проблема с клиентом или со службой. Например, если метрики содержат много запросов подряд с кодом состояния HTTP 429 (ограничение количества), значит, к запросам применяется регулирование. В этом случае просмотрите сведения в разделе Слишком высокая частота запросов.

Проблемы с сетью, ошибка из-за истечения времени ожидания чтения Netty, низкая пропускная способность, высокая задержка

Общие рекомендации

Обеспечить наилучшую производительность можно так.

  • Убедитесь, что приложение выполняется в том же регионе, который указан для вашей учетной записи Azure Cosmos DB.
  • Проверьте использование ЦП на узле, где выполняется приложение. Если используются 50 или более процентов ЦП, запустите приложение на узле с лучшей конфигурацией. Можно также распределить нагрузку на большее число компьютеров.

Регулирование подключения

Регулирование подключения может произойти из-за ограничения числа подключений на узле или нехватки портов Azure SNAT (PAT).

Ограничение числа подключений на узле

Некоторые системы Linux (например, Red Hat) имеют ограничение на общее число открытых файлов. Сокеты в Linux реализованы как файлы, поэтому это число также существенно ограничивает общее число подключений. Выполните следующую команду.

ulimit -a

Максимальное разрешенное количество открытых файлов, которые определены как "nofile", должно быть по крайней мере вдвое больше, чем размер пула подключений. Дополнительные сведения см. в советах по повышению производительности для пакета SDK Java версии 4 для Azure Cosmos DB.

Нехватка портов SNAT (PAT) Azure

Если ваше приложение развернуто в службе "Виртуальные машины Azure" без общедоступного IP-адреса, по умолчанию порты Azure SNAT используются для установления подключений к любой конечной точке вне вашей виртуальной машины. Количество разрешенных подключений от виртуальной машины к конечной точке Azure Cosmos DB ограничивается конфигурацией Azure SNAT.

Порты Azure SNAT используются, только если у виртуальной машины есть частный IP-адрес и процесс на виртуальной машине пытается установить подключение к общедоступному IP-адресу. Избежать ограничений Azure SNAT можно двумя способами:

  • Добавьте конечную точку службы Azure Cosmos DB к подсети виртуальной сети для службы "Виртуальные машины Azure". Дополнительные сведения см. в статье Конечные точки служб для виртуальной сети Azure.

    При включении конечной точки службы запросы больше не отправляются с общедоступного IP-адреса в Azure Cosmos DB. Вместо этого отправляются идентификаторы виртуальной сети и подсети. Это изменение может привести к сбою брандмауэра, если разрешены только общедоступные IP-адреса. Если вы используете брандмауэр, при включении конечной точки службы добавьте подсеть в брандмауэре с помощью списков управления доступом для виртуальных сетей.

  • Назначьте общедоступный IP-адрес виртуальной машине Azure.

Не удается связаться со службой — брандмауэр

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

GoneException{error=null, resourceAddress='https://cdb-ms-prod-westus-fd4.documents.azure.com:14940/apps/e41242a5-2d71-5acb-2e00-5e5f744b12de/services/d8aa21a5-340b-21d4-b1a2-4a5333e7ed8a/partitions/ed028254-b613-4c2a-bf3c-14bd5eb64500/replicas/131298754052060051p//', statusCode=410, message=Message: The requested resource is no longer available at the server., getCauseInfo=[class: class io.netty.channel.ConnectTimeoutException, message: connection timed out: cdb-ms-prod-westus-fd4.documents.azure.com/101.13.12.5:14940]

Если на компьютере приложения запущен брандмауэр, откройте диапазон портов от 10 000 до 20 000, которые используются в прямом режиме. Также ознакомьтесь с разделом Ограничение числа подключений на узле.

UnknownHostException

UnknownHostException означает, что платформа Java не может разрешить запись DNS для конечной точки Azure Cosmos DB на затронутом компьютере. Убедитесь, что компьютер может разрешить запись DNS или у вас есть пользовательское программное обеспечение разрешения DNS (например, VPN или прокси-сервер или пользовательское решение), убедитесь, что он содержит правильную конфигурацию для конечной точки DNS, что ошибка не может быть устранена. Если ошибка происходит постоянно, можно проверить разрешение DNS компьютера с помощью команды curl на конечной точке, описанной в этой ошибке.

Прокси-сервер HTTP

При использовании прокси-сервера HTTP убедитесь, что он может поддерживать число подключений, указанное в свойстве ConnectionPolicy пакета SDK. В противном случае возникнут проблемы с подключением.

Недопустимый шаблон кодировки: блокировка потока ввода-вывода Netty

Пакет SDK использует библиотеку Netty для ввода-вывода, чтобы связываться с Azure Cosmos DB. Пакет SDK имеет асинхронный API и использует неблокирующие API Netty для ввода-вывода. Операции ввода-вывода, присущие пакету SDK, выполняются в потоках Netty для ввода-вывода. Число потоков Netty для ввода-вывода настроено так, чтобы совпадать с числом ядер ЦП компьютера, где выполняется приложение.

Потоки Netty для ввода-вывода предназначены только для операций Netty для ввода-вывода без блокировки. Пакет SDK возвращает результат вызова API, касающийся одного из потоков Netty для ввода-вывода, в код приложения. Если приложение выполняет операцию длительное время после получения результатов касательно потока Netty, пакет SDK может не располагать достаточным количеством потоков ввода-вывода для выполнения внутренних операций ввода-вывода. Кодирование такого приложения может привести к низкой пропускной способности, длительной задержке и сбоям io.netty.handler.timeout.ReadTimeoutException. Обходное решение заключается в том, чтобы переключить поток, когда вы знаете, что операция займет некоторое время.

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

Асинхронный API пакета SDK для Java версии 4 (Maven com.azure::azure-cosmos)


//Bad code with read timeout exception

int requestTimeoutInSeconds = 10;

/* ... */

AtomicInteger failureCount = new AtomicInteger();
// Max number of concurrent item inserts is # CPU cores + 1
Flux<Family> familyPub =
        Flux.just(Families.getAndersenFamilyItem(), Families.getAndersenFamilyItem(), Families.getJohnsonFamilyItem());
familyPub.flatMap(family -> {
    return container.createItem(family);
}).flatMap(r -> {
    try {
        // Time-consuming work is, for example,
        // writing to a file, computationally heavy work, or just sleep.
        // Basically, it's anything that takes more than a few milliseconds.
        // Doing such operations on the IO Netty thread
        // without a proper scheduler will cause problems.
        // The subscriber will get a ReadTimeoutException failure.
        TimeUnit.SECONDS.sleep(2 * requestTimeoutInSeconds);
    } catch (Exception e) {
    }
    return Mono.empty();
}).doOnError(Exception.class, exception -> {
    failureCount.incrementAndGet();
}).blockLast();
assert(failureCount.get() > 0);

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

Асинхронный API пакета SDK для Java версии 4 (Maven com.azure::azure-cosmos)

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(ex);

Может потребоваться выполнить длительную работу (например, такую, которая требует значительных вычислений или блокировки ввода-вывода). В этом случае необходимо переключение потока на рабочую роль, предоставляемую параметром customScheduler с помощью API .publishOn(customScheduler).

Асинхронный API пакета SDK для Java версии 4 (Maven com.azure::azure-cosmos)

container.createItem(family)
        .publishOn(customScheduler) // Switches the thread.
        .subscribe(
                // ...
        );

Используя publishOn(customScheduler), вы освобождаете поток Netty для ввода-вывода и переключаетесь на собственный поток, предоставляемый настраиваемым планировщиком. Это изменение позволяет решить проблему. Сбой io.netty.handler.timeout.ReadTimeoutException больше возникать не будет.

Высокая частота запросов

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

  • Применение интервала задержки getRetryAfterInMilliseconds.

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

Обработка ошибок реактивной цепочки пакета SDK для Java

Обработка ошибок из пакета SDK Java для Azure Cosmos DB важна, когда речь идет о логике приложения клиента. Существуют различные механизмы обработки ошибок, предоставляемые платформой реактора , которая может использоваться в различных сценариях. Мы рекомендуем клиентам изучить эти операторы по обработке ошибок и использовать те, которые лучше подходят для их сценариев логики повторных попыток.

Внимание

Мы не рекомендуем использовать оператор onErrorContinue(), так как он поддерживается не во всех сценариях. Обратите внимание, что onErrorContinue() — это специальный оператор, из-за которого поведение реактивной цепочки может стать неочевидным. Он работает с вышестоящими, а не нижестоящими операторами и требует для своей работы поддержки определенных операторов. При его использовании область может легко распространиться на вышестоящие элементы в коде библиотеки, что не предусмотрено (и приводит к неожиданному поведению). Дополнительные сведения об этом специальном операторе см. в документации по onErrorContinue().

Ошибка подключения к эмулятору Azure Cosmos DB

Сертификат HTTPS эмулятора для Azure Cosmos DB является самозаверяющим. Чтобы SDK работал с эмулятором, необходимо импортировать сертификат эмулятора в Java TrustStore. Дополнительные сведения см. в статье Экспорт сертификатов эмулятора для Azure Cosmos DB.

Проблемы с конфликтом зависимостей

Пакет SDK Java для Azure Cosmos DB извлекает множество зависимостей; Как правило, если дерево зависимостей проекта включает более старую версию артефакта, от которой зависит пакет SDK Java для Azure Cosmos DB, это может привести к непредвиденным ошибкам при запуске приложения. Если вы отладите, почему приложение неожиданно создает исключение, рекомендуется дважды проверка, что дерево зависимостей не случайно извлекает более раннюю версию одного или нескольких зависимостей пакета SDK java для Azure Cosmos DB.

Обходной путь для такой проблемы состоит в том, чтобы определить, какие из зависимостей вашего проекта требуют старую версию, исключить транзитивную зависимость от этой более старой версии и позволить пакету SDK Java для Azure Cosmos DB установить более новую версию.

Чтобы узнать, какие зависимости проекта используют более старую версию, что необходима пакету SDK Java для Azure Cosmos DB, выполните следующую команду для файла pom.xml вашего проекта.

mvn dependency:tree

Дополнительные сведения см. в руководстве Maven по дереву зависимостей.

После того как вы узнаете, какая зависимость проекта зависит от более старой версии, вы можете изменить зависимость от этой библиотеки в файле POM и исключить транзитивную зависимость, следуя приведенному ниже примеру (предполагается, что устаревшей зависимостью является reactor-core):

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-reactor}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-reactor}</artifactId>
  <version>${version-of-lib-which-brings-in-reactor}</version>
  <exclusions>
    <exclusion>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-core</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Дополнительные сведения см. в руководстве по исключению транзитивной зависимости.

Включение ведения журнала для пакета SDK клиента

Пакет SDK Java версии 4 для Azure Cosmos DB использует SLF4j как интерфейс ведения журнала, который поддерживает запись на популярные платформы ведения журналов, такие как log4j и logback.

Например, если вы хотите использовать log4j как платформу ведения журналов, добавьте в путь к классу Java следующие библиотеки:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>${log4j.version}</version>
</dependency>

Добавьте также конфигурацию log4j.

# this is a sample log4j configuration

# Set root logger level to INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.azure.cosmos=INFO
#log4j.category.io.netty=OFF
#log4j.category.io.projectreactor=OFF
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n

Дополнительные сведения см. в руководстве по ведению журналов с использованием sfl4j.

Сетевая статистика ОС

Запустите команду netstat, чтобы понять, сколько подключений находится в состоянии ESTABLISHED, CLOSE_WAIT и т. д.

В Linux вы можете запустить следующую команду:

netstat -nap

В Windows можно выполнить ту же команду, но с другими аргументами:

netstat -abn

Отфильтруйте результат для отображения только подключений к конечной точке Azure Cosmos DB.

Количество подключений к конечной точке Azure Cosmos DB в состоянии ESTABLISHED не должно превышать настроенного размера пула подключений.

Много подключений к конечной точке Azure Cosmos DB могут быть в состоянии CLOSE_WAIT. Возможно, более 1000. Такое большое количество указывает, что подключения быстро устанавливаются и быстро разрываются. Подобная ситуация может привести к проблемам. Дополнительные сведения см. в разделе Распространенные проблемы и их обходные решения.

Типичные проблемы с запросами

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

Следующие шаги