Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka

Kelanjutan dari terjemahan buku kecil:
Memahami Pesan Broker
penulis: Jakub Korab, penerbit: O'Reilly Media, Inc., tanggal terbit: Juni 2017, ISBN: 9781492049296.

Bagian terjemahan sebelumnya: Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 1 PENDAHULUAN

BAB 3

Kafka

Kafka dikembangkan di LinkedIn untuk mengatasi beberapa keterbatasan broker pesan tradisional dan menghindari keharusan menyiapkan beberapa broker pesan untuk interaksi point-to-point yang berbeda, yang dijelaskan dalam buku ini di bagian "Scaling up and out" di halaman 28 Kasus penggunaan LinkedIn sebagian besar bergantung pada penyerapan data dalam jumlah sangat besar secara satu arah, seperti klik halaman dan log akses, sambil tetap mengizinkan data tersebut digunakan oleh beberapa sistem tanpa memengaruhi produktivitas produsen atau konsumen lainnya. Faktanya, alasan keberadaan Kafka adalah untuk mendapatkan jenis arsitektur perpesanan yang dijelaskan oleh Universal Data Pipeline.

Mengingat tujuan akhir ini, persyaratan lain secara alami muncul. Kafka harus:

  • Menjadi sangat cepat
  • Berikan lebih banyak bandwidth saat bekerja dengan pesan
  • Mendukung model Publisher-Subscriber dan Point-to-Point
  • Jangan memperlambat dengan menambahkan konsumen. Misalnya, kinerja antrean dan topik di ActiveMQ menurun seiring bertambahnya jumlah konsumen di tujuan.
  • Dapat diskalakan secara horizontal; jika satu broker yang menyimpan pesan hanya dapat melakukannya pada kecepatan disk maksimum, maka masuk akal untuk melampaui satu instance broker untuk meningkatkan kinerja
  • Batasi akses untuk menyimpan dan mengambil kembali pesan

Untuk mencapai semua ini, Kafka mengadopsi arsitektur yang mendefinisikan kembali peran dan tanggung jawab klien dan broker perpesanan. Model JMS sangat berorientasi pada broker, dimana broker bertanggung jawab untuk mendistribusikan pesan dan klien hanya perlu khawatir tentang mengirim dan menerima pesan. Kafka, di sisi lain, berpusat pada klien, dengan klien mengambil banyak fitur broker tradisional, seperti distribusi yang adil dari pesan yang relevan kepada konsumen, dengan imbalan broker yang sangat cepat dan dapat diskalakan. Bagi orang yang telah bekerja dengan sistem perpesanan tradisional, bekerja dengan Kafka membutuhkan perubahan pikiran yang mendasar.
Arah teknik ini telah mengarah pada penciptaan infrastruktur perpesanan yang mampu meningkatkan throughput dengan banyak urutan besarnya dibandingkan dengan broker konvensional. Seperti yang akan kita lihat, pendekatan ini disertai dengan kompromi, yang berarti Kafka tidak cocok untuk jenis beban kerja tertentu dan perangkat lunak yang diinstal.

Model Tujuan Terpadu

Untuk memenuhi persyaratan yang dijelaskan di atas, Kafka telah mengkombinasikan publish-subscribe dan pesan point-to-point di bawah satu jenis tujuan βˆ’ tema. Ini membingungkan bagi orang-orang yang telah bekerja dengan sistem perpesanan, di mana kata "topik" mengacu pada mekanisme penyiaran yang darinya (dari topik) pembacaan tidak dapat dipertahankan. Topik Kafka harus dianggap sebagai jenis tujuan campuran, sebagaimana didefinisikan dalam pengantar buku ini.

Untuk sisa bab ini, kecuali kami secara eksplisit menyatakan sebaliknya, istilah "topik" akan mengacu pada topik Kafka.

Untuk memahami sepenuhnya bagaimana perilaku topik dan jaminan apa yang mereka berikan, pertama-tama kita perlu melihat bagaimana penerapannya di Kafka.
Setiap topik di Kafka memiliki catatannya sendiri.
Produsen yang mengirim pesan ke Kafka menulis ke log ini, dan konsumen membaca dari log menggunakan penunjuk yang terus bergerak maju. Secara berkala, Kafka menghapus bagian terlama dari log, baik pesan di bagian tersebut sudah dibaca atau belum. Bagian utama dari desain Kafka adalah bahwa broker tidak peduli apakah pesan dibaca atau tidak - itu adalah tanggung jawab klien.

Istilah "log" dan "pointer" tidak muncul Dokumentasi Kafka. Istilah-istilah terkenal ini digunakan di sini untuk membantu pemahaman.

Model ini benar-benar berbeda dari ActiveMQ, di mana pesan dari semua antrian disimpan dalam log yang sama, dan broker menandai pesan sebagai dihapus setelah dibaca.
Sekarang mari kita gali lebih dalam dan lihat log topik lebih detail.
Log Kafka terdiri dari beberapa partisi (Gambar-3 1). Kafka menjamin pemesanan yang ketat di setiap partisi. Artinya, pesan yang ditulis ke partisi dalam urutan tertentu akan dibaca dalam urutan yang sama. Setiap partisi diimplementasikan sebagai file log bergulir yang berisi himpunan bagian (subset) dari semua pesan yang dikirim ke topik oleh produsernya. Topik yang dibuat berisi, secara default, satu partisi. Ide partisi adalah ide sentral Kafka untuk penskalaan horizontal.

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-1. Partisi Kafka

Saat produser mengirim pesan ke topik Kafka, ia memutuskan partisi mana yang akan dikirimi pesan. Kami akan melihat ini lebih detail nanti.

Membaca pesan

Klien yang ingin membaca pesan mengelola pointer bernama yang dipanggil kelompok konsumen, yang menunjuk ke mengimbangi pesan di partisi. Offset adalah posisi inkremental yang dimulai pada 0 di awal partisi. Grup konsumen ini, yang direferensikan dalam API melalui group_id yang ditentukan pengguna, sesuai dengan satu konsumen logis atau sistem.

Sebagian besar sistem perpesanan membaca data dari tujuan menggunakan beberapa instance dan utas untuk memproses pesan secara paralel. Dengan demikian, biasanya akan ada banyak instansi konsumen yang berbagi kelompok konsumen yang sama.

Masalah membaca dapat direpresentasikan sebagai berikut:

  • Topik memiliki beberapa partisi
  • Beberapa kelompok konsumen dapat menggunakan topik secara bersamaan
  • Sekelompok konsumen dapat memiliki beberapa contoh terpisah

Ini adalah masalah banyak-ke-banyak yang tidak sepele. Untuk memahami bagaimana Kafka menangani hubungan antara grup konsumen, instans konsumen, dan partisi, mari kita lihat serangkaian skenario pembacaan yang semakin kompleks.

Konsumen dan kelompok konsumen

Mari kita ambil topik dengan satu partisi sebagai titik awal (Gambar-3 2).

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-2. Konsumen membaca dari partisi

Saat instance konsumen terhubung dengan group_id miliknya sendiri ke topik ini, ia diberi partisi baca dan offset di partisi tersebut. Posisi offset ini dikonfigurasi di klien sebagai penunjuk ke posisi terbaru (pesan terbaru) atau posisi paling awal (pesan terlama). Konsumen meminta (polling) pesan dari topik, yang menyebabkannya dibaca secara berurutan dari log.
Posisi offset secara teratur dikembalikan ke Kafka dan disimpan sebagai pesan dalam topik internal _konsumen_offset. Pesan yang telah dibaca masih belum dihapus, tidak seperti broker biasa, dan klien dapat memundurkan offset untuk memproses ulang pesan yang sudah dilihat.

Saat konsumen logis kedua terhubung menggunakan group_id yang berbeda, ia mengelola penunjuk kedua yang tidak bergantung pada yang pertama (Gambar-3 3). Dengan demikian, topik Kafka bertindak seperti antrian di mana ada satu konsumen dan seperti topik publish-subscribe (pub-sub) normal yang dilanggan oleh banyak konsumen, dengan manfaat tambahan bahwa semua pesan disimpan dan dapat diproses berkali-kali.

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-3. Dua konsumen dalam kelompok konsumen yang berbeda membaca dari partisi yang sama

Konsumen dalam kelompok konsumen

Saat satu instans konsumen membaca data dari sebuah partisi, ia memiliki kendali penuh atas penunjuk dan memproses pesan seperti yang dijelaskan di bagian sebelumnya.
Jika beberapa instance konsumen terhubung dengan group_id yang sama ke suatu topik dengan satu partisi, maka instance yang terhubung terakhir akan diberikan kendali atas pointer dan sejak saat itu akan menerima semua pesan (Gambar-3 4).

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-4. Dua konsumen dalam kelompok konsumen yang sama membaca dari partisi yang sama

Mode pemrosesan ini, di mana jumlah instance konsumen melebihi jumlah partisi, dapat dianggap sebagai konsumen eksklusif. Ini dapat berguna jika Anda memerlukan pengelompokan "aktif-pasif" (atau "panas-hangat") dari instans konsumen Anda, meskipun menjalankan banyak konsumen secara paralel ("aktif-aktif" atau "panas-panas") jauh lebih umum daripada konsumen. Siaga.

Perilaku distribusi pesan yang dijelaskan di atas dapat mengejutkan dibandingkan dengan perilaku antrean JMS normal. Pada model ini, pesan yang dikirim ke antrian akan terdistribusi secara merata di antara kedua konsumen.

Paling sering, saat kami membuat banyak contoh konsumen, kami melakukannya untuk memproses pesan secara paralel, atau untuk meningkatkan kecepatan membaca, atau untuk meningkatkan stabilitas proses membaca. Karena hanya satu instance konsumen yang dapat membaca data dari partisi pada satu waktu, bagaimana hal ini dapat dicapai di Kafka?

Salah satu cara untuk melakukannya adalah dengan menggunakan satu instans konsumen untuk membaca semua pesan dan meneruskannya ke kumpulan utas. Meskipun pendekatan ini meningkatkan throughput pemrosesan, pendekatan ini meningkatkan kompleksitas logika konsumen dan tidak melakukan apa pun untuk meningkatkan ketahanan sistem pembacaan. Jika salah satu salinan konsumen mati karena listrik padam atau kejadian serupa, maka pengurangan berhenti.

Cara kanonik untuk menyelesaikan masalah ini di Kafka adalah dengan menggunakan bОlebih banyak partisi.

Mempartisi

Partisi adalah mekanisme utama untuk memparalelkan pembacaan dan penskalaan topik di luar bandwidth instance broker tunggal. Untuk lebih memahami hal ini, mari pertimbangkan situasi di mana ada topik dengan dua partisi dan satu konsumen berlangganan topik ini (Gambar-3 5).

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-5. Satu konsumen membaca dari beberapa partisi

Dalam skenario ini, konsumen diberi kendali atas pointer yang sesuai dengan group_id di kedua partisi dan mulai membaca pesan dari kedua partisi.
Ketika konsumen tambahan untuk group_id yang sama ditambahkan ke topik ini, Kafka merealokasi salah satu partisi dari konsumen pertama ke konsumen kedua. Setelah itu, setiap instance konsumen akan membaca dari satu partisi topik (Gambar-3 6).

Untuk memastikan bahwa pesan diproses secara paralel dalam 20 utas, Anda memerlukan setidaknya 20 partisi. Jika ada lebih sedikit partisi, Anda akan memiliki konsumen yang tidak memiliki apa-apa untuk dikerjakan, seperti yang dijelaskan sebelumnya dalam pembahasan konsumen eksklusif.

Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 3. Kafka
Gambar 3-6. Dua konsumen dalam kelompok konsumen yang sama membaca dari partisi yang berbeda

Skema ini sangat mengurangi kerumitan broker Kafka dibandingkan dengan distribusi pesan yang diperlukan untuk mempertahankan antrian JMS. Di sini Anda tidak perlu khawatir tentang poin-poin berikut:

  • Konsumen mana yang harus menerima pesan berikutnya, berdasarkan alokasi round-robin, kapasitas buffer prefetch saat ini, atau pesan sebelumnya (seperti untuk grup pesan JMS).
  • Pesan mana yang dikirim ke konsumen mana dan apakah harus dikirim ulang jika terjadi kegagalan.

Yang harus dilakukan broker Kafka adalah menyampaikan pesan secara berurutan kepada konsumen saat konsumen memintanya.

Namun, persyaratan untuk memparalelkan pengoreksian dan pengiriman ulang pesan yang gagal tidak hilang - tanggung jawab untuk itu hanya berpindah dari broker ke klien. Ini berarti bahwa mereka harus diperhitungkan dalam kode Anda.

Mengirim pesan

Produsen pesan tersebut bertanggung jawab untuk memutuskan partisi mana yang akan dikirimi pesan. Untuk memahami mekanisme yang dilakukan, pertama-tama kita perlu mempertimbangkan apa sebenarnya yang sebenarnya kita kirim.

Sedangkan di JMS kami menggunakan struktur pesan dengan metadata (header dan properti) dan badan yang berisi muatan (payload), di Kafka pesannya adalah pasangkan "nilai kunci". Muatan pesan dikirim sebagai nilai. Kuncinya, di sisi lain, terutama digunakan untuk mempartisi dan harus berisi kunci khusus logika bisnisuntuk meletakkan pesan terkait di partisi yang sama.

Di Bab 2, kita membahas skenario taruhan online di mana peristiwa terkait harus diproses secara berurutan oleh satu konsumen:

  1. Akun pengguna dikonfigurasi.
  2. Uang dikreditkan ke akun.
  3. Taruhan dibuat yang menarik uang dari akun.

Jika setiap peristiwa adalah pesan yang diposting ke suatu topik, maka kunci alaminya adalah ID akun.
Saat pesan dikirim menggunakan Kafka Producer API, pesan tersebut diteruskan ke fungsi partisi yang, mengingat pesan dan status cluster Kafka saat ini, mengembalikan ID partisi tujuan pengiriman pesan. Fitur ini diimplementasikan di Java melalui antarmuka Partitioner.

Antarmuka ini terlihat seperti ini:

interface Partitioner {
    int partition(String topic,
        Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
}

Implementasi Partitioner menggunakan algoritme hashing tujuan umum default di atas kunci untuk menentukan partisi, atau round-robin jika tidak ada kunci yang ditentukan. Nilai default ini berfungsi dengan baik di sebagian besar kasus. Namun, di masa mendatang Anda pasti ingin menulis sendiri.

Menulis strategi partisi Anda sendiri

Mari kita lihat contoh di mana Anda ingin mengirim metadata bersama dengan muatan pesan. Muatan dalam contoh kami adalah instruksi untuk melakukan deposit ke akun game. Sebuah instruksi adalah sesuatu yang kami ingin dijamin untuk tidak dimodifikasi pada transmisi dan ingin memastikan bahwa hanya sistem upstream terpercaya yang dapat menginisiasi instruksi tersebut. Dalam hal ini, sistem pengirim dan penerima menyepakati penggunaan tanda tangan untuk mengautentikasi pesan.
Di JMS normal, kami cukup mendefinisikan properti "tanda tangan pesan" dan menambahkannya ke pesan. Namun, Kafka tidak memberi kita mekanisme untuk meneruskan metadata, hanya kunci dan nilai.

Karena nilainya adalah muatan transfer bank yang integritasnya ingin kami pertahankan, kami tidak punya pilihan selain menentukan struktur data yang akan digunakan dalam kunci. Dengan asumsi bahwa kita memerlukan ID akun untuk mempartisi, karena semua pesan yang terkait dengan akun harus diproses secara berurutan, kita akan membuat struktur JSON berikut:

{
  "signature": "541661622185851c248b41bf0cea7ad0",
  "accountId": "10007865234"
}

Karena nilai tanda tangan akan bervariasi tergantung pada payload, strategi hashing default antarmuka Partitioner tidak akan mengelompokkan pesan terkait dengan andal. Oleh karena itu, kita perlu menulis strategi kita sendiri yang akan mengurai kunci ini dan mempartisi nilai accountId.

Kafka menyertakan checksum untuk mendeteksi korupsi pesan di toko dan memiliki fitur keamanan yang lengkap. Meski begitu, persyaratan khusus industri, seperti di atas, terkadang muncul.

Strategi pemartisian pengguna harus memastikan bahwa semua pesan terkait berakhir di partisi yang sama. Meskipun ini tampak sederhana, persyaratannya dapat menjadi rumit karena pentingnya mengurutkan pesan terkait dan seberapa tetap jumlah partisi dalam suatu topik.

Jumlah partisi dalam suatu topik dapat berubah seiring waktu, karena dapat ditambahkan jika lalu lintas melampaui ekspektasi awal. Dengan demikian, kunci pesan dapat diasosiasikan dengan partisi tempat mereka awalnya dikirim, menyiratkan bagian dari status untuk dibagikan di antara instance produser.

Faktor lain yang perlu dipertimbangkan adalah pemerataan pesan di seluruh partisi. Biasanya, kunci tidak didistribusikan secara merata di seluruh pesan, dan fungsi hash tidak menjamin distribusi pesan yang adil untuk sejumlah kecil kunci.
Penting untuk diperhatikan bahwa bagaimanapun Anda memilih untuk membagi pesan, pemisah itu sendiri mungkin perlu digunakan kembali.

Pertimbangkan persyaratan untuk mereplikasi data antar cluster Kafka di lokasi geografis yang berbeda. Untuk tujuan ini, Kafka hadir dengan alat baris perintah yang disebut MirrorMaker, yang digunakan untuk membaca pesan dari satu kluster dan mentransfernya ke kluster lain.

MirrorMaker harus memahami kunci dari topik yang direplikasi untuk mempertahankan urutan relatif antar pesan saat mereplikasi antar cluster, karena jumlah partisi untuk topik tersebut mungkin tidak sama di dua cluster.

Strategi pemartisian khusus relatif jarang, karena hashing atau round robin default bekerja dengan baik di sebagian besar skenario. Namun, jika Anda memerlukan jaminan pemesanan yang kuat atau perlu mengekstrak metadata dari payload, maka mempartisi adalah sesuatu yang harus Anda perhatikan lebih dekat.

Manfaat skalabilitas dan kinerja Kafka berasal dari pengalihan beberapa tanggung jawab broker tradisional kepada klien. Dalam hal ini, keputusan dibuat untuk mendistribusikan pesan yang berpotensi terkait di antara beberapa konsumen yang bekerja secara paralel.

Broker JMS juga perlu menangani persyaratan tersebut. Menariknya, mekanisme pengiriman pesan terkait ke konsumen yang sama, yang diterapkan melalui Grup Pesan JMS (variasi dari strategi sticky load balancing (SLB)), juga mengharuskan pengirim untuk menandai pesan terkait. Dalam kasus JMS, broker bertanggung jawab untuk mengirimkan grup pesan terkait ini ke satu dari banyak konsumen, dan mentransfer kepemilikan grup jika konsumen jatuh.

Perjanjian Produsen

Mempartisi bukan satu-satunya hal yang perlu dipertimbangkan saat mengirim pesan. Mari kita lihat metode send() dari kelas Producer di Java API:

Future < RecordMetadata > send(ProducerRecord < K, V > record);
Future < RecordMetadata > send(ProducerRecord < K, V > record, Callback callback);

Harus segera dicatat bahwa kedua metode mengembalikan Future, yang menunjukkan bahwa operasi pengiriman tidak segera dilakukan. Hasilnya adalah pesan (ProducerRecord) ditulis ke send buffer untuk setiap partisi aktif dan dikirim ke broker sebagai utas latar belakang di pustaka klien Kafka. Meskipun ini membuat segalanya menjadi sangat cepat, itu berarti aplikasi yang tidak berpengalaman dapat kehilangan pesan jika prosesnya dihentikan.

Seperti biasa, ada cara untuk membuat operasi pengiriman lebih andal dengan mengorbankan performa. Ukuran buffer ini dapat diatur ke 0, dan thread aplikasi pengirim akan dipaksa menunggu hingga pengiriman pesan ke broker selesai, sebagai berikut:

RecordMetadata metadata = producer.send(record).get();

Lebih lanjut tentang membaca pesan

Membaca pesan memiliki kerumitan tambahan yang perlu dispekulasikan. Berbeda dengan API JMS, yang dapat menjalankan pendengar pesan sebagai tanggapan atas pesan, itu Konsumen Kafka hanya jajak pendapat. Mari kita lihat lebih dekat metodenya pemilihan()digunakan untuk tujuan ini:

ConsumerRecords < K, V > poll(long timeout);

Nilai balik dari metode ini adalah struktur wadah yang berisi banyak objek catatan konsumen dari kemungkinan beberapa partisi. catatan konsumen itu sendiri merupakan objek pemegang untuk pasangan nilai kunci dengan metadata terkait, seperti partisi asalnya.

Seperti yang dibahas dalam Bab 2, kita harus mengingat apa yang terjadi pada pesan setelah berhasil atau tidak berhasil diproses, misalnya, jika klien tidak dapat memproses pesan atau jika dibatalkan. Di JMS, ini ditangani melalui mode pengakuan. Pialang akan menghapus pesan yang berhasil diproses, atau mengirim ulang pesan mentah atau palsu (dengan asumsi transaksi digunakan).
Kafka bekerja sangat berbeda. Pesan tidak dihapus di broker setelah proofreading, dan apa yang terjadi pada kegagalan adalah tanggung jawab dari kode proofreading itu sendiri.

Seperti yang telah kami katakan, grup konsumen dikaitkan dengan offset di log. Posisi log yang terkait dengan offset ini sesuai dengan pesan berikutnya yang akan dikeluarkan sebagai tanggapan pemilihan(). Titik waktu ketika offset ini meningkat sangat menentukan untuk membaca.

Kembali ke model membaca yang dibahas sebelumnya, pemrosesan pesan terdiri dari tiga tahap:

  1. Mengambil pesan untuk dibaca.
  2. Memproses pesan.
  3. Konfirmasi pesan.

Konsumen Kafka hadir dengan opsi konfigurasi aktifkan.otomatis.commit. Ini adalah setelan default yang sering digunakan, seperti umumnya setelan yang berisi kata "otomatis".

Sebelum Kafka 0.10, klien yang menggunakan opsi ini akan mengirimkan offset dari pesan terakhir yang dibaca pada panggilan berikutnya pemilihan() setelah diproses. Ini berarti bahwa pesan apa pun yang telah diambil dapat diproses ulang jika klien telah memprosesnya tetapi tiba-tiba dihancurkan sebelum menelepon pemilihan(). Karena broker tidak menyatakan berapa kali pesan telah dibaca, konsumen berikutnya yang mengambil pesan itu tidak akan tahu hal buruk terjadi. Perilaku ini adalah pseudo-transaksional. Offset hanya dilakukan jika pesan berhasil diproses, tetapi jika klien dibatalkan, broker akan mengirimkan pesan yang sama lagi ke klien lain. Perilaku ini sesuai dengan jaminan pengiriman pesan "setidaknya sekali".

Di Kafka 0.10, kode klien telah diubah sehingga komit dipicu secara berkala oleh pustaka klien, seperti yang dikonfigurasi auto.commit.interval.ms. Perilaku ini berada di antara mode JMS AUTO_ACKNOWLEDGE dan DUPS_OK_ACKNOWLEDGE. Saat menggunakan komitmen otomatis, pesan dapat dilakukan terlepas dari apakah pesan tersebut benar-benar diproses - hal ini dapat terjadi jika konsumen lambat. Jika konsumen membatalkan, pesan akan diambil oleh konsumen berikutnya, mulai dari posisi yang dikomit, yang dapat mengakibatkan pesan tidak terjawab. Dalam hal ini, Kafka tidak kehilangan pesan, kode bacaan tidak memprosesnya.

Mode ini memiliki janji yang sama seperti di versi 0.9: pesan dapat diproses, tetapi jika gagal, offset mungkin tidak dilakukan, berpotensi menyebabkan pengiriman menjadi dua kali lipat. Semakin banyak pesan yang Anda ambil saat mengeksekusi pemilihan(), semakin banyak masalah ini.

Seperti dibahas dalam β€œMembaca Pesan dari Antrean” di halaman 21, tidak ada pengiriman pesan satu kali dalam sistem pesan saat mode kegagalan diperhitungkan.

Di Kafka, ada dua cara untuk melakukan (commit) offset (offset): secara otomatis dan manual. Dalam kedua kasus tersebut, pesan dapat diproses berkali-kali jika pesan telah diproses tetapi gagal sebelum komit. Anda juga dapat memilih untuk tidak memproses pesan sama sekali jika komit terjadi di latar belakang dan kode Anda selesai sebelum dapat diproses (mungkin di Kafka 0.9 dan sebelumnya).

Anda dapat mengontrol proses komit offset manual di API konsumen Kafka dengan menyetel parameter aktifkan.otomatis.commit ke false dan secara eksplisit memanggil salah satu metode berikut:

void commitSync();
void commitAsync();

Jika Anda ingin memproses pesan "setidaknya sekali", Anda harus melakukan offset secara manual komitSync()dengan menjalankan perintah ini segera setelah memproses pesan.

Metode ini tidak mengizinkan pesan untuk diakui sebelum diproses, tetapi tidak melakukan apa pun untuk menghilangkan kemungkinan penundaan pemrosesan sambil memberikan kesan transaksional. Tidak ada transaksi di Kafka. Klien tidak memiliki kemampuan untuk melakukan hal berikut:

  • Secara otomatis memutar kembali pesan palsu. Konsumen sendiri harus menangani pengecualian yang timbul dari payload bermasalah dan pemadaman backend, karena mereka tidak dapat mengandalkan broker untuk mengirim ulang pesan.
  • Kirim pesan ke beberapa topik dalam satu operasi atom. Seperti yang akan kita lihat sebentar lagi, kontrol atas topik dan partisi yang berbeda dapat berada di mesin yang berbeda di cluster Kafka yang tidak mengoordinasikan transaksi saat dikirim. Pada saat penulisan ini, beberapa pekerjaan telah dilakukan untuk memungkinkan hal ini dengan KIP-98.
  • Kaitkan membaca satu pesan dari satu topik dengan mengirim pesan lain ke topik lain. Sekali lagi, arsitektur Kafka bergantung pada banyak mesin independen yang berjalan sebagai satu bus dan tidak ada upaya untuk menyembunyikannya. Misalnya, tidak ada komponen API yang memungkinkan Anda menautkan konsumen ΠΈ Produsen dalam sebuah transaksi. Di JMS, ini disediakan oleh objek sesidari mana diciptakan Produser Pesan ΠΈ PesanKonsumen.

Jika kita tidak dapat mengandalkan transaksi, bagaimana kita dapat menyediakan semantik yang lebih dekat dengan yang disediakan oleh sistem perpesanan tradisional?

Jika ada kemungkinan bahwa offset konsumen dapat meningkat sebelum pesan diproses, seperti selama crash konsumen, maka konsumen tidak memiliki cara untuk mengetahui apakah grup konsumennya melewatkan pesan saat diberi partisi. Jadi salah satu strateginya adalah memundurkan offset ke posisi sebelumnya. API konsumen Kafka menyediakan metode berikut untuk ini:

void seek(TopicPartition partition, long offset);
void seekToBeginning(Collection < TopicPartition > partitions);

metode mencari() dapat digunakan dengan metode
offsetForTimes(Peta cap waktuToSearch) untuk mundur ke keadaan di beberapa titik tertentu di masa lalu.

Secara implisit, dengan menggunakan pendekatan ini berarti sangat mungkin beberapa pesan yang sebelumnya diproses akan dibaca dan diproses kembali. Untuk menghindarinya, kita dapat menggunakan pembacaan idempoten, seperti yang dijelaskan di Bab 4, untuk melacak pesan yang dilihat sebelumnya dan menghapus duplikat.

Alternatifnya, kode konsumen Anda dapat dibuat sederhana, selama kehilangan atau duplikasi pesan dapat diterima. Saat kami melihat kasus penggunaan yang umum digunakan Kafka, seperti menangani peristiwa log, metrik, pelacakan klik, dll., kami menyadari bahwa hilangnya pesan individual tidak mungkin berdampak signifikan pada aplikasi di sekitarnya. Dalam kasus seperti itu, nilai default dapat diterima dengan sempurna. Di sisi lain, jika aplikasi Anda perlu mengirim pembayaran, Anda harus hati-hati menangani setiap pesan. Semuanya bermuara pada konteks.

Pengamatan pribadi menunjukkan bahwa ketika intensitas pesan meningkat, nilai setiap pesan individu menurun. Pesan besar cenderung berharga bila dilihat dalam bentuk agregat.

Ketersediaan Tinggi

Pendekatan Kafka terhadap ketersediaan tinggi sangat berbeda dengan pendekatan ActiveMQ. Kafka dirancang di sekitar klaster skala besar di mana semua instans broker menerima dan mendistribusikan pesan pada waktu yang sama.

Cluster Kafka terdiri dari beberapa instance broker yang berjalan di server yang berbeda. Kafka dirancang untuk berjalan pada perangkat keras biasa yang berdiri sendiri, di mana setiap node memiliki penyimpanan khusus sendiri. Penggunaan penyimpanan terpasang jaringan (SAN) tidak disarankan karena beberapa node komputasi dapat bersaing untuk waktu.Π«e interval penyimpanan dan menciptakan konflik.

Kafka adalah selalu aktif sistem. Banyak pengguna Kafka besar tidak pernah mematikan cluster mereka dan perangkat lunak selalu diperbarui dengan restart berurutan. Ini dicapai dengan menjamin kompatibilitas dengan versi sebelumnya untuk pesan dan interaksi antar broker.

Broker terhubung ke cluster server Penjaga kebun binatang, yang bertindak sebagai registri data konfigurasi dan digunakan untuk mengoordinasikan peran masing-masing broker. ZooKeeper sendiri adalah sistem terdistribusi yang menyediakan ketersediaan tinggi melalui replikasi informasi dengan membangun jumlah anggota minimum.

Dalam kasus dasar, topik dibuat di kluster Kafka dengan properti berikut:

  • Jumlah partisi. Seperti yang telah dibahas sebelumnya, nilai pasti yang digunakan di sini bergantung pada tingkat pembacaan paralel yang diinginkan.
  • Faktor replikasi (faktor) menentukan berapa banyak instance broker di cluster yang harus berisi log untuk partisi ini.

Menggunakan ZooKeepers untuk koordinasi, Kafka mencoba mendistribusikan partisi baru secara adil di antara broker di cluster. Ini dilakukan oleh satu contoh yang bertindak sebagai Pengendali.

Saat runtime untuk setiap partisi topik Pengendali menetapkan peran ke broker pemimpin (pemimpin, master, presenter) dan pengikut (pengikut, budak, bawahan). Broker, yang bertindak sebagai pemimpin partisi ini, bertanggung jawab untuk menerima semua pesan yang dikirim oleh produsen dan mendistribusikan pesan tersebut ke konsumen. Saat pesan dikirim ke partisi topik, pesan tersebut direplikasi ke semua node broker yang bertindak sebagai pengikut untuk partisi tersebut. Setiap node yang berisi log untuk partisi dipanggil replika. Seorang broker dapat bertindak sebagai leader untuk beberapa partisi dan sebagai follower untuk yang lainnya.

Seorang pengikut yang berisi semua pesan yang dipegang oleh pemimpin disebut replika tersinkronisasi (replika yang dalam keadaan tersinkronisasi, replika in-sync). Jika broker yang bertindak sebagai leader untuk partisi turun, broker mana pun yang diperbarui atau disinkronkan untuk partisi tersebut dapat mengambil alih peran leader. Ini adalah desain yang sangat berkelanjutan.

Bagian dari konfigurasi produser adalah parameternya pantat, yang menentukan berapa banyak replika yang harus mengakui (mengakui) penerimaan pesan sebelum utas aplikasi melanjutkan pengiriman: 0, 1, atau semuanya. Jika disetel ke semua, kemudian ketika pesan diterima, leader akan mengirimkan konfirmasi kembali ke produser segera setelah menerima konfirmasi (acknowledgment) record dari beberapa cues (termasuk dirinya sendiri) yang ditentukan oleh setting topik min.insync.replika (standar 1). Jika pesan tidak berhasil direplikasi, maka produser akan mengeluarkan pengecualian aplikasi (Replika Tidak Cukup ΠΈΠ»ΠΈ NotCukupReplikaSetelahMenambahkan).

Konfigurasi tipikal membuat topik dengan faktor replikasi 3 (1 pemimpin, 2 pengikut per partisi) dan parameternya min.insync.replika diatur ke 2. Dalam hal ini, cluster akan mengizinkan salah satu broker yang mengelola partisi topik turun tanpa mempengaruhi aplikasi klien.

Ini membawa kita kembali ke trade-off yang sudah akrab antara kinerja dan keandalan. Replikasi terjadi dengan mengorbankan waktu tunggu tambahan untuk konfirmasi (ucapan terima kasih) dari pengikut. Meskipun, karena berjalan secara paralel, replikasi ke setidaknya tiga node memiliki kinerja yang sama dengan dua (mengabaikan peningkatan penggunaan bandwidth jaringan).

Dengan menggunakan skema replikasi ini, Kafka secara cerdik menghindari kebutuhan untuk menulis setiap pesan secara fisik ke disk dengan operasi tersebut sinkronisasi(). Setiap pesan yang dikirim oleh produser akan ditulis ke log partisi, tetapi seperti yang dibahas di Bab 2, penulisan ke file pada awalnya dilakukan di buffer sistem operasi. Jika pesan ini direplikasi ke instance Kafka lain dan ada dalam ingatannya, hilangnya pemimpin tidak berarti bahwa pesan itu sendiri hilang - itu dapat diambil alih oleh replika yang disinkronkan.
Penolakan untuk melakukan operasi sinkronisasi() berarti Kafka dapat menerima pesan secepat ia menulisnya ke memori. Sebaliknya, semakin lama Anda dapat menghindari membilas memori ke disk, semakin baik. Karena alasan ini, tidak jarang broker Kafka mengalokasikan memori 64 GB atau lebih. Penggunaan memori ini berarti bahwa satu instance Kafka dapat dengan mudah berjalan dengan kecepatan ribuan kali lebih cepat daripada broker pesan tradisional.

Kafka juga dapat dikonfigurasi untuk menerapkan operasi sinkronisasi() untuk mengirim pesan paket. Karena semua yang ada di Kafka berorientasi pada paket, ini benar-benar berfungsi dengan baik untuk banyak kasus penggunaan dan merupakan alat yang berguna bagi pengguna yang membutuhkan jaminan yang sangat kuat. Sebagian besar kinerja murni Kafka berasal dari pesan yang dikirim ke broker sebagai paket dan pesan ini dibaca dari broker dalam blok berurutan menggunakan salinan nol operasi (operasi di mana tugas menyalin data dari satu area memori ke yang lain tidak dilakukan). Yang terakhir adalah peningkatan kinerja dan sumber daya yang besar dan hanya dimungkinkan melalui penggunaan struktur data log yang mendasari yang menentukan skema partisi.

Performa yang jauh lebih baik dimungkinkan dalam klaster Kafka dibandingkan dengan satu broker Kafka, karena partisi topik dapat diskalakan di banyak mesin terpisah.

Hasil

Dalam bab ini, kita melihat bagaimana arsitektur Kafka menata kembali hubungan antara klien dan pialang untuk menyediakan jalur perpesanan yang luar biasa kuat, dengan throughput berkali-kali lebih besar daripada pialang pesan konvensional. Kami telah membahas fungsionalitas yang digunakannya untuk mencapai hal ini dan melihat secara singkat arsitektur aplikasi yang menyediakan fungsionalitas ini. Di bab selanjutnya, kita akan melihat masalah umum yang perlu dipecahkan oleh aplikasi berbasis pesan dan mendiskusikan strategi untuk mengatasinya. Kami akan mengakhiri bab ini dengan menguraikan cara berbicara tentang teknologi perpesanan secara umum sehingga Anda dapat mengevaluasi kesesuaiannya untuk kasus penggunaan Anda.

Bagian terjemahan sebelumnya: Memahami broker pesan. Mempelajari mekanisme perpesanan dengan ActiveMQ dan Kafka. Bab 1

Terjemahan selesai: tele.gg/middle_java

Untuk dilanjutkan ...

Hanya pengguna terdaftar yang dapat berpartisipasi dalam survei. Masuk, silakan.

Apakah Kafka digunakan di organisasi Anda?

  • Ya

  • Tidak

  • Dulu dipakai, sekarang tidak

  • Kami berencana untuk menggunakan

38 pengguna memilih. 8 pengguna abstain.

Sumber: www.habr.com

Tambah komentar