Memecahkan masalah saat Anda menggunakan Azure Cosmos DB Async Java SDK v2 dengan API untuk akun NoSQL

BERLAKU UNTUK: NoSQL

Penting

Ini bukan Java SDK terbaru untuk Azure Cosmos DB! Anda harus meningkatkan proyek ke Azure Cosmos DB Java SDK v4 lalu membaca panduan pemecahan masalah Azure Cosmos DB Java SDK v4. Ikuti petunjuk dalam panduan Migrasi ke Azure Cosmos DB Java SDK v4 dan peningkatan Reactor vs RxJava.

Artikel ini hanya membahas pemecahan masalah untuk Azure Cosmos DB Java SDK v2. Silakan lihat Catatan Rilis, repositori Maven dan tips performa Azure Cosmos DB Java SDK v2 untuk informasi selengkapnya.

Penting

Pada tanggal 31 Agustus 2024 Azure Cosmos DB Async Java SDK v2.x akan pensiun; SDK dan semua aplikasi yang menggunakan SDK akan tetap berfungsi; Azure Cosmos DB hanya akan berhenti memberikan pemeliharaan dan dukungan lebih lanjut untuk SDK ini. Sebaiknya ikuti petunjuk di atas untuk bermigrasi ke Azure Cosmos DB Java SDK v4.

Artikel ini membahas masalah umum, solusi, langkah diagnostik, dan alat saat Anda menggunakan Java Async SDK dengan Azure Cosmos DB untuk akun NoSQL. Java Async SDK menyediakan representasi logis sisi klien untuk mengakses Azure Cosmos DB untuk NoSQL. Artikel ini menjelaskan alat dan pendekatan untuk membantu Anda jika mengalami masalah.

Mulai dengan daftar ini:

Masalah umum dan solusi

Masalah jaringan, kegagalan waktu habis baca Netty, throughput rendah, latensi tinggi

Saran umum

  • Pastikan aplikasi berjalan di wilayah yang sama dengan akun Azure Cosmos DB Anda.
  • Periksa penggunaan CPU di host tempat aplikasi berjalan. Jika penggunaan CPU 90 persen atau lebih, jalankan aplikasi Anda di host dengan konfigurasi yang lebih tinggi. Atau Anda dapat mendistribusikan beban pada lebih banyak komputer.

Pembatasan koneksi

Pembatasan koneksi dapat terjadi karena batas koneksi pada komputer host atau kelelahan port Azure SNAT (PAT).

Batas koneksi pada komputer host

Beberapa sistem Linux, seperti `Red Hat, memiliki batas atas jumlah total file terbuka. Soket di Linux diimplementasikan sebagai file, sehingga angka ini membatasi jumlah total koneksi juga. Jalankan perintah berikut.

ulimit -a

Jumlah file terbuka maksimum yang diizinkan, yang diidentifikasi sebagai "nofile," harus setidaknya dua kali lipat ukuran kumpulan koneksi Anda. Untuk informasi selengkapnya, lihat Tips performa.

Kelelahan port Azure SNAT (PAT)

Jika aplikasi Anda diterapkan di Azure Virtual Machines tanpa alamat IP publik, secara default port Azure SNAT membangun koneksi ke titik akhir di luar VM Anda. Jumlah koneksi yang diizinkan dari VM ke titik akhir Azure Cosmos DB dibatasi oleh konfigurasi Azure SNAT.

Port Azure SNAT hanya digunakan saat VM Anda memiliki alamat IP pribadi dan proses dari VM mencoba menyambungkan ke alamat IP publik. Ada dua solusi untuk menghindari batasan Azure SNAT:

  • Menambahkan titik akhir layanan Azure Cosmos DB Anda ke subnet jaringan virtual Azure Virtual Machines Anda. Untuk informasi selengkapnya, lihat Titik akhir layanan Microsoft Azure Virtual Network.

    Ketika titik akhir layanan diaktifkan, permintaan tidak lagi dikirimkan dari IP publik ke Azure Cosmos DB. Sebagai gantinya, jaringan virtual dan identitas subnet dikirim. Perubahan ini dapat mengakibatkan firewall turun jika hanya IP publik yang diizinkan. Jika Anda menggunakan firewall, saat Anda mengaktifkan titik akhir layanan, tambahkan subnet ke firewall menggunakan Virtual Network ACL.

  • Tetapkan IP publik ke Azure VM Anda.

Tidak dapat menjangkau Layanan - firewall

ConnectTimeoutException menunjukkan bahwa SDK tidak dapat menjangkau layanan. Anda mungkin mengalami kegagalan yang mirip dengan yang berikut ini saat menggunakan mode langsung:

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]

Jika Anda memiliki firewall yang berjalan di komputer aplikasi Anda, buka rentang port 10.000 hingga 20.000 yang digunakan oleh mode langsung. Ikuti juga Batas koneksi pada komputer host.

Proksi HTTP

Jika Anda menggunakan proksi HTTP, pastikan proksi tersebut dapat mendukung jumlah koneksi yang dikonfigurasi di SDK ConnectionPolicy. Jika tidak, Anda akan menghadapi masalah koneksi.

Pola pengkodean tidak valid: Memblokir utas IO Netty

SDK menggunakan pustaka IO Netty untuk berkomunikasi dengan Azure Cosmos DB. SDK memiliki Async API dan menggunakan IO API non-blokir Netty. Fungsi IO SDK dilakukan pada utas IO Netty. Jumlah utas IO Netty dikonfigurasi agar sama dengan jumlah core CPU komputer aplikasi.

Utas IO Netty dimaksudkan untuk digunakan hanya untuk pekerjaan IO non-blokir Netty. SDK mengembalikan hasil pemanggilan API pada salah satu utas IO Netty ke kode aplikasi. Jika aplikasi melakukan operasi jangka panjang setelah menerima hasil pada utas Netty, SDK mungkin tidak memiliki cukup thread IO untuk melakukan pekerjaan IO internalnya. Pengodean aplikasi tersebut dapat mengakibatkan throughput rendah, latensi tinggi, dan kegagalan io.netty.handler.timeout.ReadTimeoutException. Solusinya adalah mengganti utas ketika Anda tahu operasi membutuhkan waktu.

Misalnya, lihat cuplikan kode berikut. Anda mungkin melakukan pekerjaan jangka panjang yang membutuhkan lebih dari beberapa milidetik utas Netty. Jika demikian, Anda akhirnya bisa masuk ke status di mana tidak ada thread IO Netty yang hadir untuk memproses pekerjaan IO. Akibatnya, Anda mendapatkan kegagalan ReadTimeoutException.

Asinkronkan Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

@Test
public void badCodeWithReadTimeoutException() throws Exception {
    int requestTimeoutInSeconds = 10;

    ConnectionPolicy policy = new ConnectionPolicy();
    policy.setRequestTimeoutInMillis(requestTimeoutInSeconds * 1000);

    AsyncDocumentClient testClient = new AsyncDocumentClient.Builder()
            .withServiceEndpoint(TestConfigurations.HOST)
            .withMasterKeyOrResourceToken(TestConfigurations.MASTER_KEY)
            .withConnectionPolicy(policy)
            .build();

    int numberOfCpuCores = Runtime.getRuntime().availableProcessors();
    int numberOfConcurrentWork = numberOfCpuCores + 1;
    CountDownLatch latch = new CountDownLatch(numberOfConcurrentWork);
    AtomicInteger failureCount = new AtomicInteger();

    for (int i = 0; i < numberOfConcurrentWork; i++) {
        Document docDefinition = getDocumentDefinition();
        Observable<ResourceResponse<Document>> createObservable = testClient
                .createDocument(getCollectionLink(), docDefinition, null, false);
        createObservable.subscribe(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) {
                    }
                },

                exception -> {
                    //It will be io.netty.handler.timeout.ReadTimeoutException.
                    exception.printStackTrace();
                    failureCount.incrementAndGet();
                    latch.countDown();
                },
                () -> {
                    latch.countDown();
                });
    }

    latch.await();
    assertThat(failureCount.get()).isGreaterThan(0);
}

Solusinya adalah mengubah utas tempat Anda melakukan pekerjaan yang membutuhkan waktu. Tentukan instans singleton penjadwal untuk aplikasi Anda.

Asinkronkan Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

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

Anda mungkin perlu melakukan pekerjaan yang membutuhkan waktu, misalnya, pekerjaan berat secara komputasi atau memblokir IO. Dalam hal ini, alihkan untuk ke pekerja yang disediakan oleh customScheduler Anda menggunakan .observeOn(customScheduler) API.

Asinkronkan Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)

Observable<ResourceResponse<Document>> createObservable = client
        .createDocument(getCollectionLink(), docDefinition, null, false);

createObservable
        .observeOn(customScheduler) // Switches the thread.
        .subscribe(
            // ...
        );

Dengan menggunakan observeOn(customScheduler), Anda merilis thread IO Netty dan beralih ke utas kustom Anda sendiri yang disediakan oleh penjadwal kustom. Modifikasi ini memecahkan masalah. Anda tidak akan mendapatkan kegagalan io.netty.handler.timeout.ReadTimeoutException lagi.

Masalah kehabisan kumpulan koneksi

PoolExhaustedException adalah kegagalan pada sisi klien. Kegagalan ini menunjukkan bahwa beban kerja aplikasi Anda lebih tinggi dari yang dapat dilayani oleh kumpulan koneksi SDK. Tingkatkan ukuran kumpulan koneksi atau distribusikan beban di beberapa aplikasi.

Rentang permintaan terlalu besar

Kegagalan ini adalah kegagalan sisi server. Ini menunjukkan bahwa Anda mengonsumsi throughput yang disediakan. Coba lagi nanti. Jika Anda sering mengalami kegagalan ini, pertimbangkan melakukan peningkatan throughput koleksi.

Kegagalan menyambungkan ke Azure Cosmos DB Emulator

Sertifikat HTTPS Azure Cosmos DB Emulator ditandatangani sendiri. Agar SDK dapat digunakan dengan emulator, impor sertifikat emulator ke Java TrustStore. Untuk informasi selengkapnya, lihat Mengekspor sertifikat Azure Cosmos DB Emulator.

Masalah Konflik Dependensi

Exception in thread "main" java.lang.NoSuchMethodError: rx.Observable.toSingle()Lrx/Single;

Pengecualian di atas menunjukkan Anda memiliki dependensi pada versi lama RxJava lib (misal, 1.2.2). SDK kami mengandalkan RxJava 1.3.8 dengan API yang tidak tersedia di versi RxJava lebih lama.

Solusi untuk masalah tersebut adalah mengidentifikasi dependensi lain mana yang membawa RxJava-1.2.2 dan mengecualikan dependensi transitif pada RxJava-1.2.2, serta mengizinkan CosmosDB SDK membawa versi yang lebih baru.

Untuk mengidentifikasi pustaka mana yang membawa RxJava-1.2.2 jalankan perintah berikut di samping file pom.xml Anda:

mvn dependency:tree

Untuk informasi selengkapnya, lihat panduan pohon ketergantungan maven.

Setelah mengidentifikasi bahwa RxJava-1.2.2 adalah dependensi transitif dari dependensi lain di dalam proyek, Anda dapat memodifikasi dependensi pada pustaka dalam file pom dan mengecualikan dependensi transitif RxJava:

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-rxjava1.2.2}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-rxjava1.2.2}</artifactId>
  <version>${version-of-lib-which-brings-in-rxjava1.2.2}</version>
  <exclusions>
    <exclusion>
      <groupId>io.reactivex</groupId>
      <artifactId>rxjava</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Untuk informasi selengkapnya, lihat panduan dependensi transitif yang dikecualikan.

Mengaktifkan pengelogan SDK klien

Azure Cosmos DB Java SDK menggunakan SLF4j sebagai muka pengelogan yang mendukung pengelogan ke kerangka kerja pengelogan populer seperti log4j dan logback.

Misalnya, jika Anda ingin menggunakan log4j sebagai kerangka kerja pengelogan, tambahkan libs berikut di classpath Java Anda.

<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>

Tambahkan juga konfigurasi log4j.

# this is a sample log4j configuration

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

log4j.category.com.microsoft.azure.cosmosdb=DEBUG
#log4j.category.io.netty=INFO
#log4j.category.io.reactivex=INFO
# 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

Untuk informasi selengkapnya, lihat manual pengelogan sfl4j.

Statistik jaringan OS

Jalankan perintah netstat untuk merasakan berapa banyak koneksi dalam status seperti ESTABLISHED dan CLOSE_WAIT.

Di Linux, Anda dapat menjalankan perintah berikut.

netstat -nap

Filter hasilnya hanya ke koneksi ke titik akhir Azure Cosmos DB.

Jumlah koneksi ke titik akhir Azure Cosmos DB dalam status ESTABLISHED tidak boleh lebih besar dari ukuran kumpulan koneksi yang telah dikonfigurasi.

Banyak koneksi ke titik akhir Azure Cosmos DB mungkin berada dalam status CLOSE_WAIT. Mungkin ada lebih dari 1.000. Angka yang tinggi menunjukkan bahwa koneksi dibuat dan dihentikan dengan cepat. Situasi ini berpotensi menimbulkan masalah. Untuk informasi selengkapnya, lihat bagian Masalah umum dan solusi.