Konstrubrikoj de distribuitaj aplikoj. Dua aproksimado

Anonco

Kolegoj, meze de somero mi planas publikigi alian serion da artikoloj pri la dezajno de vicsistemoj: "La VTrade Eksperimento" - provo skribi kadron por komercaj sistemoj. La serio ekzamenos la teorion kaj praktikon konstrui interŝanĝon, aŭkcion kaj vendejon. Fine de la artikolo mi invitas vin voĉdoni por la temoj, kiuj plej interesas vin.

Konstrubrikoj de distribuitaj aplikoj. Dua aproksimado

Ĉi tiu estas la fina artikolo en la serio pri distribuitaj reaktivaj aplikoj en Erlang/Elixir. EN unua artikolo vi povas trovi la teoriajn fundamentojn de reaktiva arkitekturo. Dua artikolo ilustras la bazajn ŝablonojn kaj mekanismojn por konstrui tiajn sistemojn.

Hodiaŭ ni levos problemojn pri disvolviĝo de la kodobazo kaj projektoj ĝenerale.

Organizo de servoj

En la reala vivo, kiam vi disvolvas servon, vi ofte devas kombini plurajn interagajn ŝablonojn en unu regilo. Ekzemple, la uzantservo, kiu solvas la problemon de administrado de projekto-uzantprofiloj, devas respondi al req-resp petoj kaj raporti profilĝisdatigojn per pub-sub. Ĉi tiu kazo estas sufiĉe simpla: malantaŭ mesaĝado estas unu regilo, kiu efektivigas la servan logikon kaj publikigas ĝisdatigojn.

La situacio fariĝas pli komplika kiam ni bezonas efektivigi mistoleran distribuitan servon. Ni imagu, ke la postuloj por uzantoj ŝanĝiĝis:

  1. nun la servo devus prilabori petojn sur 5 clusternodoj,
  2. povi plenumi fonajn prilaborajn taskojn,
  3. kaj ankaŭ povi dinamike administri abonlistojn por profilaj ĝisdatigoj.

Komento: Ni ne konsideras la temon de konsekvenca stokado kaj reproduktado de datumoj. Ni supozu, ke ĉi tiuj problemoj estis solvitaj pli frue kaj la sistemo jam havas fidindan kaj skaleblan stokan tavolon, kaj pritraktantoj havas mekanismojn por interagi kun ĝi.

La formala priskribo de la uzantservo fariĝis pli komplika. El vidpunkto de programisto, ŝanĝoj estas minimumaj pro la uzo de mesaĝado. Por kontentigi la unuan postulon, ni devas agordi ekvilibron ĉe la req-resp interŝanĝpunkto.

La postulo prilabori fonajn taskojn okazas ofte. Ĉe uzantoj, ĉi tio povus esti kontroli uzantdokumentojn, prilabori elŝutitan plurmedian aŭ sinkronigi datumojn kun sociaj amaskomunikiloj. retoj. Ĉi tiuj taskoj devas esti iel distribuitaj ene de la areto kaj la progreso de ekzekuto monitorita. Sekve, ni havas du solvopciojn: aŭ uzu la tasko-distribuoŝablonon de la antaŭa artikolo, aŭ, se ĝi ne konvenas, verku kutiman taskoplanilon, kiu administros la aron da procesoroj laŭ la maniero, kiun ni bezonas.

Punkto 3 postulas la pub-sub-ŝablonan etendon. Kaj por efektivigo, post kreado de pub-sub-interŝanĝpunkto, ni devas aldone lanĉi la regilon de ĉi tiu punkto ene de nia servo. Tiel, estas kvazaŭ ni movas la logikon por prilaborado de abonoj kaj malabonoj de la mesaĝa tavolo en la efektivigon de uzantoj.

Kiel rezulto, la malkomponaĵo de la problemo montris, ke por plenumi la postulojn, ni devas lanĉi 5-ekzemplojn de la servo sur malsamaj nodoj kaj krei plian enton - pub-sub-regilo, respondeca por la abono.
Por ruli 5 pritraktilojn, vi ne bezonas ŝanĝi la servokodon. La sola plia ago estas starigi ekvilibrajn regulojn ĉe la interŝanĝa punkto, pri kiu ni parolos iom poste.
Ekzistas ankaŭ plia komplekseco: la pub-sub-regilo kaj kutima taskoplanilo devas funkcii en ununura kopio. Denove, la mesaĝa servo, kiel fundamenta, devas provizi mekanismon por elekti gvidanton.

Elekto de gvidantoj

En distribuitaj sistemoj, gvidanto-elekto estas la proceduro por nomumado de ununura procezo respondeca por planado de distribuita pretigo de iu ŝarĝo.

En sistemoj kiuj ne estas emaj al centraligo, universalaj kaj konsent-bazitaj algoritmoj, kiel ekzemple paxos aŭ floso, estas uzitaj.
Ĉar mesaĝado estas makleristo kaj centra elemento, ĝi scias pri ĉiuj servaj regiloj - kandidatoj. Mesaĝado povas nomumi gvidanton sen voĉdonado.

Post komenci kaj konektiĝi al la interŝanĝa punkto, ĉiuj servoj ricevas sisteman mesaĝon #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Se LeaderPid koincidas kun pid aktuala procezo, ĝi estas nomumita kiel la gvidanto, kaj la listo Servers inkluzivas ĉiujn nodojn kaj iliajn parametrojn.
Momente nova aperas kaj funkcianta grapolnodo estas malkonektita, ĉiuj servoregiloj ricevas #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} laŭe.

Tiel, ĉiuj komponantoj konscias pri ĉiuj ŝanĝoj, kaj la areto estas garantiita havi unu gvidanton en ajna momento.

Perantoj

Por efektivigi kompleksajn distribuitajn prilaborajn procezojn, same kiel en problemoj de optimumigo de ekzistanta arkitekturo, estas oportune uzi perantojn.
Por ne ŝanĝi la servan kodon kaj solvi, ekzemple, problemojn de plia prilaborado, enrutado aŭ registranta mesaĝojn, vi povas ebligi prokurilon antaŭ la servo, kiu plenumos la tutan plian laboron.

Klasika ekzemplo de pub-sub-optimumigo estas distribuita aplikaĵo kun komerca kerno, kiu generas ĝisdatigajn eventojn, kiel prezŝanĝojn en la merkato, kaj alirtavolo - N-serviloj, kiuj provizas websocket API por retaj klientoj.
Se vi decidas fronte, tiam klienta servo aspektas jene:

  • la kliento establas ligojn kun la platformo. Sur la flanko de la servilo kiu finas la trafikon, procezo estas lanĉita por servi ĉi tiun konekton.
  • En la kunteksto de la serva procezo okazas rajtigo kaj abono al ĝisdatigoj. La procezo nomas la abonmetodon por temoj.
  • Post kiam okazaĵo estas generita en la kerno, ĝi estas liverita al la procezoj servantaj la ligojn.

Ni imagu, ke ni havas 50000 5 abonantojn al la temo "novaĵoj". Abonantoj estas distribuitaj egale tra 50000 serviloj. Kiel rezulto, ĉiu ĝisdatigo, alveninta al la interŝanĝpunkto, estos reproduktita 10000 XNUMX fojojn: XNUMX XNUMX fojojn sur ĉiu servilo, laŭ la nombro da abonantoj sur ĝi. Ne tre efika skemo, ĉu ne?
Por plibonigi la situacion, ni enkonduku prokurilon, kiu havas la saman nomon kiel la interŝanĝpunkto. La tutmonda nomo registristo devas povi resendi la plej proksiman procezon laŭnome, tio estas grava.

Ni lanĉu ĉi tiun prokurilon sur la alirtavolaj serviloj, kaj ĉiuj niaj procezoj servantaj la websocket api abonos ĝin, kaj ne al la originala pub-sub-interŝanĝpunkto en la kerno. Prokurado abonas la kernon nur en la kazo de unika abono kaj reproduktas la envenantan mesaĝon al ĉiuj ĝiaj abonantoj.
Kiel rezulto, 5 mesaĝoj estos senditaj inter la kerno kaj alirserviloj, anstataŭ 50000.

Envojigo kaj ekvilibro

Req-Resp

En la nuna mesaĝa efektivigo, ekzistas 7 petaj distribuaj strategioj:

  • default. La peto estas sendita al ĉiuj regiloj.
  • round-robin. Petoj estas listigitaj kaj cikle distribuitaj inter regiloj.
  • consensus. La regiloj kiuj servas la servon estas dividitaj en gvidantojn kaj sklavojn. Petoj estas senditaj nur al la gvidanto.
  • consensus & round-robin. La grupo havas gvidanton, sed petoj estas distribuitaj inter ĉiuj membroj.
  • sticky. La hashfunkcio estas kalkulita kaj asignita al specifa prizorganto. Postaj petoj kun ĉi tiu subskribo iras al la sama prizorganto.
  • sticky-fun. Kiam pravalorigante la interŝanĝo punkto, la hash kalkulo funkcio por sticky ekvilibrigi.
  • fun. Simile al gluiĝema amuzo, nur vi povas aldone alidirekti, malakcepti aŭ antaŭprilabori ĝin.

La distribua strategio estas agordita kiam la interŝanĝpunkto estas pravigita.

Krom ekvilibro, mesaĝado permesas vin etikedi entojn. Ni rigardu la specojn de etikedoj en la sistemo:

  • Konekta etikedo. Permesas al vi kompreni per kiu rilato la eventoj venis. Uzita kiam regila procezo konektas al la sama interŝanĝpunkto, sed kun malsamaj vojŝlosiloj.
  • Serva etikedo. Ebligas al vi kombini pritraktilojn en grupojn por unu servo kaj vastigi vojigon kaj ekvilibrajn kapablojn. Por la req-resp padrono, vojigo estas linia. Ni sendas peton al la interŝanĝpunkto, poste ĝi transdonas ĝin al la servo. Sed se ni bezonas dividi la prizorgilojn en logikaj grupoj, tiam la disigo estas farita per etikedoj. Kiam oni specifas etikedon, la peto estos sendita al specifa grupo de regiloj.
  • Petu etikedon. Permesas al vi distingi inter respondoj. Ĉar nia sistemo estas nesinkrona, por prilabori servorespondojn ni devas povi specifi RequestTag dum sendado de peto. El ĝi ni povos kompreni la respondon, al kiu peto venis al ni.

Drinkejo-sub

Por pub-sub ĉio estas iom pli simpla. Ni havas interŝanĝpunkton, al kiu mesaĝoj estas publikigitaj. La interŝanĝpunkto distribuas mesaĝojn inter abonantoj, kiuj abonis la envojigŝlosilojn, kiujn ili bezonas (ni povas diri, ke tio estas analoga al temoj).

Skalebleco kaj faŭltoleremo

La skaleblo de la sistemo kiel tutaĵo dependas de la grado da skalebleco de la tavoloj kaj komponentoj de la sistemo:

  • Servoj estas skalitaj aldonante pliajn nodojn al la areto kun pritraktiloj por ĉi tiu servo. Dum prova operacio, vi povas elekti la optimuman ekvilibran politikon.
  • La mesaĝservo mem ene de aparta areto estas ĝenerale skalita aŭ movante precipe ŝarĝitajn interŝanĝpunktojn al apartaj aretnodoj, aŭ aldonante prokurprocezojn al precipe ŝarĝitaj areoj de la areto.
  • La skaleblo de la tuta sistemo kiel karakterizaĵo dependas de la fleksebleco de la arkitekturo kaj la kapablo kombini individuajn aretojn en oftan logikan unuon.

La sukceso de projekto ofte dependas de la simpleco kaj rapideco de skalo. Mesaĝado en sia nuna versio kreskas kune kun la aplikaĵo. Eĉ se mankas al ni aro de 50-60 maŝinoj, ni povas recurri al federacio. Bedaŭrinde, la temo de federacio estas ekster la amplekso de ĉi tiu artikolo.

Rezervado

Analizante ŝarĝbalancadon, ni jam diskutis redundon de servoregiloj. Tamen, mesaĝado ankaŭ devas esti rezervita. Okaze de nodo aŭ maŝinkraŝo, mesaĝado devus aŭtomate renormaliĝi, kaj en la plej mallonga ebla tempo.

En miaj projektoj mi uzas pliajn nodojn, kiuj prenas la ŝarĝon en kazo de falo. Erlang havas norman distribuitan reĝiman efektivigon por OTP-aplikoj. Distribuita reĝimo elfaras reakiron en kazo de fiasko lanĉante la malsukcesan aplikaĵon sur alia antaŭe lanĉita nodo. La procezo estas travidebla; post fiasko, la aplikaĵo aŭtomate moviĝas al la malsukcesa nodo. Vi povas legi pli pri ĉi tiu funkcio tie.

Produkteco

Ni provu almenaŭ proksimume kompari la agadon de rabbitmq kaj niajn kutimajn mesaĝojn.
Mi trovis oficialaj rezultoj rabbitmq testado de la openstack-teamo.

En paragrafo 6.14.1.2.1.2.2. La origina dokumento montras la rezulton de la RPC CAST:
Konstrubrikoj de distribuitaj aplikoj. Dua aproksimado

Ni ne faros aldonajn agordojn al la OS-kerno aŭ erlang VM anticipe. Kondiĉoj por testado:

  • erl elektas: +A1 +sbtu.
  • La testo ene de ununura erlang-nodo funkcias per tekokomputilo kun malnova i7 en movebla versio.
  • Aretaj provoj estas faritaj sur serviloj kun reto 10G.
  • La kodo funkcias en docker-ujoj. Reto en NAT-reĝimo.

Testkodo:

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.

Scenaro 1: La testo ruliĝas sur tekkomputilo kun malnova i7-poŝtelefona versio. La testo, mesaĝado kaj servo estas efektivigitaj sur unu nodo en unu Docker-ujo:

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)

Scenaro 2: 3 nodoj kurantaj sur malsamaj maŝinoj sub 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)

En ĉiuj kazoj, CPU-uzado ne superis 250%

Rezultoj

Mi esperas, ke ĉi tiu ciklo ne aspektas kiel mensa rubo kaj mia sperto estos de vera utilo al ambaŭ esploristoj de distribuitaj sistemoj kaj praktikistoj, kiuj estas komence de konstruado de distribuitaj arkitekturoj por siaj komercaj sistemoj kaj rigardas Erlang/Elixir kun intereso. , sed vi dubas ĉu ĝi valoras...

Foto @chuttersnap

Nur registritaj uzantoj povas partopreni la enketon. Ensaluti, bonvolu.

Kiajn temojn mi traktu pli detale kadre de la serio VTrade Experiment?

  • Teorio: Merkatoj, ordoj kaj ilia tempo: TAGO, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Libro de mendoj. Teorio kaj praktiko de efektivigo de libro kun grupiĝoj

  • Bildigo de komerco: Tikoj, stangoj, rezolucioj. Kiel konservi kaj kiel glui

  • Backoffice. Planado kaj disvolviĝo. Monitorado de dungitoj kaj enketo de okazaĵaj

  • API. Ni eltrovu, kiajn interfacojn necesas kaj kiel efektivigi ilin

  • Stokado de informoj: PostgreSQL, Timecale, Tarantool en komercaj sistemoj

  • Reaktiveco en komercaj sistemoj

  • Alia. Mi skribos en la komentoj

Voĉdonis 6 uzantoj. 4 uzantoj sindetenis.

fonto: www.habr.com

Aldoni komenton