Bagikan melalui


Memecahkan masalah Azure Service Bus

Artikel ini membahas teknik investigasi kegagalan, konkurensi, kesalahan umum untuk jenis kredensial di pustaka klien Azure Service Bus Java, dan langkah-langkah mitigasi untuk mengatasi kesalahan ini.

Mengaktifkan dan mengonfigurasi pengelogan

Azure SDK for Java menawarkan cerita pengelogan yang konsisten untuk membantu memecahkan masalah kesalahan aplikasi dan untuk membantu mempercepat resolusinya. Log yang dihasilkan menangkap alur aplikasi sebelum mencapai status terminal untuk membantu menemukan masalah akar. Untuk panduan tentang pengelogan, lihat Mengonfigurasi pengelogan di Azure SDK for Java dan gambaran umum pemecahan masalah .

Selain mengaktifkan pengelogan, atur tingkat log ke VERBOSE atau DEBUG memberikan wawasan tentang status pustaka. Bagian berikut menunjukkan contoh konfigurasi log4j2 dan logback untuk mengurangi pesan yang berlebihan saat pengelogan verbose diaktifkan.

Mengonfigurasi Log4J 2

Gunakan langkah-langkah berikut untuk mengonfigurasi Log4J 2:

  1. Tambahkan dependensi di pom.xml Anda menggunakan yang dari sampel pengelogan pom.xml, di bagian "Dependensi yang diperlukan untuk Log4j2".
  2. Tambahkan log4j2.xml ke folder src/main/resources Anda.

Mengonfigurasi logback

Gunakan langkah-langkah berikut untuk mengonfigurasi logback:

  1. Tambahkan dependensi di pom.xml Anda menggunakan yang dari contoh pencatatan pom.xml, di bagian "Dependensi yang diperlukan untuk logback".
  2. Tambahkan logback.xml ke folder src/main/resources Anda.

Mengaktifkan pengelogan transportasi AMQP

Jika mengaktifkan log klien tidak cukup untuk mendiagnosis masalah Anda, Anda dapat mengaktifkan log ke file di pustaka dasar AMQP, Qpid Proton-J. Qpid Proton-J menggunakan java.util.logging. Anda dapat mengaktifkan pengelogan dengan membuat file konfigurasi dengan konten yang diperlihatkan di bagian berikutnya. Atau, atur proton.trace.level=ALL dan opsi konfigurasi apa pun yang Anda inginkan untuk implementasi java.util.logging.Handler. Untuk kelas implementasi dan opsinya, lihat Paket java.util.logging dalam dokumentasi Java 8 SDK.

Untuk melacak bingkai transportasi AMQP, atur variabel lingkungan PN_TRACE_FRM=1.

Contoh file logging.properties

File konfigurasi berikut mencatat output tingkat TRACE dari Proton-J ke file proton-trace.log:

handlers=java.util.logging.FileHandler
.level=OFF
proton.trace.level=ALL
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=proton-trace.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tr] %3$s %4$s: %5$s %n

Mengurangi pengelogan

Salah satu cara untuk mengurangi pengelogan adalah dengan mengubah verbositas. Cara lain adalah menambahkan filter yang mengecualikan log dari paket nama pencatat seperti com.azure.messaging.servicebus atau com.azure.core.amqp. Misalnya, lihat file XML di bagian Mengonfigurasi Log4J 2 dan Mengonfigurasi Logback.

Saat Anda mengirimkan bug, pesan log dari kelas dalam paket berikut adalah penting:

  • com.azure.core.amqp.implementation
  • com.azure.core.amqp.implementation.handler
    • Pengecualiannya adalah Anda dapat mengabaikan pesan onDelivery di ReceiveLinkHandler.
  • com.azure.messaging.servicebus.implementation

Konkurensi di ServiceBusProcessorClient

ServiceBusProcessorClient memungkinkan aplikasi untuk mengonfigurasi berapa banyak panggilan ke handler pesan yang harus terjadi secara bersamaan. Konfigurasi ini memungkinkan untuk memproses beberapa pesan secara paralel. Ketika ServiceBusProcessorClient mengonsumsi pesan dari entitas non-sesi, aplikasi dapat mengonfigurasi konkurensi yang diinginkan menggunakan API maxConcurrentCalls. Untuk entitas yang sesi diaktifkan, kebersamaan yang diinginkan adalah maxConcurrentSessions kali maxConcurrentCalls.

Jika aplikasi mengamati lebih sedikit panggilan bersamaan ke pengendali pesan daripada konkurensi yang dikonfigurasi, hal ini mungkin karena ukuran kumpulan utas tidak sesuai.

ServiceBusProcessorClient menggunakan utas daemon dari reaktor global boundedElastic thread pool untuk memanggil handler pesan. Jumlah maksimum utas bersamaan dalam kumpulan ini dibatasi oleh batasan. Secara default, batas ini sepuluh kali lipat dari jumlah inti CPU yang tersedia. Agar ServiceBusProcessorClient secara efektif mendukung konkurensi yang diinginkan aplikasi (maxConcurrentCalls atau maxConcurrentSessions kali maxConcurrentCalls), Anda harus memiliki nilai batas kumpulan boundedElastic yang lebih tinggi dari konkurensi yang diinginkan. Anda dapat mengambil alih batas default dengan mengatur properti sistem reactor.schedulers.defaultBoundedElasticSize.

Anda harus menyetel kumpulan utas dan alokasi CPU berdasarkan kasus per kasus. Namun, ketika Anda mengambil alih batas kumpulan, sebagai titik awal, batasi utas bersamaan menjadi sekitar 20-30 per inti CPU. Kami menyarankan agar Anda membatasi konkurensi yang diinginkan per instans ServiceBusProcessorClient hingga sekitar 20-30. Buat profil dan ukur kasus penggunaan spesifik Anda dan sesuaikan aspek konkurensi yang sesuai. Untuk skenario beban tinggi, pertimbangkan untuk menjalankan beberapa instans ServiceBusProcessorClient di mana setiap instans dibangun dari instans ServiceBusClientBuilder baru. Selain itu, pertimbangkan untuk menjalankan setiap ServiceBusProcessorClient di host khusus - seperti kontainer atau VM - sehingga waktu henti dalam satu host tidak memengaruhi pemrosesan pesan secara keseluruhan.

Perlu diingat bahwa menetapkan nilai tinggi untuk batas kumpulan pada host dengan sedikit inti CPU dapat memiliki efek buruk. Beberapa tanda sumber daya CPU rendah atau kumpulan dengan terlalu banyak utas pada CPU yang lebih sedikit adalah: batas waktu yang sering, kunci hilang, kebuntuan, atau throughput yang lebih rendah. Jika Anda menjalankan aplikasi Java pada kontainer, sebaiknya gunakan dua atau beberapa inti vCPU. Kami tidak menyarankan untuk memilih apa pun yang kurang dari 1 inti vCPU saat menjalankan aplikasi Java pada lingkungan kontainer. Untuk rekomendasi mendalam tentang resourcing, lihat Kontainerisasi aplikasi Java Anda.

Hambatan dalam berbagi koneksi

Semua klien yang dibuat dari instans ServiceBusClientBuilder bersama berbagi koneksi yang sama ke namespace Bus Layanan.

Menggunakan koneksi bersama memungkinkan operasi multipleks di antara klien pada satu koneksi, tetapi berbagi juga dapat menjadi hambatan jika ada banyak klien, atau klien bersama-sama menghasilkan beban tinggi. Setiap koneksi memiliki utas I/O yang terkait dengannya. Saat berbagi koneksi, klien menempatkan pekerjaan mereka ke dalam antrean kerja utas I/O bersama ini, dan kemajuan setiap klien bergantung pada penyelesaian tepat waktu dari pekerjaannya di dalam antrean tersebut. Utas I/O menangani pekerjaan yang diantrekan secara serial. Artinya, jika antrean kerja utas I/O dari koneksi bersama berakhir dengan banyak pekerjaan tertunda untuk ditangani, maka karakteristiknya mirip dengan kinerja CPU rendah. Kondisi ini dijelaskan di bagian sebelumnya tentang konkurensi - misalnya, klien mengulur waktu, batas waktu habis, kunci hilang, atau perlambatan dalam jalur pemulihan.

Service Bus SDK menggunakan pola penamaan reactor-executor-* untuk utas I/O koneksi. Ketika aplikasi mengalami penyempitan koneksi bersama, maka mungkin tercermin dalam penggunaan CPU utas I/O. Selain itu, dalam heap dump atau dalam memori langsung, objek ReactorDispatcher$workQueue adalah antrean kerja thread I/O. Antrean kerja yang panjang dalam cuplikan memori selama periode kemacetan mungkin menunjukkan bahwa utas I/O bersama kelebihan beban dengan pekerjaan tertunda.

Oleh karena itu, jika beban aplikasi ke endpoint Azure Service Bus cukup tinggi dalam hal jumlah keseluruhan pesan terkirim-terima atau ukuran payload, Anda harus menggunakan instans penyusun terpisah untuk setiap klien yang Anda buat. Misalnya, untuk setiap entitas - antrean atau topik - Anda dapat membuat ServiceBusClientBuilder baru dan mengembangkan klien dari ServiceBusClientBuilder tersebut. Dalam kasus beban yang sangat tinggi pada entitas tertentu, Anda mungkin ingin membuat beberapa instance klien untuk entitas tersebut atau menjalankan klien di beberapa host - misalnya, kontainer atau VM - untuk menyeimbangkan beban.

Klien berhenti saat menggunakan titik akhir kustom Application Gateway

Alamat titik akhir kustom merujuk pada alamat titik akhir HTTPS yang disediakan oleh aplikasi dan dapat diterjemahkan ke Service Bus atau dikonfigurasi untuk mengarahkan lalu lintas ke Service Bus. Azure Application Gateway memudahkan untuk membuat front-end HTTPS yang meneruskan lalu lintas ke Azure Service Bus. Anda dapat mengonfigurasi Service Bus SDK untuk aplikasi guna menggunakan alamat IP front-end Application Gateway sebagai titik akhir kustom untuk menyambungkan ke Service Bus.

Application Gateway menawarkan beberapa kebijakan keamanan yang mendukung versi protokol TLS yang berbeda. Ada kebijakan yang telah ditentukan sebelumnya yang memberlakukan TLSv1.2 sebagai versi minimum, ada juga kebijakan lama dengan TLSv1.0 sebagai versi minimum. Front-end HTTPS akan menerapkan kebijakan TLS.

Saat ini, Service Bus SDK tidak mengenali penghentian TCP jarak jauh tertentu oleh ujung depan Application Gateway, yang menggunakan TLSv1.0 sebagai versi minimum. Misalnya, jika front end mengirim paket TCP FIN, ACK untuk menutup koneksi ketika propertinya diperbarui, SDK tidak dapat mendeteksinya, sehingga tidak dapat tersambung kembali, dan klien tidak dapat mengirim atau menerima pesan lagi. Penghentian seperti itu hanya terjadi saat menggunakan TLSv1.0 sebagai versi minimum. Untuk mengurangi, gunakan kebijakan keamanan dengan TLSv1.2 atau lebih tinggi sebagai versi minimum untuk front-end Application Gateway.

Dukungan untuk TLSv1.0 dan 1.1 di semua Layanan Azure sudah diumumkan berakhir pada 31 Oktober 2024, jadi transisi ke TLSv1.2 sangat disarankan.

Pesan atau kunci sesi hilang

Antrean atau langganan topik pada Azure Service Bus memiliki lama kunci yang ditetapkan di tingkat sumber daya. Ketika klien penerima menarik pesan dari sumber daya, broker Service Bus menerapkan kunci awal pada pesan. Kunci awal berlangsung selama durasi kunci yang diatur di tingkat sumber daya. Jika kunci pesan tidak diperpanjang sebelum kedaluwarsa, pengelola Service Bus merilis pesan agar tersedia bagi penerima lain. Jika aplikasi mencoba menyelesaikan atau meninggalkan pesan setelah kedaluwarsa kunci, panggilan API gagal dengan kesalahan com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue.

Klien Service Bus mendukung menjalankan tugas perpanjangan kunci di latar belakang yang secara otomatis memperbarui kunci pesan setiap kali sebelum kedaluwarsa. Secara default, tugas perpanjangan kunci berjalan selama 5 menit. Anda dapat menyesuaikan durasi perpanjangan kunci dengan menggunakan ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration). Jika Anda meneruskan nilai Duration.ZERO, tugas perpanjangan kunci dinonaktifkan.

Daftar berikut menjelaskan beberapa pola penggunaan atau lingkungan host yang dapat menyebabkan kesalahan kunci hilang:

  • Tugas pembaruan kunci dinonaktifkan, sehingga waktu pemrosesan pesan aplikasi melebihi durasi kunci yang ditetapkan di tingkat sumber daya.

  • Waktu pemrosesan pesan aplikasi melebihi durasi tugas perpanjangan kunci yang dikonfigurasi. Perhatikan bahwa, jika durasi perpanjangan kunci tidak diatur secara eksplisit, itu default ke 5 menit.

  • Aplikasi telah mengaktifkan fitur Prefetch dengan mengatur nilai prefetch ke bilangan bulat positif menggunakan ServiceBusReceiverClientBuilder.prefetchCount(prefetch). Ketika fitur Prefetch diaktifkan, klien akan mengambil sejumlah pesan yang sesuai dengan nilai prefetch dari entitas Service Bus - antrean atau topik - dan menyimpannya dalam buffer prefetch di dalam memori. Pesan tetap berada di buffer prefetch hingga diterima ke dalam aplikasi. Klien tidak memperpanjang kunci pesan saat berada di buffer prefetch. Jika pemrosesan aplikasi memakan waktu terlalu lama sehingga kunci pesan kedaluwarsa saat masih berada di buffer prefetch, maka aplikasi bisa memperoleh pesan dengan kunci yang sudah kedaluwarsa. Untuk informasi selengkapnya, lihat Mengapa Prefetch bukan opsi default?

  • Lingkungan host memiliki masalah jaringan sesekali - misalnya, kegagalan atau pemadaman jaringan sementara - yang mencegah tugas pembaruan kunci memperbarui kunci tepat waktu.

  • Lingkungan host kekurangan CPU yang cukup atau sering mengalami kekurangan siklus CPU sehingga menunda tugas pembaruan kunci dari berjalan tepat waktu.

  • Waktu sistem host tidak akurat - misalnya, jam tidak sinkron - menunda tugas perpanjangan kunci dan membuatnya tidak berjalan tepat waktu.

  • Utas I/O koneksi kelebihan beban, mempengaruhi kemampuannya untuk melakukan panggilan pembaruan kunci pada jaringan tepat waktu. Dua skenario berikut dapat menyebabkan masalah ini:

    • Aplikasi ini menjalankan terlalu banyak klien penerima yang berbagi koneksi yang sama. Untuk informasi selengkapnya, lihat bagian batasan berbagi koneksi.
    • Aplikasi ini telah mengonfigurasi ServiceBusReceiverClient.receiveMessages atau ServiceBusProcessorClient untuk memiliki nilai maxMessages atau maxConcurrentCalls yang besar. Untuk informasi selengkapnya, lihat bagian Konkurensi di ServiceBusProcessorClient.
  • Pola aplikasi umum yang meningkatkan kemungkinan kesalahan kehilangan kunci melibatkan penjadwalan tugas pembaruan kunci jangka panjang - misalnya, tugas dengan durasi yang berlangsung beberapa jam. Seperti disebutkan sebelumnya, berbagai faktor di luar kontrol klien Bus Layanan dapat mengganggu perpanjangan kunci yang berhasil, sehingga desain aplikasi harus menghindari asumsi perpanjangan yang dijamin selama jangka waktu yang lama. Untuk menghindari harus memproses ulang operasi yang berjalan lama, pertimbangkan untuk memecah pekerjaan menjadi potongan yang lebih kecil atau menerapkan logika titik pemeriksaan idempotensi.

Jumlah tugas perpanjangan kunci di klien sama dengan nilai parameter maxMessages atau maxConcurrentCalls yang ditetapkan untuk ServiceBusProcessorClient atau ServiceBusReceiverClient.receiveMessages. Banyaknya tugas pembaruan kunci yang melakukan beberapa panggilan jaringan juga dapat berdampak buruk dalam pembatasan namespace Service Bus.

Jika host tidak cukup sumber daya, kunci masih dapat hilang meskipun hanya ada beberapa tugas pembaruan kunci yang berjalan. Jika Anda menjalankan aplikasi Java pada kontainer, sebaiknya gunakan dua atau beberapa inti vCPU. Kami tidak menyarankan untuk memilih apa pun yang kurang dari 1 inti vCPU saat menjalankan aplikasi Java pada lingkungan kontainer. Untuk rekomendasi mendalam tentang resourcing, lihat Kontainerisasi aplikasi Java Anda.

Komentar yang sama tentang kunci juga relevan untuk antrean Azure Service Bus atau langganan topik yang mengaktifkan sesi. Ketika klien penerima terhubung ke sesi di sumber daya, broker menerapkan kunci awal ke sesi. Untuk mempertahankan kunci pada sesi, tugas perpanjangan kunci di klien harus terus memperbarui kunci sesi sebelum kedaluwarsa. Untuk sumber daya yang mendukung sesi, partisi yang mendasarinya terkadang berpindah untuk mencapai penyeimbangan beban di seluruh node Service Bus - misalnya, ketika node baru ditambahkan untuk berbagi beban. Ketika itu terjadi, kunci sesi dapat hilang. Jika aplikasi mencoba menyelesaikan atau meninggalkan pesan setelah kunci sesi hilang, panggilan API gagal dengan kesalahan com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiver.

Langkah berikutnya

Jika panduan pemecahan masalah dalam artikel ini tidak membantu mengatasi masalah saat Anda menggunakan pustaka klien Azure SDK for Java, kami sarankan Anda mengajukan masalah di repositori Azure SDK for Java GitHub .