Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Apa yang bisa memaksa perusahaan besar seperti Lamoda, dengan proses yang efisien dan puluhan layanan yang saling terhubung, mengubah pendekatannya secara signifikan? Motivasi bisa sangat berbeda: dari legislatif hingga keinginan untuk bereksperimen yang melekat pada semua programmer.

Namun bukan berarti Anda tidak bisa mengandalkan manfaat tambahan. Sergey Zaika akan memberi tahu Anda apa sebenarnya yang bisa Anda menangkan jika Anda menerapkan API berbasis peristiwa di Kafka (sedikit sekali). Pasti juga akan ada pembicaraan tentang pencapaian besar dan penemuan menarik - eksperimen tidak dapat dilakukan tanpanya.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Penafian: Artikel ini berdasarkan materi dari pertemuan yang diadakan Sergey pada November 2018 di HighLoad++. Pengalaman langsung Lamoda bekerja dengan Kafka menarik perhatian pendengar tidak kurang dari laporan lain dalam jadwal tersebut. Kami pikir ini adalah contoh bagus dari fakta bahwa Anda dapat dan harus selalu menemukan orang-orang yang berpikiran sama, dan penyelenggara HighLoad++ akan terus berusaha menciptakan suasana yang kondusif untuk hal ini.

Tentang prosesnya

Lamoda adalah platform e-commerce besar yang memiliki pusat kontak sendiri, layanan pengiriman (dan banyak afiliasi), studio foto, gudang besar, dan semua ini berjalan pada perangkat lunaknya sendiri. Ada puluhan metode pembayaran, mitra b2b yang mungkin menggunakan beberapa atau semua layanan ini dan ingin mengetahui informasi terkini tentang produknya. Selain itu, Lamoda beroperasi di tiga negara selain Federasi Rusia dan semuanya sedikit berbeda di sana. Secara total, mungkin ada lebih dari seratus cara untuk mengkonfigurasi pesanan baru, yang harus diproses dengan caranya sendiri. Semua ini bekerja dengan bantuan lusinan layanan yang terkadang berkomunikasi dengan cara yang tidak jelas. Ada juga sistem pusat yang tanggung jawab utamanya adalah status pesanan. Kami memanggilnya BOB, saya bekerja dengannya.

Alat Pengembalian Dana dengan API berbasis peristiwa

Kata yang didorong oleh peristiwa (event-driven) sudah cukup usang; sedikit lebih jauh kita akan mendefinisikan secara lebih rinci apa yang dimaksud dengan kata ini. Saya akan mulai dengan konteks saat kami memutuskan untuk mencoba pendekatan API berbasis peristiwa di Kafka.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Di toko mana pun, selain pesanan yang dibayar pelanggan, ada kalanya toko diharuskan mengembalikan uang karena produk tidak sesuai dengan pelanggan. Ini adalah proses yang relatif singkat: kami mengklarifikasi informasi, jika perlu, dan mentransfer uang.

Namun pengembaliannya menjadi lebih rumit karena perubahan undang-undang, dan kami harus menerapkan layanan mikro terpisah untuknya.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Motivasi kami:

  1. Hukum FZ-54 - singkatnya, undang-undang mengharuskan pelaporan ke kantor pajak tentang setiap transaksi moneter, baik itu pengembalian atau penerimaan, dalam waktu SLA yang cukup singkat, yaitu beberapa menit. Kami sebagai perusahaan e-commerce menjalankan operasional yang cukup banyak. Secara teknis, hal ini berarti tanggung jawab baru (yang berarti layanan baru) dan perbaikan pada semua sistem yang terlibat.
  2. BOB berpisah adalah proyek internal perusahaan untuk membebaskan BOB dari sejumlah besar tanggung jawab non-inti dan mengurangi kompleksitasnya secara keseluruhan.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Diagram ini menunjukkan sistem utama Lamoda. Sekarang kebanyakan dari mereka lebih banyak konstelasi 5-10 layanan mikro di sekitar monolit yang menyusut. Mereka tumbuh perlahan, tapi kami mencoba membuatnya lebih kecil, karena menyebarkan fragmen yang dipilih di tengah itu menakutkan - kami tidak bisa membiarkannya jatuh. Kami terpaksa memesan semua bursa (panah) dan mempertimbangkan fakta bahwa salah satu bursa mungkin tidak tersedia.

BOB juga memiliki bursa yang cukup banyak: sistem pembayaran, sistem pengiriman, sistem notifikasi, dll.

Secara teknis BOB adalah:

  • ~150 ribu baris kode + ~100 ribu baris pengujian;
  • php7.2 + Komponen Zend 1 & Symfony 3;
  • >100 API & ~50 integrasi keluar;
  • 4 negara dengan logika bisnisnya masing-masing.

Menerapkan BOB itu mahal dan menyakitkan, jumlah kode dan masalah yang dipecahkannya sedemikian rupa sehingga tidak ada yang bisa memikirkan semuanya. Secara umum, ada banyak alasan untuk menyederhanakannya.

Proses Pengembalian

Awalnya, dua sistem terlibat dalam proses ini: BOB dan Pembayaran. Sekarang dua lagi muncul:

  • Layanan Fiskalisasi, yang akan menangani masalah fiskalisasi dan komunikasi dengan layanan eksternal.
  • Alat Pengembalian Dana, yang hanya berisi pertukaran baru agar tidak menggelembungkan BOB.

Sekarang prosesnya terlihat seperti ini:

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

  1. BOB menerima permintaan pengembalian dana.
  2. BOB berbicara tentang Alat Pengembalian Dana ini.
  3. Alat Pengembalian Dana memberi tahu Pembayaran: “Kembalikan uangnya.”
  4. Pembayaran mengembalikan uang.
  5. Refund Tool dan BOB saling menyinkronkan status, karena untuk saat ini keduanya membutuhkannya. Kami belum siap untuk sepenuhnya beralih ke Alat Pengembalian Dana, karena BOB memiliki UI, laporan akuntansi, dan secara umum banyak data yang tidak dapat ditransfer dengan mudah. Anda harus duduk di dua kursi.
  6. Permintaan fiskalisasi hilang.

Hasilnya, kami membuat semacam bus acara di Kafka - bus acara, tempat semuanya dimulai. Hore, sekarang kita punya satu titik kegagalan (sarkasme).

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Pro dan kontranya cukup jelas. Kami membuat bus, artinya sekarang semua layanan bergantung padanya. Hal ini menyederhanakan desain, namun menimbulkan satu titik kegagalan ke dalam sistem. Kafka akan crash, prosesnya akan berhenti.

Apa itu API yang digerakkan oleh peristiwa

Jawaban yang bagus untuk pertanyaan ini ada dalam laporan Martin Fowler (GOTO 2017) "Banyak Makna Arsitektur Berbasis Peristiwa".

Secara singkat apa yang kami lakukan:

  1. Selesaikan semua pertukaran asinkron melalui penyimpanan acara. Daripada memberi tahu setiap konsumen yang tertarik tentang perubahan status melalui jaringan, kami menulis peristiwa tentang perubahan status ke penyimpanan terpusat, dan konsumen yang tertarik dengan topik tersebut membaca semua yang muncul dari sana.
  2. Peristiwa dalam hal ini adalah pemberitahuan (pemberitahuan) bahwa ada sesuatu yang berubah di suatu tempat. Misalnya status pesanan telah berubah. Konsumen yang tertarik dengan beberapa data yang menyertai perubahan status yang tidak disertakan dalam notifikasi dapat mengetahui sendiri statusnya.
  3. Opsi maksimum adalah sumber acara yang lengkap, perpindahan negara, di mana peristiwa berisi semua informasi yang diperlukan untuk diproses: dari mana asalnya dan status apa, bagaimana tepatnya data berubah, dll. Satu-satunya pertanyaan adalah kelayakan dan jumlah informasi yang mampu Anda simpan.

Sebagai bagian dari peluncuran Alat Pengembalian Dana, kami menggunakan opsi ketiga. Pemrosesan peristiwa ini menyederhanakan karena tidak perlu mengekstrak informasi mendetail, ditambah lagi menghilangkan skenario di mana setiap peristiwa baru menghasilkan serangkaian permintaan klarifikasi dari konsumen.

Layanan Alat Pengembalian Dana tidak dimuat, jadi Kafka lebih mementingkan rasa pena daripada kebutuhan. Saya tidak berpikir jika layanan pengembalian dana menjadi proyek dengan beban tinggi, bisnis akan senang.

Pertukaran asinkron SEBAGAIMANA ADANYA

Untuk pertukaran asynchronous, departemen PHP biasanya menggunakan RabbitMQ. Kami mengumpulkan data untuk permintaan tersebut, memasukkannya ke dalam antrian, dan konsumen dari layanan yang sama membacanya dan mengirimkannya (atau tidak mengirimkannya). Untuk APInya sendiri, Lamoda aktif menggunakan Swagger. Kami merancang API, mendeskripsikannya dalam Swagger, dan menghasilkan kode klien dan server. Kami juga menggunakan JSON RPC 2.0 yang sedikit ditingkatkan.

Di beberapa tempat, bus ESB digunakan, beberapa menggunakan activeMQ, tetapi, secara umum, KelinciMQ - standar.

Pertukaran async MENJADI

Saat merancang pertukaran melalui bus peristiwa, sebuah analogi dapat ditelusuri. Kami juga mendeskripsikan pertukaran data di masa depan melalui deskripsi struktur peristiwa. Format yaml, kami harus melakukan pembuatan kode sendiri, generator membuat DTO sesuai dengan spesifikasi dan mengajarkan klien dan server untuk bekerja dengannya. Generasi masuk ke dalam dua bahasa - golang dan php. Hal ini membantu menjaga perpustakaan tetap konsisten. Generatornya ditulis dalam bahasa golang, oleh karena itu diberi nama gogi.

Sumber acara di Kafka adalah hal yang biasa. Ada solusi dari Kafka Confluent versi perusahaan utama, ada Nakadi, solusi dari saudara domain kami Zalando. Kita motivasi untuk memulai dengan vanilla Kafka - ini berarti membiarkan solusi tersebut bebas sampai kita akhirnya memutuskan apakah kita akan menggunakannya di mana pun, dan juga memberikan ruang bagi diri kita sendiri untuk bermanuver dan melakukan perbaikan: kita menginginkan dukungan untuk kita JSON RPC 2.0, generator untuk dua bahasa dan mari kita lihat apa lagi.

Ironisnya, bahkan dalam kasus yang membahagiakan, ketika ada bisnis yang kurang lebih serupa, Zalando, yang membuat solusi yang kurang lebih serupa, kita tidak dapat menggunakannya secara efektif.

Pola arsitektur saat peluncuran adalah sebagai berikut: kita membaca langsung dari Kafka, tetapi menulis hanya melalui events-bus. Ada banyak hal yang siap dibaca di Kafka: broker, penyeimbang, dan kurang lebih siap untuk penskalaan horizontal, saya ingin mempertahankan ini. Kami ingin menyelesaikan rekaman melalui satu Gateway alias Events-bus, dan inilah alasannya.

Acara-bus

Atau bus acara. Ini hanyalah gateway http tanpa kewarganegaraan, yang mengambil beberapa peran penting:

  • Memproduksi Validasi — kami memeriksa apakah kejadian tersebut memenuhi spesifikasi kami.
  • Sistem master acara, yaitu, ini adalah sistem utama dan satu-satunya di perusahaan yang menjawab pertanyaan tentang peristiwa mana yang strukturnya dianggap valid. Validasi hanya melibatkan tipe data dan enum untuk menentukan konten secara ketat.
  • Fungsi hash untuk sharding - struktur pesan Kafka adalah nilai kunci dan menggunakan hash kunci, struktur tersebut dihitung di mana meletakkannya.

Mengapa

Kami bekerja di perusahaan besar dengan proses yang efisien. Mengapa mengubah sesuatu? Ini adalah eksperimen, dan kami berharap dapat memperoleh beberapa manfaat.

1:n+1 pertukaran (satu ke banyak)

Kafka mempermudah menghubungkan konsumen baru ke API.

Katakanlah Anda memiliki direktori yang perlu Anda perbarui di beberapa sistem sekaligus (dan di beberapa sistem baru). Sebelumnya, kami menemukan bundel yang mengimplementasikan set-API, dan sistem master diberi tahu tentang alamat konsumen. Sekarang sistem master mengirimkan pembaruan ke topik tersebut, dan semua orang yang tertarik membacanya. Sistem baru telah muncul - kami mendaftarkannya untuk topik tersebut. Ya, juga bundel, tapi lebih sederhana.

Dalam hal alat pengembalian dana, yang merupakan bagian dari BOB, akan lebih mudah bagi kami untuk tetap menyinkronkannya melalui Kafka. Pembayaran menyatakan bahwa uang dikembalikan: BOB, RT mengetahui hal ini, mengubah statusnya, Dinas Fiskalisasi mengetahui hal ini dan mengeluarkan cek.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Kami memiliki rencana untuk membuat Layanan Pemberitahuan terpadu yang akan memberi tahu klien tentang berita mengenai pesanan/pengembaliannya. Sekarang tanggung jawab ini tersebar antar sistem. Cukup bagi kami untuk mengajari Layanan Notifikasi untuk menangkap informasi relevan dari Kafka dan meresponsnya (dan menonaktifkan notifikasi ini di sistem lain). Tidak diperlukan pertukaran langsung baru.

Didorong oleh data

Informasi antar sistem menjadi transparan - tidak peduli “perusahaan berdarah” apa yang Anda miliki dan tidak peduli seberapa besar simpanan Anda. Lamoda memiliki departemen Analisis Data yang mengumpulkan data dari sistem dan menyajikannya ke dalam bentuk yang dapat digunakan kembali, baik untuk bisnis maupun sistem cerdas. Kafka memungkinkan Anda dengan cepat memberi mereka banyak data dan terus memperbarui arus informasi.

Log replikasi

Pesan tidak hilang setelah dibaca, seperti di RabbitMQ. Ketika suatu peristiwa berisi informasi yang cukup untuk diproses, kita memiliki riwayat perubahan terkini pada objek tersebut, dan, jika diinginkan, kemampuan untuk menerapkan perubahan ini.

Jangka waktu penyimpanan log replikasi bergantung pada intensitas penulisan topik ini; Kafka memungkinkan Anda menetapkan batas waktu penyimpanan dan volume data secara fleksibel. Untuk topik intensif, penting agar semua konsumen memiliki waktu untuk membaca informasi sebelum informasi tersebut hilang, bahkan jika informasi tersebut tidak dapat dioperasikan dalam jangka pendek. Biasanya dimungkinkan untuk menyimpan data satuan hari, yang cukup untuk mendukung.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Selanjutnya sedikit menceritakan kembali dokumentasinya, bagi yang belum familiar dengan Kafka (gambarnya juga dari dokumentasi)

AMQP memiliki antrian: kami menulis pesan ke antrian untuk konsumen. Biasanya, satu antrian diproses oleh satu sistem dengan logika bisnis yang sama. Jika Anda perlu memberi tahu beberapa sistem, Anda dapat mengajarkan aplikasi untuk menulis ke beberapa antrian atau mengonfigurasi pertukaran dengan mekanisme fanout, yang mengkloningnya sendiri.

Kafka memiliki abstraksi serupa tema, di mana Anda menulis pesan, tetapi pesan tersebut tidak hilang setelah dibaca. Secara default, saat Anda terhubung ke Kafka, Anda menerima semua pesan dan memiliki opsi untuk menyimpannya dari bagian terakhir yang Anda tinggalkan. Artinya, Anda membaca secara berurutan, Anda tidak boleh menandai pesan sebagai telah dibaca, tetapi menyimpan id yang kemudian Anda dapat melanjutkan membaca. Id yang Anda pilih disebut offset, dan mekanismenya adalah commit offset.

Oleh karena itu, logika yang berbeda dapat diterapkan. Misalnya, kami memiliki BOB dalam 4 salinan untuk negara berbeda - Lamoda ada di Rusia, Kazakhstan, Ukraina, Belarus. Karena diterapkan secara terpisah, keduanya memiliki konfigurasi dan logika bisnisnya sendiri yang sedikit berbeda. Kami menunjukkan dalam pesan negara mana yang dimaksud. Setiap konsumen BOB di setiap negara membaca dengan groupId yang berbeda, dan jika pesan tersebut tidak berlaku untuk mereka, mereka melewatkannya, mis. segera melakukan offset +1. Jika topik yang sama dibaca oleh Layanan Pembayaran kami, maka Layanan Pembayaran melakukannya dengan grup terpisah, dan oleh karena itu offset tidak berpotongan.

Persyaratan acara:

  • Kelengkapan data. Saya ingin acara tersebut memiliki data yang cukup sehingga dapat diproses.

  • Integritas Kami mendelegasikan ke Events-bus verifikasi bahwa acara tersebut konsisten dan dapat memprosesnya.
  • Urutannya penting. Jika terjadi kepulangan, kita terpaksa bekerja dengan sejarah. Dengan notifikasi, pesanan tidak penting, jika notifikasinya homogen, emailnya akan sama terlepas dari pesanan mana yang datang lebih dulu. Dalam hal pengembalian dana, ada proses yang jelas; jika kami mengubah pesanan, pengecualian akan muncul, pengembalian dana tidak akan dibuat atau diproses - kami akan berakhir di status yang berbeda.
  • Konsistensi. Kami memiliki toko, dan sekarang kami membuat acara, bukan API. Kami memerlukan cara untuk mengirimkan informasi tentang peristiwa baru dan perubahan yang sudah ada dengan cepat dan murah ke layanan kami. Hal ini dicapai melalui spesifikasi umum dalam repositori git dan pembuat kode yang terpisah. Oleh karena itu, klien dan server di layanan yang berbeda terkoordinasi.

Kafka di Lamoda

Kami memiliki tiga instalasi Kafka:

  1. Log;
  2. R&D;
  3. Acara-bus.

Hari ini kita hanya membicarakan poin terakhir. Di bus acara, kami tidak memiliki instalasi yang sangat besar - 3 broker (server) dan hanya 27 topik. Biasanya, satu topik adalah satu proses. Tapi ini adalah poin yang halus, dan kami akan membahasnya sekarang.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Di atas adalah grafik rps. Proses refund ditandai dengan garis turquoise (iya yang ada di sumbu X), dan garis pink adalah proses update konten.

Katalog Lamoda berisi jutaan produk, dan datanya diperbarui setiap saat. Beberapa koleksi sudah ketinggalan zaman, yang baru dirilis untuk menggantikannya, dan model-model baru terus bermunculan di katalog. Kami mencoba memprediksi apa yang akan menarik bagi pelanggan kami besok, jadi kami terus membeli barang baru, memotretnya, dan memperbarui etalase.

Puncak merah muda adalah pembaruan produk, yaitu perubahan produk. Terlihat mereka memotret, memotret, dan kemudian lagi! — memuat paket acara.

Kasus penggunaan Peristiwa Lamoda

Kami menggunakan arsitektur yang dibangun untuk operasi berikut:

  • Pelacakan status pengembalian: ajakan bertindak dan pelacakan status dari semua sistem yang terlibat. Pembayaran, status, fiskalisasi, pemberitahuan. Di sini kami menguji pendekatannya, membuat alat, mengumpulkan semua bug, menulis dokumentasi, dan memberi tahu rekan kami cara menggunakannya.
  • Memperbarui kartu produk: konfigurasi, metadata, karakteristik. Satu sistem membaca (yang ditampilkan), dan beberapa sistem menulis.
  • Email, dorong dan sms: pesanan sudah diambil, pesanan sudah sampai, retur sudah diterima, dll, banyak sekali.
  • Stok, pembaharuan gudang — pembaruan kuantitatif barang, hanya angka: kedatangan di gudang, pengembalian. Semua sistem yang terkait dengan pemesanan barang harus beroperasi dengan data terkini. Saat ini sistem update stok cukup kompleks, Kafka akan menyederhanakannya.
  • Analisis Data (Departemen Litbang), alat ML, analitik, statistik. Kami ingin informasi transparan - Kafka sangat cocok untuk ini.

Sekarang bagian yang lebih menarik adalah tentang terobosan besar dan penemuan menarik yang telah terjadi selama enam bulan terakhir.

Masalah desain

Katakanlah kita ingin melakukan hal baru - misalnya, mentransfer seluruh proses pengiriman ke Kafka. Sekarang sebagian dari proses tersebut diimplementasikan dalam Pemrosesan Pesanan di BOB. Ada model status di balik pengalihan pesanan ke layanan pengiriman, perpindahan ke gudang perantara, dan sebagainya. Ada keseluruhan monolit, bahkan dua, ditambah sekumpulan API yang didedikasikan untuk pengiriman. Mereka tahu lebih banyak tentang pengiriman.

Tampaknya area ini mirip, namun Pemrosesan Pesanan di BOB dan Sistem Pengiriman memiliki status yang berbeda. Misalnya, beberapa jasa kurir tidak mengirimkan status perantara, tetapi hanya status akhir: “terkirim” atau “hilang”. Sebaliknya, yang lain melaporkan dengan sangat rinci tentang pergerakan barang. Setiap orang memiliki aturan validasinya masing-masing: bagi sebagian orang, email tersebut valid, artinya akan diproses; untuk yang lain tidak valid, tapi pesanan tetap diproses karena ada nomor telepon yang bisa dihubungi, dan ada yang bilang pesanan seperti itu tidak akan diproses sama sekali.

Aliran data

Dalam kasus Kafka, muncul pertanyaan tentang pengorganisasian aliran data. Tugas ini melibatkan pemilihan strategi berdasarkan beberapa poin; mari kita bahas semuanya.

Dalam satu topik atau berbeda?

Kami memiliki spesifikasi acara. Di BOB kami menulis bahwa pesanan ini dan itu perlu dikirimkan, dan menunjukkan: nomor pesanan, komposisinya, beberapa SKU dan kode batang, dll. Ketika barang sampai di gudang, pengiriman akan dapat menerima status, stempel waktu dan segala sesuatu yang diperlukan. Namun kemudian kami ingin menerima pembaruan mengenai data ini di BOB. Kami memiliki proses kebalikan dalam menerima data dari pengiriman. Apakah ini acara yang sama? Atau apakah ini pertukaran terpisah yang pantas mendapatkan topik tersendiri?

Kemungkinan besar, mereka akan sangat mirip, dan godaan untuk membuat satu topik bukannya tidak berdasar, karena topik yang terpisah berarti konsumen yang terpisah, konfigurasi yang terpisah, generasi yang terpisah dari semua ini. Tapi bukan fakta.

Bidang baru atau acara baru?

Namun jika menggunakan event yang sama, maka muncul masalah lain. Misalnya, tidak semua sistem pengiriman dapat menghasilkan DTO seperti yang dapat dihasilkan BOB. Kami mengirimi mereka id, tetapi mereka tidak menyimpannya karena mereka tidak memerlukannya, dan dari sudut pandang memulai proses bus peristiwa, bidang ini wajib diisi.

Jika kita memperkenalkan aturan untuk event-bus yang mengharuskan bidang ini, maka kita terpaksa menetapkan aturan validasi tambahan di BOB atau di start event handler. Validasi mulai menyebar ke seluruh layanan - ini sangat tidak nyaman.

Masalah lainnya adalah godaan untuk melakukan pembangunan bertahap. Kita diberitahu bahwa ada sesuatu yang perlu ditambahkan pada acara tersebut, dan mungkin jika dipikir-pikir, seharusnya acara tersebut merupakan acara tersendiri. Namun dalam skema kami, peristiwa terpisah adalah topik terpisah. Topik terpisah adalah keseluruhan proses yang saya jelaskan di atas. Pengembang tergoda untuk menambahkan bidang lain ke skema JSON dan membuatnya kembali.

Dalam hal pengembalian uang, kami tiba di acara dalam setengah tahun. Kami memiliki satu peristiwa meta yang disebut pembaruan pengembalian dana, yang memiliki bidang tipe yang menjelaskan apa sebenarnya pembaruan ini. Oleh karena itu, kami memiliki peralihan “luar biasa” dengan validator yang memberi tahu kami cara memvalidasi peristiwa ini dengan jenis ini.

Pembuatan versi acara

Untuk memvalidasi pesan di Kafka Anda dapat menggunakan Avro, tetapi perlu segera menggunakannya dan menggunakan Confluent. Dalam kasus kami, kami harus berhati-hati dengan pembuatan versi. Tidak selalu mungkin untuk membaca ulang pesan dari log replikasi karena model telah “kiri”. Pada dasarnya, ternyata membangun versi sehingga modelnya kompatibel: misalnya, menjadikan suatu bidang opsional untuk sementara. Jika perbedaannya terlalu kuat, kami mulai menulis topik baru, dan memindahkan klien setelah mereka selesai membaca topik lama.

Urutan pembacaan partisi yang terjamin

Topik di dalam Kafka dibagi menjadi beberapa partisi. Hal ini tidak terlalu penting ketika kita merancang entitas dan pertukaran, namun penting ketika memutuskan bagaimana menggunakan dan menskalakannya.

Biasanya, Anda menulis satu topik di Kafka. Secara default, satu partisi digunakan, dan semua pesan dalam topik ini masuk ke partisi tersebut. Dan konsumen akibatnya membaca pesan-pesan ini secara berurutan. Katakanlah sekarang kita perlu memperluas sistem sehingga pesan dibaca oleh dua konsumen berbeda. Jika, misalnya, Anda mengirim SMS, Anda dapat meminta Kafka untuk membuat partisi tambahan, dan Kafka akan mulai membagi pesan menjadi dua bagian - setengah di sini, setengah di sini.

Bagaimana Kafka membaginya? Setiap pesan memiliki isi (tempat kami menyimpan JSON) dan kunci. Anda dapat melampirkan fungsi hash ke kunci ini, yang akan menentukan di partisi mana pesan akan masuk.

Dalam kasus kami dengan pengembalian dana, ini penting, jika kita mengambil dua partisi, maka ada kemungkinan konsumen paralel akan memproses acara kedua sebelum yang pertama dan akan terjadi masalah. Fungsi hash memastikan bahwa pesan dengan kunci yang sama berakhir di partisi yang sama.

Peristiwa vs perintah

Ini adalah masalah lain yang kami temui. Peristiwa adalah peristiwa tertentu: kita mengatakan sesuatu terjadi di suatu tempat (sesuatu_terjadi), misalnya suatu barang dibatalkan atau terjadi pengembalian dana. Jika seseorang mendengarkan peristiwa ini, maka berdasarkan "item dibatalkan", entitas pengembalian dana akan dibuat, dan "pengembalian dana terjadi" akan ditulis di suatu tempat di pengaturan.

Namun biasanya, saat Anda merancang acara, Anda tidak ingin menulisnya dengan sia-sia - Anda mengandalkan fakta bahwa seseorang akan membacanya. Ada godaan besar untuk menulis bukan sesuatu yang terjadi (barang_dibatalkan, pengembalian dana_dikembalikan), tetapi sesuatu yang harus_dilakukan. Misalnya barang siap dikembalikan.

Di satu sisi, ini menunjukkan bagaimana acara tersebut akan digunakan. Di sisi lain, namanya tidak terdengar seperti nama acara pada umumnya. Selain itu, tidak jauh dari sini ke perintah do_something. Namun Anda tidak memiliki jaminan bahwa seseorang membaca acara ini; dan jika Anda membacanya, maka Anda berhasil membacanya; dan jika Anda berhasil membacanya, maka Anda melakukan sesuatu, dan sesuatu itu berhasil. Saat suatu peristiwa menjadi do_something, umpan balik menjadi diperlukan, dan itu menjadi masalah.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Dalam pertukaran asinkron di RabbitMQ, saat Anda membaca pesan, buka http, Anda mendapat respons - setidaknya pesan telah diterima. Saat Anda menulis ke Kafka, ada pesan yang Anda tulis ke Kafka, namun Anda tidak tahu apa pun tentang cara pemrosesannya.

Oleh karena itu, dalam kasus kami, kami harus memperkenalkan peristiwa respons dan mengatur pemantauan sehingga jika begitu banyak peristiwa yang dikirim, setelah waktu tertentu, jumlah peristiwa respons yang sama akan tiba. Jika ini tidak terjadi, sepertinya ada yang tidak beres. Misalnya, jika kami mengirimkan acara “item_ready_to_refund”, kami mengharapkan pengembalian dana akan dilakukan, uang akan dikembalikan ke klien, dan acara “money_refunded” akan dikirimkan kepada kami. Namun hal ini belum pasti sehingga perlu dilakukan pemantauan.

Nuansa

Ada masalah yang cukup jelas: jika Anda membaca suatu topik secara berurutan, dan Anda mendapatkan pesan yang buruk, konsumen akan jatuh, dan Anda tidak akan melangkah lebih jauh. Anda membutuhkan hentikan semua konsumen, komit offset lebih lanjut untuk melanjutkan membaca.

Kami mengetahuinya, kami memperhitungkannya, namun hal itu terjadi. Dan hal ini terjadi karena event tersebut valid dari sudut pandang event-bus, event tersebut valid dari sudut pandang validator aplikasi, namun tidak valid dari sudut pandang PostgreSQL, karena dalam satu sistem kita MySQL dengan UNSIGNED INT, dan dalam sistem yang baru ditulis memiliki PostgreSQL hanya dengan INT. Ukurannya sedikit lebih kecil, dan ID-nya tidak pas. Symfony meninggal dengan pengecualian. Kami, tentu saja, menangkap pengecualian karena kami mengandalkannya, dan akan melakukan offset ini, tapi sebelum itu kami ingin menambah penghitung masalah, karena pesan tidak berhasil diproses. Penghitung dalam proyek ini juga ada di database, dan Symfony telah menutup komunikasi dengan database, dan pengecualian kedua menghentikan seluruh proses tanpa ada peluang untuk melakukan offset.

Layanan terhenti selama beberapa waktu - untungnya, dengan Kafka hal ini tidak terlalu buruk, karena pesannya tetap ada. Ketika pekerjaan dipulihkan, Anda dapat menyelesaikan membacanya. Itu nyaman.

Kafka memiliki kemampuan untuk mengatur offset sewenang-wenang melalui perkakas. Tetapi untuk melakukan ini, Anda harus menghentikan semua konsumen - dalam kasus kami, siapkan rilis terpisah di mana tidak akan ada konsumen, pemindahan. Kemudian di Kafka Anda dapat menggeser offset melalui perkakas, dan pesan akan terkirim.

Nuansa lain - log replikasi vs rdkafka.so - terkait dengan spesifikasi proyek kami. Kami menggunakan PHP, dan di PHP, sebagai aturan, semua perpustakaan berkomunikasi dengan Kafka melalui repositori rdkafka.so, dan kemudian ada semacam pembungkus. Mungkin ini adalah kesulitan pribadi kita, namun ternyata membaca kembali sebagian dari apa yang sudah kita baca tidaklah mudah. Secara umum, ada masalah perangkat lunak.

Kembali ke spesifik bekerja dengan partisi, itu tertulis langsung di dokumentasi konsumen >= partisi topik. Tapi saya mengetahuinya lebih lambat dari yang saya inginkan. Jika Anda ingin menskalakan dan memiliki dua konsumen, Anda memerlukan setidaknya dua partisi. Artinya, jika Anda memiliki satu partisi yang berisi 20 ribu pesan, dan Anda membuat partisi baru, jumlah pesan tidak akan segera disamakan. Oleh karena itu, untuk memiliki dua konsumen paralel, Anda perlu berurusan dengan partisi.

Pemantauan

Saya pikir cara kita memantaunya akan semakin jelas masalah apa saja yang ada pada pendekatan yang ada.

Misalnya, kami menghitung berapa banyak produk dalam database yang baru-baru ini mengubah statusnya, dan karenanya, peristiwa seharusnya terjadi berdasarkan perubahan ini, dan kami mengirimkan nomor ini ke sistem pemantauan kami. Kemudian dari Kafka kita mendapatkan angka kedua, berapa sebenarnya kejadian yang tercatat. Jelasnya, selisih kedua angka ini harus selalu nol.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Selain itu, Anda perlu memantau bagaimana kinerja produsen, apakah bus peristiwa menerima pesan, dan bagaimana kinerja konsumen. Misalnya, pada grafik di bawah, Alat Pengembalian Dana berfungsi dengan baik, namun BOB jelas memiliki beberapa masalah (puncak biru).

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Saya telah menyebutkan kelambatan kelompok konsumen. Secara kasar, ini adalah jumlah pesan yang belum dibaca. Secara umum, konsumen kami bekerja dengan cepat, sehingga lag biasanya 0, namun terkadang dapat terjadi puncak dalam jangka pendek. Kafka dapat melakukan ini secara langsung, tetapi Anda perlu menetapkan interval tertentu.

Ada sebuah proyek Liangyang akan memberi Anda informasi lebih lanjut tentang Kafka. Ini hanya menggunakan API grup konsumen untuk memberikan status tentang kinerja grup ini. Selain OK dan Gagal, ada peringatan, dan Anda mungkin mengetahui bahwa konsumen Anda tidak dapat mengatasi laju produksi - mereka tidak punya waktu untuk mengoreksi apa yang tertulis. Sistem ini cukup cerdas dan mudah digunakan.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Seperti inilah respons API. Ini grup bob-live-fifa, partisi refund.update.v1, status OK, lag 0 - offset akhir terakhir ini dan itu.

Pengalaman dalam mengembangkan layanan Alat Pengembalian Dana dengan API asinkron di Kafka

Pemantauan diperbarui_di SLA (macet) sudah saya sebutkan. Misalnya produk sudah berubah status siap dikembalikan. Kami menginstal Cron, yang mengatakan bahwa jika dalam 5 menit objek ini belum dikembalikan (kami mengembalikan uang melalui sistem pembayaran dengan sangat cepat), maka pasti ada yang tidak beres, dan ini jelas merupakan kasus dukungan. Oleh karena itu, kita cukup mengambil Cron, yang membaca hal-hal seperti itu, dan jika lebih besar dari 0, maka ia akan mengirimkan peringatan.

Ringkasnya, menggunakan acara akan lebih mudah jika:

  • informasi dibutuhkan oleh beberapa sistem;
  • hasil pengolahan tidak penting;
  • ada beberapa acara atau acara kecil.

Tampaknya artikel tersebut memiliki topik yang sangat spesifik - API asinkron di Kafka, tetapi sehubungan dengan itu saya ingin merekomendasikan banyak hal sekaligus.
Pertama, selanjutnya HighLoad ++ kita harus menunggu hingga bulan November; pada bulan April akan ada versi St. Petersburg, dan pada bulan Juni kita akan membicarakan tentang muatan tinggi di Novosibirsk.
Kedua, penulis laporan, Sergei Zaika, adalah anggota Komite Program konferensi baru kami tentang manajemen pengetahuan PengetahuanConf. Konferensi ini berlangsung satu hari dan akan berlangsung pada tanggal 26 April, namun programnya sangat intens.
Dan itu akan terjadi pada bulan Mei PHP Rusia и RIT++ (termasuk DevOpsConf) - Anda juga dapat menyarankan topik Anda di sana, membicarakan pengalaman Anda, dan mengeluh tentang boneka kerucut Anda.

Sumber: www.habr.com

Tambah komentar