Lima masalah skalabilitas yang harus dihindari dengan aplikasi Kafka Anda

Seorang pengusaha berkacamata menggunakan laptop di kantor modern

Apache Kafka adalah platform streaming peristiwa dengan kinerja dan skalabilitas tinggi. Untuk membuka potensi penuh Kafka, Anda perlu mempertimbangkan desain aplikasi Anda dengan hati-hati. Sangat mudah untuk menulis aplikasi Kafka yang kinerjanya buruk atau pada akhirnya terbendung batasan skalabilitas. Sejak 2015, IBM telah menyediakan layanan IBM® Event Streams, yang merupakan layanan Apache Kafka yang terkelola sepenuhnya yang berjalan di IBM Cloud. Sejak itu, layanan ini telah membantu banyak pelanggan, dan juga berbagai tim di IBM, menyelesaikan masalah skalabilitas dan kinerja dengan aplikasi Kafka yang telah mereka tulis.

Artikel ini menjelaskan beberapa masalah umum Apache Kafka dan memberikan beberapa rekomendasi tentang cara menghindari masalah skalabilitas untuk aplikasi Anda.

1. Minimalkan waktu tunggu perjalanan bolak-balik jaringan

Operasi Kafka tertentu berjalan dengan klien mengirim data ke broker dan menunggu respons. Seluruh perjalanan bolak-balik mungkin memakan waktu 10 milidetik, yang terdengar cepat, tetapi membatasi Anda pada paling banyak 100 operasi per detik. Untuk alasan ini, disarankan agar Anda mencoba menghindari operasi semacam ini jika mungkin. Untungnya, klien Kafka menyediakan cara bagi Anda untuk menghindari menunggu waktu bolak-balik ini. Anda hanya perlu memastikan untuk memanfaatkan kemampuan ini.

Tips untuk memaksimalkan throughput:

  1. Jangan periksa setiap pesan yang dikirim jika berhasil. API Kafka memungkinkan Anda memisahkan pengiriman pesan dari pemeriksaan apakah pesan berhasil diterima oleh broker. Menunggu konfirmasi bahwa pesan diterima dapat memperkenalkan latensi bolak-balik jaringan ke dalam aplikasi Anda, jadi usahakan untuk meminimalkan ini jika mungkin. Ini bisa berarti mengirim pesan sebanyak mungkin, sebelum memeriksa konfirmasi bahwa semuanya diterima. Atau itu bisa berarti mendelegasikan pemeriksaan untuk pengiriman pesan yang berhasil ke utas eksekusi lain dalam aplikasi, sehingga dapat berjalan secara paralel saat Anda mengirimkan lebih banyak pesan.
  2. Jangan ikuti pemrosesan setiap pesan dengan commit offset. Melakukan commit offset (secara sinkron) dijalankan sebagai perjalanan bolak-balik jaringan dengan server. Baik lakukan commit offset lebih jarang, atau gunakan fungsi commit offset asinkron untuk menghindari konsekuensi perjalanan bolak-balik ini untuk setiap pesan yang Anda proses. Perhatikan bahwa melakukan commit offset lebih jarang dapat berarti bahwa lebih banyak data perlu diproses ulang jika aplikasi Anda gagal.

Jika Anda membaca di atas dan berpikir, “Aduh, bukankah itu akan membuat aplikasi saya lebih kompleks?”, jawabannya adalah ya, itu mungkin akan terjadi. Ada yang harus dikorbankan antara throughput dan kompleksitas aplikasi. Apa yang membuat waktu bolak-balik jaringan menjadi jebakan yang sangat berbahaya adalah bahwa begitu Anda mencapai batas ini, itu dapat memerlukan perubahan aplikasi yang ekstensif untuk mencapai peningkatan throughput lebih lanjut.

2. Jangan biarkan waktu pemrosesan yang meningkat disalahartikan sebagai kegagalan konsumen

Salah satu fitur bermanfaat dari Kafka adalah memonitor “keaktifan” aplikasi yang dikonsumsi dan memutuskan koneksi apa pun yang mungkin gagal. Ini bekerja dengan membuat broker melacak kapan setiap klien konsumen terakhir memanggil “polling” (terminologi Kafka untuk meminta lebih banyak pesan). Jika klien tidak melakukan polling cukup sering, broker yang terhubung dengannya menyimpulkan bahwa koneksi pasti gagal dan memutusnya. Ini dirancang untuk memungkinkan klien yang tidak mengalami masalah untuk masuk dan mengambil alih tugas dari klien yang gagal.

Sayangnya, dengan skema ini broker Kafka tidak dapat membedakan antara klien yang membutuhkan waktu lama untuk memproses pesan yang diterimanya dan klien yang benar-benar gagal. Pertimbangkan sebuah aplikasi konsumsi yang mengulang proses: 1) Memanggil polling dan mendapatkan kembali sekumpulan pesan; atau 2) memproses setiap pesan dalam sekumpulan pesan, membutuhkan waktu 1 detik untuk memproses setiap pesan.

Jika konsumen ini menerima batch 10 pesan, maka akan memerlukan waktu sekitar 10 detik antara panggilan polling. Secara default, Kafka akan mengizinkan hingga 300 detik (5 menit) antar polling sebelum memutuskan koneksi klien, jadi semuanya akan berfungsi dengan baik dalam skenario ini. Tetapi apa yang terjadi pada hari yang sangat sibuk ketika tumpukan pesan mulai menumpuk pada topik sedang dikonsumsi aplikasi? Daripada hanya mendapatkan 10 pesan kembali dari setiap panggilan jajak pendapat, aplikasi Anda mendapatkan 500 pesan (secara default ini adalah jumlah maksimum catatan yang dapat dikembalikan oleh panggilan ke jajak pendapat). Itu akan menghasilkan waktu pemrosesan yang cukup bagi Kafka untuk memutuskan instance aplikasi telah gagal dan memutusnya. Ini berita buruk.

Anda akan senang untuk mengetahui bahwa itu bisa jadi lebih buruk. Ada kemungkinan semacam loop masukan terjadi. Ketika Kafka mulai memutuskan koneksi klien karena mereka tidak memanggil polling cukup sering, akan ada lebih sedikit instance aplikasi yang tersedia untuk memproses pesan. Kemungkinan adanya backlog besar pesan pada topik meningkat, yang mengarah pada meningkatnya kemungkinan bahwa lebih banyak klien akan mendapatkan sejumlah besar pesan dan membutuhkan waktu terlalu lama untuk memprosesnya. Akhirnya semua instance aplikasi yang mengonsumsi aplikasi masuk ke loop mulai ulang, dan tidak ada pekerjaan yang selesai.

Langkah apa yang dapat Anda ambil untuk menghindari hal ini?

  1. Jumlah waktu maksimum antara panggilan polling dapat dikonfigurasi menggunakan konsumen Kafka konfigurasi “max.poll.interval.ms”. Jumlah maksimum pesan yang dapat dikembalikan oleh suatu polling juga dapat dikonfigurasi menggunakan konfigurasi “max.poll.records”. Sebagai aturan praktis, upayakan untuk mengurangi “max.poll.records” dalam preferensi untuk meningkatkan “max.poll.interval.ms” karena menetapkan interval polling maksimum yang besar akan membuat Kafka membutuhkan waktu lebih lama untuk mengidentifikasi konsumen yang benar-benar gagal.
  2. Konsumen Kafka juga dapat diinstruksikan untuk menjeda dan melanjutkan aliran pesan. Menjeda konsumsi mencegah metode polling mengembalikan pesan apa pun, tetapi tetap mengatur ulang timer yang digunakan untuk menentukan apakah klien gagal. Menjeda dan melanjutkan adalah taktik yang berguna jika Anda: a) memperkirakan bahwa setiap pesan mungkin membutuhkan waktu lama untuk diproses; dan b) ingin Kafka dapat mendeteksi kegagalan klien sdi tengah proses sebuah pesan.
  3. Jangan abaikan kegunaan metrik klien Kafka. Topik metrik dapat mengisi seluruh artikel dengan sendirinya, tetapi dalam konteks ini konsumen memaparkan metrik untuk waktu rata-rata dan maksimum antar polling. Memantau metrik ini dapat membantu mengidentifikasi situasi di mana sistem hilir adalah alasan bahwa setiap pesan yang diterima dari Kafka membutuhkan waktu lebih lama dari yang diharapkan untuk diproses.

Kita akan kembali ke topik kegagalan konsumen nanti dalam artikel ini, ketika kita melihat bagaimana mereka dapat memicu penyeimbangan ulang kelompok konsumen dan efek negatif yang dapat ditimbulkannya.

3. Minimalkan biaya konsumen yang menganggur

Di balik proses, protokol yang digunakan oleh konsumen Kafka untuk menerima pesan bekerja dengan mengirimkan permintaan “ambil” ke broker Kafka. Sebagai bagian dari permintaan ini, klien menunjukkan apa yang harus dilakukan broker jika tidak ada pesan untuk dikembalikan, termasuk berapa lama broker harus menunggu sebelum mengirim respons kosong. Secara default, konsumen Kafka menginstruksikan broker untuk menunggu hingga 500 milidetik (diatur oleh konfigurasi konsumen “fetch.max.wait.ms”) agar setidaknya 1 byte data pesan tersedia (diatur oleh konfigurasi “fetch.min.bytes”).

Menunggu 500 milidetik terdengar wajar, tetapi jika aplikasi Anda memiliki konsumen yang sebagian besar menganggur, dan skalanya hingga 5.000 instance, itu berpotensi menjadi 2.500 permintaan per detik untuk tidak melakukan apa-apa. Masing-masing permintaan ini membutuhkan waktu CPU pada broker untuk memproses, dan secara ekstrem dapat memengaruhi kinerja dan stabilitas klien Kafka yang ingin melakukan pekerjaan yang bermanfaat.

Biasanya pendekatan Kafka untuk penskalaan adalah menambahkan lebih banyak broker, dan kemudian menyeimbangkan kembali partisi topik secara merata di semua broker, baik lama maupun baru. Sayangnya, pendekatan ini mungkin tidak membantu jika klien Anda membombardir Kafka dengan permintaan fetch yang tidak perlu. Setiap klien akan mengirim permintaan fetch ke setiap broker yang memimpin partisi topik tempat klien mengonsumsi pesan. Jadi ada kemungkinan bahwa bahkan setelah menskalakan klaster Kafka, dan mendistribusikan ulang partisi, sebagian besar klien Anda akan mengirimkan permintaan fetch ke sebagian besar broker.

Jadi, apa yang bisa Anda lakukan?

  1. Mengubah konfigurasi konsumen Kafka dapat membantu mengurangi efek ini. Jika Anda ingin menerima pesan segera setelah mereka tiba, “fetch.min.bytes” harus tetap pada defaultnya 1; namun, konfigurasi “fetch.max.wait.ms” dapat ditingkatkan ke nilai yang lebih besar dan melakukannya akan mengurangi jumlah permintaan yang dibuat oleh konsumen yang menganggur.
  2. Pada cakupan yang lebih luas, apakah aplikasi Anda perlu memiliki ribuan instance, yang masing-masing jarang dikonsumsi dari Kafka? Mungkin ada alasan yang sangat baik mengapa demikian, tetapi mungkin ada cara untuk merancangnya agar penggunaan Kafka menjadi lebih efisien. Kami akan membahas beberapa pertimbangan ini di bagian berikutnya.

4. Pilih jumlah topik dan partisi yang sesuai

Jika Anda datang ke Kafka dengan pengalaman sebelumnya dengan sistem publish-subscribe lainnya (seperti Message Queuing Telemetry Transport (MQTT)), maka Anda mungkin mengira topik Kafka sangat ringan, hampir bersifat sementara. Nyatanya tidak. Kafka jauh lebih nyaman dengan sejumlah topik yang diukur dalam jumlah ribuan. Topik Kafka juga diharapkan berumur relatif lama. Praktik seperti membuat topik untuk menerima pesan balasan tunggal, kemudian menghapus topik, jarang terjadi di Kafka dan tidak sesuai dengan sifat-sifat unggul Kafka.

Sebaliknya, rencanakan topik yang berumur panjang. Mungkin topik berumur sama dengan masa pakai aplikasi atau aktivitas. Selain itu, usahakan untuk membatasi jumlah topik dalam jumlah ratusan atau mungkin jumlah ribuan yang rendah. Ini mungkin memerlukan perspektif yang berbeda tentang pesan apa yang disisipkan pada topik tertentu.

Pertanyaan terkait yang sering muncul adalah, “Berapa banyak partisi yang harus dimiliki topik saya?” Secara tradisional, sarannya adalah lebihkan estimasi, karena menambahkan partisi setelah topik dibuat tidak mengubah partisi data yang sudah ada di topik tersebut (dan karenanya dapat memengaruhi konsumen yang mengandalkan partisi untuk menjaga urutan pesan di dalam partisi). Ini adalah saran yang bagus; tapi kami ingin menyarankan beberapa pertimbangan tambahan:

  1. Untuk topik yang diperkirakan memiliki throughput yang diukur dalam MB/detik, atau di mana throughput dapat tumbuh seiring peningkatan skala aplikasi, kami sangat menyarankan memiliki lebih dari satu partisi, sehingga beban dapat didistribusikan ke beberapa broker. Layanan Event Streams selalu menjalankan Kafka dengan kelipatan 3 broker. Pada saat artikel ini ditulis, jumlah maksimumnya adalah 9 broker, tetapi mungkin ini akan meningkat di masa depan. Jika Anda memilih kelipatan 3 untuk jumlah partisi dalam topik Anda maka itu dapat dibagi secara merata ke semua broker.
  2. Jumlah partisi dalam suatu topik adalah batas berapa banyak konsumen Kafka yang dapat berbagi pesan konsumsi dari topik dengan grup konsumen Kafka (lebih lanjut tentang ini nanti). Jika Anda menambahkan lebih banyak konsumen ke grup konsumen daripada partisi dalam topik, beberapa konsumen akan menganggur tanpa mengonsumsi data pesan.
  3. Tidak ada yang salah dengan memiliki topik partisi tunggal selama Anda benar-benar yakin mereka tidak akan pernah menerima lalu lintas pesan yang signifikan, atau Anda tidak akan mengandalkan pemesanan dalam topik dan dengan senang hati menambahkan lebih banyak partisi nanti.

5. Penyeimbangan ulang grup konsumen bisa sangat mengganggu

Sebagian besar aplikasi Kafka yang mengonsumsi pesan memanfaatkan kemampuan grup konsumen Kafka untuk mengoordinasikan klien mana yang mengonsumsi dari partisi topik mana. Jika ingatan Anda tentang grup konsumen sedikit kabur, berikut adalah penyegaran singkat tentang poin-poin utamanya:

  • Grup konsumen mengoordinasikan sekelompok klien Kafka sedemikian rupa sehingga hanya satu klien yang menerima pesan dari partisi topik tertentu pada waktu tertentu. Ini berguna jika Anda perlu membagikan pesan tentang topik di antara sejumlah contoh aplikasi.
  • Ketika klien Kafka bergabung dengan grup konsumen atau meninggalkan grup konsumen sebelumnya, grup konsumen tersebut diseimbangkan ulang. Umumnya, klien bergabung dengan grup konsumen ketika aplikasi yang menjadi bagiannya dimulai, dan keluar karena aplikasi dimatikan, dimulai ulang, atau macet.
  • Ketika grup diseimbangkan ulang, partisi topik didistribusikan kembali di antara anggota grup. Jadi misalnya, jika klien bergabung dengan grup, beberapa klien yang sudah ada di grup mungkin memiliki partisi topik yang diambil dari mereka (atau “dicabut” dalam terminologi Kafka) untuk diberikan kepada klien yang baru bergabung. Kebalikannya juga benar: ketika klien meninggalkan grup, partisi topik yang ditetapkan untuknya didistribusikan kembali di antara anggota yang tersisa.

Seiring semakin matangnya Kafka, algoritma penyeimbangan ulang yang semakin canggih telah (dan terus) dirancang. Dalam versi awal Kafka, ketika grup konsumen diseimbangkan ulang, semua klien dalam grup harus berhenti mengonsumsi, partisi topik akan didistribusikan kembali di antara anggota baru grup, dan semua klien akan mulai mengonsumsi lagi. Pendekatan ini memiliki dua kelemahan (jangan khawatir, ini telah ditingkatkan):

  1. Semua klien dalam grup berhenti mengonsumsi pesan saat penyeimbangan ulang terjadi. Ini memiliki dampak yang jelas pada throughput.
  2. Klien Kafka biasanya mencoba menjaga buffer pesan yang belum dikirim ke aplikasi, dan mengambil lebih banyak pesan dari broker sebelum buffer tersebut habis. Tujuannya adalah untuk mencegah pengiriman pesan ke aplikasi macet sementara lebih banyak pesan diambil dari broker Kafka (ya, seperti sebelumnya dalam artikel ini, klien Kafka juga berusaha menghindari waktu tunggu dalam perjalanan bolak-balik jaringan). Sayangnya, ketika penyeimbangan ulang menyebabkan partisi dicabut dari klien maka data buffer untuk partisi harus dibuang. Demikian juga, ketika penyeimbangan ulang menyebabkan partisi baru ditetapkan ke klien, klien akan mulai mem-buffer data mulai dari offset terakhir yang dikomit untuk partisi, yang berpotensi menyebabkan lonjakan throughput jaringan dari broker ke klien. Hal ini disebabkan oleh klien yang partisinya baru ditugaskan membaca ulang data pesan yang sebelumnya telah di-buffer oleh klien dari mana partisi dicabut.

Algoritma penyeimbangan ulang yang lebih baru memiliki peningkatan signifikan dengan, menggunakan terminologi Kafka, menambahkan “kelengketan” dan “kooperasi”:

  • Algoritma yang “lengket” mencoba memastikan bahwa setelah penyeimbangan ulang, sebanyak mungkin anggota grup mempertahankan partisi yang sama seperti sebelum penyeimbangan ulang. Ini meminimalkan jumlah data pesan buffer yang dibuang atau dibaca kembali dari Kafka saat penyeimbangan ulang terjadi.
  • Algoritma “kooperatif” memungkinkan klien untuk terus mengonsumsi pesan sementara penyeimbangan ulang terjadi. Ketika klien memiliki partisi yang ditetapkan sebelum penyeimbangan ulang dan menyimpan partisi setelah keseimbangan ulang terjadi, klien dapat terus mengonsumsi partisi yang tidak terganggu oleh penyeimbangan ulang. Hal ini sinergis dengan “kelengketan”, yang berfungsi untuk menjaga partisi tetap ditugaskan ke klien yang sama.

Meskipun adanya peningkatan pada algoritma penyeimbangan ulang yang lebih baru ini, jika aplikasi Anda sering melakukan penyeimbangan ulang grup konsumen, Anda masih akan melihat dampak pada throughput pesan secara keseluruhan dan membuang-buang bandwidth jaringan saat klien membuang dan mengambil kembali data pesan buffer. Berikut adalah beberapa saran tentang apa yang dapat Anda lakukan:

  1. Pastikan Anda dapat mendeteksi saat penyeimbangan ulang terjadi. Dalam skala besar, mengumpulkan dan memvisualisasikan metrik adalah pilihan terbaik Anda. Ini adalah situasi di mana luasnya sumber metrik membantu membangun gambaran lengkap. Broker Kafka memiliki metrik untuk jumlah byte data yang dikirim ke klien, dan juga jumlah penyeimbangan ulang grup konsumen. Jika Anda mengumpulkan metrik dari aplikasi Anda, atau waktu proses, yang menunjukkan kapan restart terjadi, menghubungkannya dengan metrik broker dapat memberikan konfirmasi lebih lanjut bahwa penyeimbangan ulang menimbulkan masalah bagi Anda.
  2. Hindari restart aplikasi yang tidak perlu ketika, misalnya, aplikasi crash. Jika Anda mengalami masalah stabilitas dengan aplikasi Anda maka ini dapat menyebabkan penyeimbangan ulang yang jauh lebih sering daripada yang diantisipasi. Mencari log aplikasi untuk pesan kesalahan umum yang ditampilkan oleh aplikasi yang crash, misalnya jejak tumpukan, dapat membantu mengidentifikasi seberapa sering masalah terjadi dan memberikan informasi yang berguna untuk men-debug masalah yang mendasarinya.
  3. Apakah Anda menggunakan algoritma penyeimbangan ulang terbaik untuk aplikasi Anda? Pada saat artikel ini ditulis, standar emasnya adalah “CooperativeStickyAssignor”; namun, defaultnya (pada Kafka 3.0) adalah menggunakan “RangeAssignor” (dan algoritma penugasan sebelumnya) dibandingkan cooperative sticky assignor. Dokumentasi Kafka menjelaskan langkah-langkah migrasi yang diperlukan klien Anda untuk menggunakan cooperative sticky assignor. Perlu juga dicatat bahwa meskipun cooperative sticky assignor adalah pilihan serba bisa yang baik, ada assignor lain yang disesuaikan dengan contoh penggunaan.
  4. Apakah anggota untuk grup konsumen tetap? Misalnya, mungkin Anda selalu menjalankan 4 instance aplikasi yang berbeda dan sangat tersedia. Anda mungkin dapat memanfaatkan fitur keanggotaan grup statis Kafka. Dengan menetapkan ID unik ke setiap instance aplikasi Anda, keanggotaan grup statis memungkinkan Anda untuk menghindari penyeimbangaan ulang sepenuhnya.
  5. Lakukan commit offset saat ini ketika partisi dicabut dari instance aplikasi Anda. Klien konsumen Kafka menyediakan pendengar (listener) untuk peristiwa penyeimbangan ulang. Jika instance aplikasi Anda memiliki partisi yang akan dicabut, pendengar memberikan kesempatan untuk melakukan commit offset untuk partisi yang akan dihapus. Keuntungan melakukan commit offset pada saat partisi dicabut adalah memastikan anggota grup mana pun yang ditugaskan partisi melanjutkan dari titik ini, daripada berpotensi memproses ulang beberapa pesan dari partisi.

Apa Berikutnya?

Sekarang Anda adalah pakar dalam menskalakan aplikasi Kafka. Kami mengundang Anda untuk mempraktikkan poin-poin penting ini dan mencoba layanan Kafka yang terkelola sepenuhnya di IBM Cloud. Untuk masalah apa pun selama penyiapan, lihat Panduan Memulai dan Pertanyaan Umum.

 
Solusi terkait
IBM Event Streams

IBM® Event Streams adalah perangkat lunak streaming peristiwa yang dibangun pada Apache Kafka sumber terbuka. Perangkat lunak ini tersedia sebagai layanan yang dikelola sepenuhnya di IBM® Cloud atau untuk hosting mandiri.

Jelajahi Event Streams
Perangkat Lunak dan Solusi Integrasi

Buka potensi bisnis dengan solusi integrasi IBM®, menghubungkan aplikasi dan sistem untuk mengakses data penting dengan cepat dan aman.

Jelajahi solusi integrasi
Layanan konsultasi cloud

Buka kemampuan baru dan pendorong ketangkasan bisnis dengan layanan konsultasi IBM cloud.

Jelajahi layanan konsultasi cloud
Ambil langkah selanjutnya

IBM® Event Streams adalah perangkat lunak streaming peristiwa yang dibangun pada Apache Kafka sumber terbuka. Perangkat lunak ini tersedia sebagai layanan yang dikelola sepenuhnya di IBM® Cloud atau untuk hosting mandiri.

  1. Jelajahi Event Streams
  2. Dapatkan informasi lebih lanjut