Mga bloke ng gusali ng mga ipinamamahaging aplikasyon. Pangalawang approximation

Anunsyo

Mga kasamahan, sa kalagitnaan ng tag-araw, plano kong maglabas ng isa pang serye ng mga artikulo sa disenyo ng mga sistema ng pagpila: "Ang Eksperimento ng VTrade" - isang pagtatangka na magsulat ng isang balangkas para sa mga sistema ng pangangalakal. Susuriin ng serye ang teorya at kasanayan ng pagbuo ng isang palitan, auction at tindahan. Sa dulo ng artikulo, inaanyayahan kita na bumoto para sa mga paksang pinaka-interesante sa iyo.

Mga bloke ng gusali ng mga ipinamamahaging aplikasyon. Pangalawang approximation

Ito ang huling artikulo sa serye tungkol sa mga ipinamahagi na reaktibong aplikasyon sa Erlang/Elixir. SA unang artikulo mahahanap mo ang teoretikal na pundasyon ng reaktibong arkitektura. Pangalawang artikulo inilalarawan ang mga pangunahing pattern at mekanismo para sa pagbuo ng mga naturang sistema.

Ngayon ay magtataas kami ng mga isyu sa pagbuo ng base ng code at mga proyekto sa pangkalahatan.

Organisasyon ng mga serbisyo

Sa totoong buhay, kapag bumubuo ng isang serbisyo, madalas mong kailangang pagsamahin ang ilang mga pattern ng pakikipag-ugnayan sa isang controller. Halimbawa, ang serbisyo ng mga gumagamit, na lumulutas sa problema sa pamamahala ng mga profile ng user ng proyekto, ay dapat tumugon sa mga kahilingan sa req-resp at mag-ulat ng mga update sa profile sa pamamagitan ng pub-sub. Ang kasong ito ay medyo simple: sa likod ng pagmemensahe ay may isang controller na nagpapatupad ng lohika ng serbisyo at nag-publish ng mga update.

Ang sitwasyon ay nagiging mas kumplikado kapag kailangan nating ipatupad ang isang fault-tolerant na ipinamamahaging serbisyo. Isipin natin na ang mga kinakailangan para sa mga user ay nagbago:

  1. ngayon ang serbisyo ay dapat magproseso ng mga kahilingan sa 5 cluster node,
  2. magagawang magsagawa ng mga gawain sa pagproseso sa background,
  3. at magagawang dynamic na pamahalaan ang mga listahan ng subscription para sa mga update sa profile.

Komento: Hindi namin isinasaalang-alang ang isyu ng pare-parehong storage at pagkopya ng data. Ipagpalagay natin na ang mga isyung ito ay nalutas nang mas maaga at ang system ay mayroon nang maaasahan at nasusukat na layer ng imbakan, at ang mga humahawak ay may mga mekanismo upang makipag-ugnayan dito.

Ang pormal na paglalarawan ng serbisyo ng mga gumagamit ay naging mas kumplikado. Mula sa pananaw ng isang programmer, ang mga pagbabago ay minimal dahil sa paggamit ng pagmemensahe. Upang matugunan ang unang kinakailangan, kailangan naming i-configure ang pagbabalanse sa req-resp exchange point.

Ang kinakailangan upang iproseso ang mga gawain sa background ay madalas na nangyayari. Sa mga user, ito ay maaaring pagsuri sa mga dokumento ng user, pagproseso ng na-download na multimedia, o pag-synchronize ng data sa social media. mga network. Ang mga gawaing ito ay kailangang maipamahagi sa loob ng cluster at ang pag-usad ng pagpapatupad ay sinusubaybayan. Samakatuwid, mayroon kaming dalawang pagpipilian sa solusyon: gamitin ang template ng pamamahagi ng gawain mula sa nakaraang artikulo, o, kung hindi ito angkop, sumulat ng custom na scheduler ng gawain na mamamahala sa pool ng mga processor sa paraang kailangan namin.

Ang punto 3 ay nangangailangan ng extension ng pub-sub na template. At para sa pagpapatupad, pagkatapos gumawa ng pub-sub exchange point, kailangan din naming ilunsad ang controller ng puntong ito sa loob ng aming serbisyo. Kaya, para bang inililipat namin ang lohika para sa pagproseso ng mga subscription at pag-unsubscribe mula sa layer ng pagmemensahe patungo sa pagpapatupad ng mga user.

Bilang resulta, ipinakita ng decomposition ng problema na upang matugunan ang mga kinakailangan, kailangan naming maglunsad ng 5 mga pagkakataon ng serbisyo sa iba't ibang mga node at lumikha ng karagdagang entity - isang pub-sub controller, na responsable para sa subscription.
Para magpatakbo ng 5 handler, hindi mo kailangang baguhin ang service code. Ang tanging karagdagang aksyon ay ang pagse-set up ng mga panuntunan sa pagbabalanse sa exchange point, na pag-uusapan natin sa ibang pagkakataon.
Mayroon ding karagdagang kumplikado: ang pub-sub controller at custom na task scheduler ay dapat gumana sa isang kopya. Muli, ang serbisyo sa pagmemensahe, bilang pangunahing isa, ay dapat magbigay ng isang mekanismo para sa pagpili ng isang pinuno.

Pinili ng Pinuno

Sa mga distributed system, ang halalan ng pinuno ay ang pamamaraan para sa paghirang ng isang proseso na responsable para sa pag-iskedyul ng ibinahagi na pagproseso ng ilang load.

Sa mga system na hindi madaling kapitan ng sentralisasyon, ginagamit ang mga unibersal at consensus-based na algorithm, gaya ng paxos o raft.
Dahil ang pagmemensahe ay isang broker at isang pangunahing elemento, alam nito ang tungkol sa lahat ng mga controllers ng serbisyo - mga lider ng kandidato. Ang pagmemensahe ay maaaring magtalaga ng pinuno nang walang pagboto.

Pagkatapos magsimula at kumonekta sa exchange point, lahat ng serbisyo ay makakatanggap ng mensahe ng system #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Kung LeaderPid sumasabay sa pid kasalukuyang proseso, ito ay itinalaga bilang pinuno, at ang listahan Servers kasama ang lahat ng mga node at ang kanilang mga parameter.
Sa sandaling may lalabas na bago at nakadiskonekta ang isang gumaganang cluster node, natatanggap ng lahat ng mga service controller #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} ΠΈ #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} ayon sa pagkakabanggit.

Sa ganitong paraan, alam ng lahat ng bahagi ang lahat ng pagbabago, at ang cluster ay garantisadong magkakaroon ng isang lider sa anumang oras.

Mga tagapamagitan

Upang ipatupad ang mga kumplikadong ibinahagi na proseso ng pagproseso, pati na rin sa mga problema sa pag-optimize ng isang umiiral na arkitektura, maginhawang gumamit ng mga tagapamagitan.
Upang hindi mabago ang code ng serbisyo at malutas, halimbawa, ang mga problema ng karagdagang pagproseso, pagruruta o pag-log ng mga mensahe, maaari mong paganahin ang isang proxy handler bago ang serbisyo, na gagawa ng lahat ng karagdagang gawain.

Ang isang klasikong halimbawa ng pub-sub optimization ay isang distributed na application na may business core na bumubuo ng update event, gaya ng mga pagbabago sa presyo sa market, at isang access layer - N server na nagbibigay ng websocket API para sa mga web client.
Kung magpasya ka nang direkta, magiging ganito ang serbisyo sa customer:

  • ang kliyente ay nagtatatag ng mga koneksyon sa platform. Sa gilid ng server na nagwawakas sa trapiko, isang proseso ang inilulunsad upang maserbisyuhan ang koneksyong ito.
  • Sa konteksto ng proseso ng serbisyo, nagaganap ang pahintulot at subscription sa mga update. Tinatawag ng proseso ang paraan ng pag-subscribe para sa mga paksa.
  • Sa sandaling nabuo ang isang kaganapan sa kernel, ihahatid ito sa mga prosesong nagseserbisyo sa mga koneksyon.

Isipin natin na mayroon tayong 50000 subscriber sa paksang "balita". Ang mga subscriber ay ibinahagi nang pantay-pantay sa 5 server. Bilang resulta, ang bawat pag-update, pagdating sa exchange point, ay gagawing 50000 beses: 10000 beses sa bawat server, ayon sa bilang ng mga subscriber dito. Hindi isang napaka-epektibong pamamaraan, tama?
Para mapabuti ang sitwasyon, magpakilala tayo ng proxy na may parehong pangalan sa exchange point. Dapat maibalik ng global name registrar ang pinakamalapit na proseso ayon sa pangalan, ito ay mahalaga.

Ilunsad natin ang proxy na ito sa mga server ng access layer, at lahat ng ating prosesong naghahatid sa websocket api ay magsu-subscribe dito, at hindi sa orihinal na pub-sub exchange point sa kernel. Nagsu-subscribe ang Proxy sa core lamang sa kaso ng isang natatanging subscription at ginagaya ang papasok na mensahe sa lahat ng mga subscriber nito.
Bilang resulta, 5 mensahe ang ipapadala sa pagitan ng kernel at access server, sa halip na 50000.

Pagruruta at pagbabalanse

Req-Resp

Sa kasalukuyang pagpapatupad ng pagmemensahe, mayroong 7 diskarte sa pamamahagi ng kahilingan:

  • default. Ang kahilingan ay ipinadala sa lahat ng mga controllers.
  • round-robin. Ang mga kahilingan ay binibilang at paikot na ipinamamahagi sa pagitan ng mga controller.
  • consensus. Ang mga controllers na nagsisilbi sa serbisyo ay nahahati sa mga pinuno at alipin. Ang mga kahilingan ay ipinapadala lamang sa pinuno.
  • consensus & round-robin. Ang grupo ay may pinuno, ngunit ang mga kahilingan ay ipinamamahagi sa lahat ng miyembro.
  • sticky. Ang hash function ay kinakalkula at itinalaga sa isang partikular na handler. Ang mga kasunod na kahilingan na may ganitong lagda ay mapupunta sa parehong tagapangasiwa.
  • sticky-fun. Kapag sinisimulan ang exchange point, ang hash na pagkalkula function para sa sticky pagbabalanse.
  • fun. Katulad ng sticky-fun, ikaw lang ang maaaring mag-redirect, tanggihan, o paunang iproseso ito.

Itinakda ang diskarte sa pamamahagi kapag sinimulan ang exchange point.

Bilang karagdagan sa pagbabalanse, nagbibigay-daan sa iyo ang pagmemensahe na mag-tag ng mga entity. Tingnan natin ang mga uri ng mga tag sa system:

  • Tag ng koneksyon. Binibigyang-daan kang maunawaan kung saan nagmula ang mga kaganapan. Ginagamit kapag ang isang proseso ng controller ay kumokonekta sa parehong exchange point, ngunit may iba't ibang mga routing key.
  • Tag ng serbisyo. Binibigyang-daan kang pagsamahin ang mga humahawak sa mga grupo para sa isang serbisyo at palawakin ang mga kakayahan sa pagruruta at pagbabalanse. Para sa pattern ng req-resp, linear ang pagruruta. Nagpapadala kami ng kahilingan sa exchange point, pagkatapos ay ipinapasa ito sa serbisyo. Ngunit kung kailangan nating hatiin ang mga humahawak sa mga lohikal na grupo, tapos ang paghahati gamit ang mga tag. Kapag tumutukoy ng tag, ipapadala ang kahilingan sa isang partikular na pangkat ng mga controller.
  • Humiling ng tag. Nagbibigay-daan sa iyo na makilala ang mga sagot. Dahil asynchronous ang aming system, upang maproseso ang mga tugon sa serbisyo kailangan naming tumukoy ng RequestTag kapag nagpapadala ng kahilingan. Mula rito ay mauunawaan natin ang sagot kung aling kahilingan ang dumating sa atin.

Pub-sub

Para sa pub-sub ang lahat ay medyo mas simple. Mayroon kaming exchange point kung saan nai-publish ang mga mensahe. Ang exchange point ay namamahagi ng mga mensahe sa mga subscriber na nag-subscribe sa mga routing key na kailangan nila (masasabi nating ito ay kahalintulad sa mga paksa).

Scalability at fault tolerance

Ang scalability ng system sa kabuuan ay depende sa antas ng scalability ng mga layer at mga bahagi ng system:

  • Pina-scale ang mga serbisyo sa pamamagitan ng pagdaragdag ng mga karagdagang node sa cluster na may mga humahawak para sa serbisyong ito. Sa panahon ng pagsubok na operasyon, maaari mong piliin ang pinakamainam na patakaran sa pagbabalanse.
  • Ang mismong serbisyo ng pagmemensahe sa loob ng isang hiwalay na cluster ay karaniwang nasusukat sa pamamagitan ng paglipat ng partikular na load na mga exchange point sa magkahiwalay na cluster node, o sa pamamagitan ng pagdaragdag ng mga proxy na proseso sa partikular na load na mga lugar ng cluster.
  • Ang scalability ng buong system bilang isang katangian ay nakasalalay sa flexibility ng arkitektura at ang kakayahang pagsamahin ang mga indibidwal na cluster sa isang karaniwang lohikal na entity.

Ang tagumpay ng isang proyekto ay kadalasang nakasalalay sa pagiging simple at bilis ng pag-scale. Ang pagmemensahe sa kasalukuyang bersyon nito ay lumalaki kasama ng application. Kahit kulang tayo ng cluster na 50-60 machines, we can resort to federation. Sa kasamaang palad, ang paksa ng federation ay lampas sa saklaw ng artikulong ito.

Pagpapareserba

Noong sinusuri ang load balancing, napag-usapan na namin ang redundancy ng mga service controller. Gayunpaman, dapat ding nakalaan ang pagmemensahe. Sa kaganapan ng isang node o machine crash, ang pagmemensahe ay dapat na awtomatikong mabawi, at sa pinakamaikling posibleng panahon.

Sa aking mga proyekto ay gumagamit ako ng mga karagdagang node na kumukuha ng load kung sakaling mahulog. Ang Erlang ay may karaniwang ipinamamahaging mode na pagpapatupad para sa mga OTP application. Ang distributed mode ay nagsasagawa ng pagbawi sa kaso ng pagkabigo sa pamamagitan ng paglulunsad ng nabigong application sa isa pang naunang inilunsad na node. Ang proseso ay transparent; pagkatapos ng isang pagkabigo, ang application ay awtomatikong lumilipat sa failover node. Maaari kang magbasa nang higit pa tungkol sa pagpapaandar na ito dito.

Pagiging Produktibo

Subukan nating ihambing ang pagganap ng rabbitmq at ang aming custom na pagmemensahe.
nakita ko opisyal na mga resulta rabbitmq testing mula sa openstack team.

Sa talata 6.14.1.2.1.2.2. Ipinapakita ng orihinal na dokumento ang resulta ng RPC CAST:
Mga bloke ng gusali ng mga ipinamamahaging aplikasyon. Pangalawang approximation

Hindi kami gagawa ng anumang karagdagang mga setting sa OS kernel o erlang VM nang maaga. Mga kondisyon para sa pagsubok:

  • erl opts: +A1 +sbtu.
  • Ang pagsubok sa loob ng iisang erlang node ay pinapatakbo sa isang laptop na may lumang i7 sa mobile na bersyon.
  • Isinasagawa ang mga cluster test sa mga server na may 10G network.
  • Ang code ay tumatakbo sa mga lalagyan ng docker. Network sa NAT mode.

Test code:

req_resp_bench(_) ->
  W = perftest:comprehensive(10000,
    fun() ->
      messaging:request(?EXCHANGE, default, ping, self()),
      receive
        #'$msg'{message = pong} -> ok
      after 5000 ->
        throw(timeout)
      end
    end
  ),
  true = lists:any(fun(E) -> E >= 30000 end, W),
  ok.

Sitwasyon 1: Ang pagsubok ay pinapatakbo sa isang laptop na may lumang i7 na mobile na bersyon. Ang pagsubok, pagmemensahe at serbisyo ay isinasagawa sa isang node sa isang lalagyan ng Docker:

Sequential 10000 cycles in ~0 seconds (26987 cycles/s)
Sequential 20000 cycles in ~1 seconds (26915 cycles/s)
Sequential 100000 cycles in ~4 seconds (26957 cycles/s)
Parallel 2 100000 cycles in ~2 seconds (44240 cycles/s)
Parallel 4 100000 cycles in ~2 seconds (53459 cycles/s)
Parallel 10 100000 cycles in ~2 seconds (52283 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (49317 cycles/s)

Sitwasyon 2: 3 node na tumatakbo sa iba't ibang mga makina sa ilalim ng docker (NAT).

Sequential 10000 cycles in ~1 seconds (8684 cycles/s)
Sequential 20000 cycles in ~2 seconds (8424 cycles/s)
Sequential 100000 cycles in ~12 seconds (8655 cycles/s)
Parallel 2 100000 cycles in ~7 seconds (15160 cycles/s)
Parallel 4 100000 cycles in ~5 seconds (19133 cycles/s)
Parallel 10 100000 cycles in ~4 seconds (24399 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (34517 cycles/s)

Sa lahat ng kaso, ang paggamit ng CPU ay hindi lalampas sa 250%

Mga resulta ng

Umaasa ako na ang cycle na ito ay hindi mukhang isang mind dump at ang aking karanasan ay magiging tunay na pakinabang sa parehong mga mananaliksik ng mga distributed system at practitioner na nasa pinakasimula pa lamang ng pagbuo ng mga distributed architecture para sa kanilang mga sistema ng negosyo at tumitingin sa Erlang/Elixir nang may interes. , ngunit may pagdududa kung sulit ba ito...

Larawan @chuttersnap

Ang mga rehistradong user lamang ang maaaring lumahok sa survey. Mag-sign in, pakiusap

Anong mga paksa ang dapat kong saklawin nang mas detalyado bilang bahagi ng serye ng Eksperimento ng VTrade?

  • Teorya: Mga merkado, mga order at ang kanilang timing: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Aklat ng mga order. Teorya at kasanayan ng pagpapatupad ng isang libro na may mga pagpapangkat

  • Visualization ng trading: Ticks, bar, resolution. Paano mag-imbak at kung paano mag-glue

  • Backoffice. Pagpaplano at pag-unlad. Pagsubaybay ng empleyado at pagsisiyasat ng insidente

  • API. Alamin natin kung anong mga interface ang kailangan at kung paano ipatupad ang mga ito

  • Imbakan ng impormasyon: PostgreSQL, Timescale, Tarantool sa mga sistema ng pangangalakal

  • Reaktibiti sa mga sistema ng pangangalakal

  • Iba pa. Magsusulat ako sa mga komento

6 mga gumagamit ang bumoto. 4 user ang umiwas.

Pinagmulan: www.habr.com

Magdagdag ng komento