
Dalam artikel terakhir , kami memeriksa fondasi teoritis arsitektur reaktif. Saatnya berbicara tentang aliran data, cara untuk menerapkan sistem Erlang / Elixir reaktif, dan pola pesan di dalamnya:
- Minta tanggapan
- Tanggapan Chunked Permintaan
- Respon dengan Permintaan
- Publikasikan-berlangganan
- Berlangganan Terbitkan-terbalik
- Distribusi tugas
SOA, MSA, dan Pesan
SOA, MSA - arsitektur sistem yang menentukan aturan untuk membangun sistem, sementara olahpesan menyediakan primitif untuk implementasinya.
Saya tidak ingin mempromosikan arsitektur sistem pembangunan ini atau itu. Saya mendukung penggunaan praktik yang paling efektif dan bermanfaat untuk proyek dan bisnis tertentu. Apapun paradigma yang kita pilih, lebih baik membuat blok sistem dengan memperhatikan cara Unix: komponen dengan konektivitas minimal yang bertanggung jawab atas entitas individu. Metode API melakukan tindakan paling sederhana dengan entitas.
Pesan - sesuai namanya, adalah perantara pesan. Tujuan utamanya adalah untuk menerima dan memberikan pesan. Dia bertanggung jawab atas antarmuka untuk mengirim informasi, pembentukan saluran logis untuk mentransmisikan informasi dalam sistem, perutean dan penyeimbangan, serta pemrosesan kegagalan di tingkat sistem.
Olahpesan yang dikembangkan tidak mencoba untuk bersaing atau mengganti rabbitmq. Fitur utamanya:
- Distribusi
Poin pertukaran dapat dibuat pada semua node cluster, sedekat mungkin dengan kode yang menggunakannya. - Kesederhanaan.
Fokus pada meminimalkan kode boilerplate dan kegunaan. - Performa terbaik.
Kami tidak mencoba mengulangi fungsionalitas rabbitmq, tetapi hanya menyoroti lapisan arsitektur dan transportasi, yang sesederhana mungkin dalam OTP, meminimalkan biaya. - Fleksibilitas.
Setiap layanan dapat menggabungkan banyak templat pertukaran. - Toleransi kesalahan yang melekat dalam desain.
- Skalabilitas.
Perpesanan tumbuh bersama aplikasi. Saat beban meningkat, Anda dapat memindahkan titik pertukaran ke masing-masing mesin.
Komentar. Dari sudut pandang organisasi kode, meta-proyek sangat cocok untuk sistem yang kompleks dengan Erlang / Elixir. Semua kode proyek dalam satu repositori - proyek payung. Pada saat yang sama, layanan-layanan mikroseperti terisolasi mungkin dan melakukan operasi sederhana yang bertanggung jawab atas entitas yang terpisah. Dengan pendekatan ini, mudah untuk mendukung API dari seluruh sistem, cukup membuat perubahan, lebih mudah untuk menulis unit dan tes integrasi.
Komponen sistem berinteraksi secara langsung atau melalui broker. Dari perspektif perpesanan, setiap layanan memiliki beberapa fase kehidupan:
- Inisialisasi layanan.
Pada tahap ini, konfigurasi dan peluncuran proses pelaksanaan layanan dan dependensi berlangsung. - Menciptakan titik pertukaran.
Layanan dapat menggunakan titik pertukaran statis yang ditentukan dalam konfigurasi simpul, atau membuat titik pertukaran secara dinamis. - Pendaftaran layanan.
Agar layanan dapat memenuhi permintaan layanan, layanan harus didaftarkan pada titik pertukaran. - Berfungsi normal.
Layanan menghasilkan pekerjaan yang bermanfaat. - Shutdown.
Ada 2 jenis shutdown: reguler dan darurat. Dengan layanan reguler, ia terputus dari titik pertukaran dan berhenti. Dalam kasus darurat, olahpesan menjalankan salah satu skenario failover.
Kelihatannya cukup rumit, tetapi tidak semuanya menakutkan dalam kode. Contoh kode dengan komentar akan diberikan dalam analisis template beberapa saat kemudian.
Pertukaran
Pertukaran poin adalah proses pengiriman pesan yang mengimplementasikan logika berinteraksi dengan komponen-komponen dalam templat pengiriman pesan. Dalam semua contoh di bawah ini, komponen berinteraksi melalui titik pertukaran, kombinasi yang membentuk pesan.
Pola pertukaran pesan (MEP)
Secara global, pola berbagi dapat dibagi menjadi dua arah dan satu arah. Yang pertama menyiratkan respon terhadap pesan yang diterima, yang terakhir tidak. Contoh klasik dari pola dua arah dalam arsitektur client-server adalah pola Request-response. Pertimbangkan templat dan modifikasinya.
Permintaan - respons atau RPC
RPC digunakan ketika kita perlu mendapatkan respons dari proses lain. Proses ini dapat diluncurkan di situs yang sama atau berlokasi di benua yang berbeda. Di bawah ini adalah diagram interaksi klien dan server melalui olahpesan.

Karena pengiriman pesan sama sekali tidak sinkron, untuk klien pertukaran dibagi menjadi 2 fase:
Permintaan Pengajuan
messaging:request(Exchange, ResponseMatchingTag, RequestDefinition, HandlerProcess).
Exchange - nama unik untuk titik pertukaran
ResponseMatchingTag - Label lokal untuk menangani respons. Misalnya, dalam hal mengirim beberapa permintaan identik milik pengguna yang berbeda.
RequestDefinition - tubuh permintaan
HandlerProcess - PID handler. Proses ini akan menerima respons dari server.
Pemrosesan tanggapan
handle_info(#'$msg'{exchange = EXCHANGE, tag = ResponseMatchingTag,message = ResponsePayload}, State)
ResponsePayload - respons server.
Untuk server, prosesnya juga terdiri dari 2 fase:
- Inisialisasi Titik Tukar
- Memproses permintaan masuk
Mari ilustrasikan template ini dengan kode. Misalkan kita perlu mengimplementasikan layanan sederhana yang menyediakan satu-satunya metode waktu yang tepat.
Kode server
Keluarkan definisi API layanan di api.hrl:
Tentukan pengontrol layanan di time_controller.erl
Kode pelanggan
Untuk mengirim permintaan ke layanan, Anda dapat memanggil API permintaan perpesanan di mana saja di klien:
case messaging:request(?EXCHANGE, tag, #time_req{opts = #{}}, self()) of ok -> ok; _ ->
Dalam sistem terdistribusi, konfigurasi komponen bisa sangat berbeda dan pada saat pesan permintaan belum dimulai, atau pengontrol layanan tidak akan siap untuk melayani permintaan. Karena itu, kita perlu memeriksa respons pesan dan menangani kasus kegagalan.
Setelah pengiriman berhasil, klien akan menerima respons atau kesalahan dari layanan.
Tangani kedua kasus di handle_info:
handle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time{unixtime = Utime}}}, State) -> ?debugVal(Utime), {noreply, State}; handle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time_error{code = ErrorCode}}}, State) -> ?debugVal({error, ErrorCode}), {noreply, State};
Tanggapan Chunked Permintaan
Lebih baik tidak membiarkan pengiriman pesan besar. Operasi responsif dan stabil seluruh sistem tergantung pada ini. Jika respons terhadap permintaan memakan banyak memori, maka pengelompokan menjadi beberapa bagian adalah wajib.

Berikut adalah beberapa contoh kasus tersebut:
- Komponen bertukar data biner, seperti file. Membagi jawaban menjadi bagian-bagian kecil membantu Anda bekerja secara efisien dengan file dari berbagai ukuran dan tidak menangkap kelebihan memori.
- Daftar. Sebagai contoh, kita perlu memilih semua catatan dari tabel besar dalam database dan mentransfernya ke komponen lain.
Saya menyebut jawaban ini lokomotif. Bagaimanapun, 1024 1 MB pesan lebih baik daripada satu pesan 1 GB.
Di kluster Erlang, kami mendapatkan keuntungan tambahan - mengurangi beban pada titik pertukaran dan jaringan, karena jawaban segera dikirim ke penerima, melewati titik pertukaran.
Respon dengan Permintaan
Ini adalah modifikasi yang cukup langka dari pola RPC untuk membangun sistem interaktif.

Terbitkan-berlangganan (pohon distribusi data)
Sistem yang berorientasi pada peristiwa memberikan data kepada konsumen ketika data tersedia. Dengan demikian, sistem lebih cenderung mendorong model daripada menarik atau polling. Fitur ini memungkinkan Anda untuk tidak membuang sumber daya dengan terus-menerus bertanya dan menunggu data.
Gambar tersebut menunjukkan proses pendistribusian pesan ke konsumen yang berlangganan topik tertentu.

Contoh klasik dari penggunaan templat ini adalah distribusi negara: dunia game dalam game komputer, data pasar pertukaran, informasi yang berguna dalam umpan data.
Pertimbangkan kode pelanggan:
init(_Args) ->
Sumber dapat memanggil fungsi publikasi posting di tempat yang nyaman:
messaging:publish_message(Exchange, Key, Message).
Exchange - nama titik pertukaran,
Kunci - kunci perutean
Pesan - payload
Berlangganan Terbitkan-terbalik

Dengan memperluas pub-sub, Anda bisa mendapatkan pola yang nyaman untuk masuk. Himpunan sumber dan konsumen bisa sangat berbeda. Angka tersebut menunjukkan kasus dengan satu konsumen dan banyak sumber.
Pola distribusi tugas
Di hampir setiap proyek, tugas pemrosesan yang ditangguhkan muncul, seperti menghasilkan laporan, mengirimkan pemberitahuan, menerima data dari sistem pihak ketiga. Throughput suatu sistem yang melakukan tugas-tugas ini mudah diukur dengan menambahkan penangan. Yang tersisa bagi kami adalah membentuk sekelompok penangan dan mendistribusikan tugas secara merata di antara mereka.
Pertimbangkan situasi yang muncul dengan contoh 3 penangan. Bahkan pada tahap pendistribusian tugas, muncul pertanyaan tentang keadilan distribusi dan luapan para penangan. Distribusi round-robin akan bertanggung jawab atas keadilan, dan untuk menghindari situasi luapan penangan, kami memperkenalkan batasan prefetch_limit . Dalam mode sementara, prefetch_limit akan mencegah satu penangan menerima semua tugas.
Pesan mengelola antrian dan prioritas pemrosesan. Penangan menerima tugas saat tersedia. Tugas mungkin berhasil atau gagal:
messaging:ack(Tack)
- dipanggil jika pemrosesan pesan berhasilmessaging:nack(Tack)
- dipanggil dalam semua situasi darurat. Setelah tugas kembali, perpesanan akan mentransfernya ke penangan lain.

Misalkan kegagalan kompleks terjadi selama pemrosesan tiga tugas: penangan 1, setelah menerima tugas, jatuh sebelum dapat berkomunikasi ke titik pertukaran. Dalam hal ini, titik pertukaran setelah batas waktu ack berakhir akan mentransfer pekerjaan ke penangan lain. Handler 3 karena alasan tertentu meninggalkan tugas dan mengirim nack, sebagai akibatnya, tugas tersebut juga diteruskan ke handler lain yang berhasil menyelesaikannya.
Hasil awal
Kami membongkar blok bangunan dasar dari sistem terdistribusi dan mendapatkan pemahaman dasar tentang aplikasi mereka di Erlang / Elixir.
Dengan menggabungkan pola-pola dasar, Anda dapat membangun paradigma kompleks untuk menyelesaikan masalah yang muncul.
Pada bagian akhir dari siklus, kami akan mempertimbangkan masalah umum dari organisasi layanan, perutean dan penyeimbangan, dan juga berbicara tentang sisi praktis skalabilitas dan toleransi kesalahan sistem.
Akhir dari bagian kedua.
Foto Marius Christensen
Ilustrasi disiapkan oleh web berikutnyaencediagrams.com