Bagikan melalui


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

Penting

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

Artikel ini membahas pemecahan masalah untuk Azure Cosmos DB Async Java SDK v2 saja. Lihat Catatan Rilis Azure Cosmos DB Async Java SDK v2, repositori Maven , dan tips performa untuk informasi selengkapnya.

Penting

Pada 31 Agustus 2024, Azure Cosmos DB Async Java SDK v2.x akan dihentikan; SDK dan semua aplikasi yang menggunakan SDK akan terus 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 pembacaan batas waktu Netty, throughput rendah, latensi tinggi

Saran umum

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

Pembatasan laju 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 jumlah ini juga membatasi jumlah total koneksi. 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.

Kekurangan port Azure SNAT (PAT)

Jika aplikasi Anda disebarkan di Azure Virtual Machines tanpa alamat IP publik, secara default port Azure SNAT membuat koneksi ke titik akhir apa pun 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 privat 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 mengakses Layanan - firewall

ConnectTimeoutException menunjukkan bahwa SDK tidak dapat menjangkau layanan. Anda mungkin mendapatkan 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 penyandian tidak valid: Utas IO Netty terblokir

SDK menggunakan pustaka Netty IO untuk berkomunikasi dengan Azure Cosmos DB. SDK memiliki API Asinkron dan menggunakan API IO netty yang tidak diblokir. Pekerjaan IO SDK dilakukan pada utas IO Netty. Jumlah utas IO Netty dikonfigurasi agar sama dengan jumlah inti CPU komputer aplikasi.

Utas IO Netty dimaksudkan untuk digunakan hanya untuk pekerjaan IO Netty yang tidak diblokir. 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 utas IO untuk melakukan pekerjaan IO internalnya. Pengkodean aplikasi tersebut dapat mengakibatkan throughput rendah, latensi tinggi, dan io.netty.handler.timeout.ReadTimeoutException kegagalan. Solusi sementara adalah mengalihkan thread saat Anda tahu operasi membutuhkan waktu.

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

Async 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);
}

Solusi alternatif adalah dengan mengubah utas tempat Anda melakukan pekerjaan yang memerlukan waktu. Tentukan instans singleton penjadwal untuk aplikasi Anda.

Async 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 yang berat secara komputasi atau memblokir IO. Dalam hal ini, alihkan utas ke pekerja yang disediakan oleh customScheduler dengan menggunakan API .observeOn(customScheduler).

Async 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 utas IO Netty dan beralih ke utas kustom Anda sendiri yang disediakan oleh penjadwal kustom. Modifikasi ini memecahkan masalah. Anda tidak akan mengalami io.netty.handler.timeout.ReadTimeoutException kegagalan lagi.

Masalah habisnya kumpulan koneksi

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

Rentang permintaan terlalu besar

Kegagalan ini adalah kegagalan sisi server. Ini menunjukkan bahwa Anda menggunakan throughput yang dialokasikan. Coba lagi nanti. Jika Anda sering mendapatkan kegagalan ini, pertimbangkan untuk meningkatkan throughput pengumpulan.

Kegagalan menyambungkan ke Emulator Azure Cosmos DB

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

Masalah Konflik Dependensi

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

Pengecualian di atas menunjukkan bahwa Anda memiliki dependensi pada versi lib RxJava yang lebih lama (misalnya, 1.2.2). SDK kami bergantung pada RxJava 1.3.8 yang memiliki API yang tidak tersedia di versi RxJava sebelumnya.

Solusi untuk masalah tersebut adalah mengidentifikasi dependensi lain mana yang membawa RxJava-1.2.2 dan mengecualikan dependensi transitif pada RxJava-1.2.2, dan memungkinkan 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 proyek Anda:

mvn dependency:tree

Untuk informasi selengkapnya, lihat panduan pohon dependensi maven.

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

<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 pengecualian dependensi transitif.

Mengaktifkan pengelogan SDK klien

Java Async SDK menggunakan SLF4j sebagai fasad 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 hasil untuk hanya koneksi ke endpoint Azure Cosmos DB.

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

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