Поделиться через


Устранение неполадок при использовании Azure Cosmos DB Java SDK v4 с 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. В это свойство записываются все сведения, связанные с одним запросом, в том числе сведения о повторных попытках и временных сбоях.

Диагностика возвращается в виде строки. От версии к версии содержимое этой строки изменяется. Это связано с совершенствованием программного обеспечения для улучшения процесса устранения неполадок в различных сценариях. В каждой версии пакета 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);
}

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

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

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

Эти CosmosDiagnosticsThresholds и CosmosDiagnosticsHandler затем можно использовать в объекте 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 IO. Пакет SDK возвращает результат вызова API, касающийся одного из потоков Netty для ввода-вывода, в код приложения. Если приложение выполняет длительную операцию после получения результатов в потоке Netty, у пакета SDK может не хватать потоков ввода-вывода для выполнения своих внутренних операций ввода-вывода. Кодирование такого приложения может привести к низкой пропускной способности, длительной задержке и сбоям io.netty.handler.timeout.ReadTimeoutException. Обходное решение заключается в том, чтобы переключить поток, когда вы знаете, что операция займет некоторое время.

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

Пакет SDK для Java версии 4 (async API, 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 и переключаетесь на собственный поток, предоставляемый настраиваемым планировщиком. Это изменение позволяет решить проблему. Сбой io.netty.handler.timeout.ReadTimeoutException больше не будет возникать.

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

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

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

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

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

Обработка ошибок из пакета 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, это может привести к непредвиденным ошибкам при запуске приложения. Если вы отлаживаете, почему ваше приложение неожиданно вызывает исключение, рекомендуется дважды проверить, что дерево зависимостей не случайно извлекает более раннюю версию одной или нескольких зависимостей Java SDK для 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. Такое высокое значение указывает на то, что подключения устанавливаются и разрываются очень быстро. Подобная ситуация может привести к проблемам. Дополнительные сведения см. в разделе Распространенные проблемы и их обходные решения.

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

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

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