Pada masa lalu
- Permintaan-tindak balas
- Respon Dipotong Permintaan
- Balas dengan Permintaan
- Terbit-langgan
- Terbitan terbalik-langganan
- Pengagihan tugas
SOA, MSA dan Pemesejan
SOA, MSA ialah seni bina sistem yang mentakrifkan peraturan untuk membina sistem, manakala pemesejan menyediakan primitif untuk pelaksanaannya.
Saya tidak mahu mempromosikan seni bina sistem ini atau itu. Saya menggunakan amalan yang paling berkesan dan berguna untuk projek dan perniagaan tertentu. Walau apa pun paradigma yang kita pilih, adalah lebih baik untuk mencipta blok sistem dengan memerhatikan Unix-way: komponen dengan ketersambungan minimum, bertanggungjawab untuk entiti individu. Kaedah API melakukan tindakan paling mudah yang mungkin dengan entiti.
Pemesejan adalah, seperti namanya, broker mesej. Tujuan utamanya adalah untuk menerima dan menghantar mesej. Ia bertanggungjawab untuk antara muka untuk menghantar maklumat, pembentukan saluran logik untuk menghantar maklumat dalam sistem, penghalaan dan pengimbangan, serta pengendalian kerosakan pada peringkat sistem.
Pemesejan yang kami bangunkan bukanlah cuba untuk bersaing dengan atau menggantikan rabbitmq. Ciri-ciri utamanya:
- Pengagihan.
Mata pertukaran boleh dibuat pada semua nod kluster, sedekat mungkin dengan kod yang menggunakannya. - Kesederhanaan.
Fokus pada meminimumkan kod boilerplate dan kemudahan penggunaan. - Persembahan terbaik.
Kami tidak cuba mengulangi fungsi rabbitmq, tetapi hanya menyerlahkan lapisan seni bina dan pengangkutan, yang kami muatkan ke dalam OTP semudah mungkin, meminimumkan kos. - Kelenturan.
Setiap perkhidmatan boleh menggabungkan banyak templat pertukaran. - Ketahanan mengikut reka bentuk.
- Kebolehskalaan.
Pemesejan berkembang dengan aplikasi. Apabila beban bertambah, anda boleh mengalihkan mata pertukaran ke mesin individu.
Komen. Dari segi organisasi kod, projek meta sangat sesuai untuk sistem Erlang/Elixir yang kompleks. Semua kod projek terletak dalam satu repositori - projek payung. Pada masa yang sama, perkhidmatan mikro diasingkan secara maksimum dan melaksanakan operasi mudah yang bertanggungjawab untuk entiti yang berasingan. Dengan pendekatan ini, ia adalah mudah untuk mengekalkan API keseluruhan sistem, ia adalah mudah untuk membuat perubahan, ia adalah mudah untuk menulis unit dan ujian integrasi.
Komponen sistem berinteraksi secara langsung atau melalui broker. Dari perspektif pemesejan, setiap perkhidmatan mempunyai beberapa fasa hayat:
- Inisialisasi perkhidmatan.
Pada peringkat ini, proses dan kebergantungan yang melaksanakan perkhidmatan dikonfigurasikan dan dilancarkan. - Mencipta titik pertukaran.
Perkhidmatan ini boleh menggunakan titik pertukaran statik yang ditentukan dalam konfigurasi nod, atau mencipta titik pertukaran secara dinamik. - Pendaftaran perkhidmatan.
Agar perkhidmatan dapat melayani permintaan, ia mesti didaftarkan di tempat pertukaran. - Berfungsi normal.
Perkhidmatan ini menghasilkan kerja yang berguna. - Penyelesaian kerja.
Terdapat 2 jenis penutupan yang mungkin: biasa dan kecemasan. Semasa operasi biasa, perkhidmatan diputuskan sambungan dari titik pertukaran dan berhenti. Dalam situasi kecemasan, pemesejan melaksanakan salah satu skrip failover.
Ia kelihatan agak rumit, tetapi kod itu tidak begitu menakutkan. Contoh kod dengan ulasan akan diberikan dalam analisis templat sedikit kemudian.
Pertukaran
Titik pertukaran ialah proses pemesejan yang melaksanakan logik interaksi dengan komponen dalam templat pemesejan. Dalam semua contoh yang dibentangkan di bawah, komponen berinteraksi melalui titik pertukaran, yang gabungannya membentuk pemesejan.
Corak pertukaran mesej (MEP)
Di peringkat global, corak pertukaran boleh dibahagikan kepada dua hala dan sehala. Yang pertama membayangkan tindak balas kepada mesej masuk, yang kedua tidak. Contoh klasik corak dua hala dalam seni bina pelayan-pelanggan ialah corak Permintaan-tindak balas. Mari lihat templat dan pengubahsuaiannya.
Permintaan-tindak balas atau RPC
RPC digunakan apabila kita perlu menerima respons daripada proses lain. Proses ini mungkin berjalan pada nod yang sama atau terletak di benua yang berbeza. Di bawah ialah gambar rajah interaksi antara klien dan pelayan melalui pemesejan.
Oleh kerana pemesejan adalah tidak segerak sepenuhnya, untuk pelanggan pertukaran dibahagikan kepada 2 fasa:
-
menghantar permintaan
messaging:request(Exchange, ResponseMatchingTag, RequestDefinition, HandlerProcess).
Pasaran Kripto β nama unik tempat pertukaran
ResponseMatchingTag β label tempatan untuk memproses tindak balas. Sebagai contoh, dalam kes menghantar beberapa permintaan yang sama kepunyaan pengguna yang berbeza.
RequestDefinition - badan permintaan
Proses Pengendali β PID pengendali. Proses ini akan menerima respons daripada pelayan. -
Memproses tindak balas
handle_info(#'$msg'{exchange = EXCHANGE, tag = ResponseMatchingTag,message = ResponsePayload}, State)
ResponsePayload - tindak balas pelayan.
Untuk pelayan, proses ini juga terdiri daripada 2 fasa:
- Memulakan titik pertukaran
- Memproses permintaan yang diterima
Mari kita ilustrasikan templat ini dengan kod. Katakan kita perlu melaksanakan perkhidmatan ringkas yang menyediakan satu kaedah masa tepat.
Kod pelayan
Mari kita tentukan API perkhidmatan dalam api.hrl:
%% =====================================================
%% entities
%% =====================================================
-record(time, {
unixtime :: non_neg_integer(),
datetime :: binary()
}).
-record(time_error, {
code :: non_neg_integer(),
error :: term()
}).
%% =====================================================
%% methods
%% =====================================================
-record(time_req, {
opts :: term()
}).
-record(time_resp, {
result :: #time{} | #time_error{}
}).
Mari kita takrifkan pengawal perkhidmatan dalam time_controller.erl
%% Π ΠΏΡΠΈΠΌΠ΅ΡΠ΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ ΡΠΎΠ»ΡΠΊΠΎ Π·Π½Π°ΡΠΈΠΌΡΠΉ ΠΊΠΎΠ΄. ΠΡΡΠ°Π²ΠΈΠ² Π΅Π³ΠΎ Π² ΡΠ°Π±Π»ΠΎΠ½ gen_server ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΡΠ°Π±ΠΎΡΠΈΠΉ ΡΠ΅ΡΠ²ΠΈΡ.
%% ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ gen_server
init(Args) ->
%% ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊ ΡΠΎΡΠΊΠ΅ ΠΎΠ±ΠΌΠ΅Π½Π°
messaging:monitor_exchange(req_resp, ?EXCHANGE, default, self())
{ok, #{}}.
%% ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΡΠΎΠ±ΡΡΠΈΡ ΠΏΠΎΡΠ΅ΡΠΈ ΡΠ²ΡΠ·ΠΈ Ρ ΡΠΎΡΠΊΠΎΠΉ ΠΎΠ±ΠΌΠ΅Π½Π°. ΠΡΠΎ ΠΆΠ΅ ΡΠΎΠ±ΡΡΠΈΠ΅ ΠΏΡΠΈΡ
ΠΎΠ΄ΠΈΡ, Π΅ΡΠ»ΠΈ ΡΠΎΡΠΊΠ° ΠΎΠ±ΠΌΠ΅Π½Π° Π΅ΡΠ΅ Π½Π΅ Π·Π°ΠΏΡΡΡΠΈΠ»Π°ΡΡ.
handle_info(#exchange_die{exchange = ?EXCHANGE}, State) ->
erlang:send(self(), monitor_exchange),
{noreply, State};
%% ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠ° API
handle_info(#time_req{opts = _Opts}, State) ->
messaging:response_once(Client, #time_resp{
result = #time{ unixtime = time_utils:unixtime(now()), datetime = time_utils:iso8601_fmt(now())}
});
{noreply, State};
%% Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΠ΅ ΡΠ°Π±ΠΎΡΡ gen_server
terminate(_Reason, _State) ->
messaging:demonitor_exchange(req_resp, ?EXCHANGE, default, self()),
ok.
Kod pelanggan
Untuk menghantar permintaan kepada perkhidmatan, anda boleh menghubungi API permintaan pemesejan di mana-mana dalam klien:
case messaging:request(?EXCHANGE, tag, #time_req{opts = #{}}, self()) of
ok -> ok;
_ -> %% repeat or fail logic
end
Dalam sistem yang diedarkan, konfigurasi komponen boleh menjadi sangat berbeza dan pada masa permintaan, pemesejan mungkin belum bermula atau pengawal perkhidmatan tidak akan bersedia untuk memberi perkhidmatan kepada permintaan. Oleh itu, kita perlu menyemak respons pemesejan dan mengendalikan kes kegagalan.
Selepas berjaya menghantar, pelanggan akan menerima respons atau ralat daripada perkhidmatan tersebut.
Mari kita kendalikan kedua-dua kes dalam 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};
Respon Dipotong Permintaan
Adalah lebih baik untuk mengelak daripada menghantar mesej yang besar. Responsif dan operasi stabil keseluruhan sistem bergantung pada ini. Jika respons kepada pertanyaan mengambil banyak memori, maka membahagikannya kepada beberapa bahagian adalah wajib.
Biar saya berikan anda beberapa contoh kes sedemikian:
- Komponen bertukar data binari, seperti fail. Memecahkan respons kepada bahagian kecil membantu anda bekerja dengan cekap dengan fail dalam sebarang saiz dan mengelakkan limpahan memori.
- Penyenaraian. Sebagai contoh, kita perlu memilih semua rekod daripada jadual besar dalam pangkalan data dan memindahkannya ke komponen lain.
Saya memanggil tindak balas ini lokomotif. Walau apa pun, 1024 mesej 1 MB adalah lebih baik daripada satu mesej 1 GB.
Dalam gugusan Erlang, kami mendapat faedah tambahan - mengurangkan beban pada titik pertukaran dan rangkaian, memandangkan respons segera dihantar kepada penerima, memintas titik pertukaran.
Balas dengan Permintaan
Ini adalah pengubahsuaian yang agak jarang berlaku pada corak RPC untuk membina sistem dialog.
Terbit-langganan (pokok pengedaran data)
Sistem dipacu acara menyampaikannya kepada pengguna sebaik sahaja data sedia. Oleh itu, sistem lebih cenderung kepada model tolak daripada model tarik atau tinjauan pendapat. Ciri ini membolehkan anda mengelakkan pembaziran sumber dengan sentiasa meminta dan menunggu data.
Rajah menunjukkan proses pengedaran mesej kepada pengguna yang melanggan topik tertentu.
Contoh klasik penggunaan corak ini ialah pengedaran keadaan: dunia permainan dalam permainan komputer, data pasaran di bursa, maklumat berguna dalam suapan data.
Mari lihat kod pelanggan:
init(_Args) ->
%% ΠΏΠΎΠ΄ΠΏΠΈΡΡΠ²Π°Π΅ΠΌΡΡ Π½Π° ΠΎΠ±ΠΌΠ΅Π½Π½ΠΈΠΊ, ΠΊΠ»ΡΡ = key
messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
{ok, #{}}.
handle_info(#exchange_die{exchange = ?SUBSCRIPTION}, State) ->
%% Π΅ΡΠ»ΠΈ ΡΠΎΡΠΊΠ° ΠΎΠ±ΠΌΠ΅Π½Π° Π½Π΅Π΄ΠΎΡΡΡΠΏΠ½Π°, ΡΠΎ ΠΏΡΡΠ°Π΅ΠΌΡΡ ΠΏΠ΅ΡΠ΅ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΡΡΡ
messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
{noreply, State};
%% ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅ΠΌ ΠΏΡΠΈΡΠ΅Π΄ΡΠΈΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ
handle_info(#'$msg'{exchange = ?SUBSCRIPTION, message = Msg}, State) ->
?debugVal(Msg),
{noreply, State};
%% ΠΏΡΠΈ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠ΅ ΠΏΠΎΡΡΠ΅Π±ΠΈΡΠ΅Π»Ρ - ΠΎΡΠΊΠ»ΡΡΠ°Π΅ΠΌΡΡ ΠΎΡ ΡΠΎΡΠΊΠΈ ΠΎΠ±ΠΌΠ΅Π½Π°
terminate(_Reason, _State) ->
messaging:unsubscribe(?SUBSCRIPTION, key, tag, self()),
ok.
Sumber boleh memanggil fungsi untuk menerbitkan mesej di mana-mana tempat yang mudah:
messaging:publish_message(Exchange, Key, Message).
Pasaran Kripto - nama mata pertukaran,
Utama - kunci penghalaan
Mesej Anda - muatan
Terbitan terbalik-langganan
Dengan mengembangkan pub-sub, anda boleh mendapatkan corak yang mudah untuk pengelogan. Set sumber dan pengguna boleh berbeza sama sekali. Angka tersebut menunjukkan kes dengan satu pengguna dan berbilang sumber.
Corak pengagihan tugas
Hampir setiap projek melibatkan tugas pemprosesan tertunda, seperti menjana laporan, menyampaikan pemberitahuan dan mendapatkan semula data daripada sistem pihak ketiga. Daya tampung sistem yang melaksanakan tugasan ini boleh ditingkatkan dengan mudah dengan menambahkan pengendali. Apa yang tinggal untuk kami ialah membentuk sekumpulan pemproses dan mengagihkan tugas secara sama rata di antara mereka.
Mari kita lihat situasi yang timbul menggunakan contoh 3 pengendali. Malah pada peringkat pengagihan tugas, persoalan keadilan pengagihan dan limpahan pengendali timbul. Pengedaran round-robin akan bertanggungjawab ke atas keadilan, dan untuk mengelakkan situasi limpahan pengendali, kami akan memperkenalkan sekatan prefetch_limit. Dalam keadaan sementara prefetch_limit akan menghalang seorang pengendali daripada menerima semua tugasan.
Pemesejan menguruskan baris gilir dan keutamaan pemprosesan. Pemproses menerima tugas apabila mereka tiba. Tugas boleh diselesaikan dengan jayanya atau gagal:
messaging:ack(Tack)
- dipanggil jika mesej berjaya diprosesmessaging:nack(Tack)
- dipanggil dalam semua situasi kecemasan. Sebaik sahaja tugasan dikembalikan, pemesejan akan menyampaikannya kepada pengendali lain.
Katakan kegagalan kompleks berlaku semasa memproses tiga tugas: pemproses 1, selepas menerima tugasan, ranap tanpa sempat melaporkan apa-apa ke tempat pertukaran. Dalam kes ini, titik pertukaran akan memindahkan tugas kepada pengendali lain selepas tamat masa ack telah tamat tempoh. Atas sebab tertentu, pengendali 3 telah meninggalkan tugasan dan menghantar nack; akibatnya, tugasan itu turut dipindahkan kepada pengendali lain yang berjaya menyelesaikannya.
Ringkasan awal
Kami telah merangkumi blok binaan asas sistem teragih dan mendapat pemahaman asas tentang penggunaannya dalam Erlang/Elixir.
Dengan menggabungkan corak asas, anda boleh membina paradigma yang kompleks untuk menyelesaikan masalah yang timbul.
Di bahagian akhir siri ini, kita akan melihat isu umum mengatur perkhidmatan, penghalaan dan pengimbangan, dan juga bercakap tentang sisi praktikal kebolehskalaan dan toleransi kesalahan sistem.
Akhir bahagian kedua.
Photo
Ilustrasi disediakan menggunakan websequencediagrams.com
Sumber: www.habr.com