julkaisu-tilausmalli (tai pub / sub, lyhyesti) on Ruby on Rails -viestimalli, jossa viestien lähettäjät (julkaisijat) eivät ohjelmoi viestejä lähetettäväksi suoraan tietyille vastaanottajille (tilaajille). Sen sijaan ohjelmoija “julkaisee” viestejä (tapahtumia) ilman minkäänlaista tilaajien tietämystä.
Vastaavasti tilaajat ilmaisevat kiinnostuksensa yhteen tai useampaan tapahtumaan ja saavat vain kiinnostavia viestejä julkaisijoiden tietämättä.
Tämän saavuttamiseksi välittäjä, nimeltään 'viestivälittäjä' tai 'tapahtumaväylä', vastaanottaa julkaistut viestit ja välittää ne edelleen tilaajille, jotka ovat rekisteröityneet vastaanottamaan niitä.
Toisin sanoen pub-sub on malli, jota käytetään viestien välittämiseen järjestelmän eri komponenttien välillä tietämättä mitään toistensa identiteetistä.
Tämä muotoilumalli ei ole uusi, mutta se ei yleensä käytä sitä Rails-kehittäjät . On monia työkaluja, jotka auttavat sisällyttämään tämän suunnittelumallin koodipohjaasi, kuten:
Kaikilla näillä työkaluilla on erilaiset pub-sub-toteutukset, mutta ne kaikki tarjoavat samat suuret edut Rails-sovellukselle.
On tavallista, mutta ei parasta käytäntöä, että Rails-sovelluksessasi on joitain rasvamalleja tai ohjaimia.
Pubi- / alakuvio voi helposti auta hajottaa rasvamalleja tai kontrollereita .
Ottaa paljon toisiinsa kietoutuneet soittopyynnöt mallien välillä on hyvin tunnettu koodihaju , ja vähitellen se yhdistää mallit tiukasti toisiinsa, mikä tekee niistä vaikeampi ylläpitää tai pidentää.
Esimerkiksi a Post
malli voi näyttää tältä:
# app/models/post.rb class Post # ... field: content, type: String # ... after_create :create_feed, :notify_followers # ... def create_feed Feed.create!(self) end def notify_followers User::NotifyFollowers.call(self) end end
Ja Post
ohjain saattaa näyttää tältä:
# app/controllers/api/v1/posts_controller.rb class Api::V1::PostsController Kuten näette, Post
mallilla on takaisinkutsut, jotka yhdistävät mallin tiukasti molempiin Feed
malli ja User::NotifyFollowers
palvelu tai huolenaihe. Käyttämällä mitä tahansa pub / sub-mallia, edellinen koodi voidaan muotoilla uudelleen olevan jotain seuraavaa, joka käyttää Wisperiä:
# app/models/post.rb class Post # ... field: content, type: String # ... # no callbacks in the models! end
Kustantajat julkaise tapahtuma mahdollisesti tarvittavan tapahtuman hyötykuorman kanssa.
# app/controllers/api/v1/posts_controller.rb # corresponds to the publisher in the previous figure class Api::V1::PostsController Tilaajat tilaa vain tapahtumat, joihin he haluavat vastata.
# app/listener/feed_listener.rb class FeedListener def post_create(post) Feed.create!(post) end end
# app/listener/user_listener.rb class UserListener def post_create(post) User::NotifyFollowers.call(self) end end
Tapahtumabussi rekisteröi eri tilaajat järjestelmään.
# config/initializers/wisper.rb Wisper.subscribe(FeedListener.new) Wisper.subscribe(UserListener.new)
Tässä esimerkissä pub-sub-malli eliminoi täysin soittopyynnöt Post
mallia ja auttoi malleja toimimaan itsenäisesti toisistaan mahdollisimman vähän tietoa toisistaan varmistaen löysän kytkennän. Käyttäytymisen laajentaminen lisätoimintoihin on vain kiinnittyminen haluttuun tapahtumaan.
Yhden vastuun periaate (SRP)
Yhden vastuun periaate on todella hyödyllistä puhtaan koodipohjan ylläpitämisessä. Ongelmana siitä kiinni pitämisessä on, että joskus luokan vastuu ei ole niin selvää kuin sen pitäisi olla. Tämä on erityisen yleistä MVC: iden (kuten Rails) suhteen.
Mallit pitäisi käsitellä pysyvyyttä, assosiaatioita eikä paljon muuta.
Ohjaimet pitäisi käsitellä käyttäjien pyyntöjä ja olla kääre liiketoimintalogiikan (Service Objects) ympärillä.
Palveluobjektit tulisi sisällyttää yksi liiketoimintalogiikan vastuista, tarjota lähtökohta ulkoisille palveluille tai toimia vaihtoehtona mallihuolenaiheille.
Kytkemisen vähentämisen ansiosta pub-sub-suunnittelumalli voidaan yhdistää yhden vastuun palveluobjekteihin (SRSO) auttaakseen kapseloimaan liiketoimintalogiikkaa ja estämään liiketoimintalogiikkaa pääsemästä joko malleihin tai ohjaimiin. Tämä pitää koodipohjan puhtaana, luettavana, ylläpidettävänä ja skaalattavana.
Tässä on esimerkki joistakin monimutkaisista liiketoimintalogiikoista, jotka on toteutettu pub / sub-mallin ja palveluobjektien avulla:
Kustantaja
# app/service/financial/order_review.rb class Financial::OrderReview include Wisper::Publisher # ... def self.call(order) if order.approved? publish(:order_create, order) else publish(:order_decline, order) end end # ...
Tilaajat
# app/listener/client_listener.rb class ClientListener def order_create(order) # can implement transaction using different service objects Client::Charge.call(order) Inventory::UpdateStock.call(order) end def order_decline(order) Client::NotifyDeclinedOrder(order) end end
Käyttämällä julkaisu-tilausmallia koodipohja organisoituu SRSO: ksi melkein automaattisesti. Lisäksi monimutkaisten työnkulkujen koodin käyttöönotto on helppo järjestää tapahtumien ympärille luettavuudesta, ylläpidettävyydestä tai skaalautuvuudesta tinkimättä.
Testaus
Hajotettaessa rasvamalleja ja ohjaimia ja meillä on paljon SRSO: ta, koodipohjan testaamisesta tulee paljon, paljon helpompaa prosessia. Tämä pätee erityisesti integraatiotestaukseen ja moduulien väliseen viestintään. Testauksen tulisi yksinkertaisesti varmistaa, että tapahtumat julkaistaan ja vastaanotetaan oikein.
Wisperillä on testaa helmi joka lisää RSpec-ottelijoita helpottamaan eri komponenttien testausta.
Kahden edellisen esimerkin (Post
esimerkki ja Order
esimerkki) testauksen tulisi sisältää seuraava:
Kustantajat
# spec/service/financial/order_review.rb describe Financial::OrderReview do it 'publishes :order_create' do @order = Fabricate(:order, approved: true) expect { Financial::OrderReview.call(@order) }.to broadcast(:order_create) end it 'publishes :order_decline' do @order = Fabricate(:order, approved: false) expect { Financial::OrderReview.call(@order) }.to broadcast(:order_decline) end end
Tilaajat
# spec/listeners/feed_listener_spec.rb describe FeedListener do it 'receives :post_create event on PostController#create' do expect(FeedListner).to receive(:post_create).with(Post.last) post '/post', { content: 'Some post content' }, request_headers end end
Julkaistujen tapahtumien testaamiseen on kuitenkin joitain rajoituksia, kun julkaisija on rekisterinpitäjä.
Jos haluat mennä ylimääräiselle mailille, myös hyötykuorman testaaminen auttaa ylläpitämään vieläkin parempaa koodipohjaa.
Kuten näette, pub-sub-suunnittelumallien testaus on helppoa. Kyse on vain siitä, että eri tapahtumat julkaistaan ja vastaanotetaan oikein.
Esitys
Tämä on enemmän a mahdollista etu. Itse julkaisu-tilaus -mallilla ei ole suurta luontaista vaikutusta koodin suorituskykyyn. Kuten kaikilla koodissasi käytetyillä työkaluilla, pubin / subin toteuttamisen työkaluilla voi olla suuri vaikutus suorituskykyyn. Joskus se voi olla huono vaikutus, mutta joskus se voi olla erittäin hyvä.
Ensinnäkin esimerkki huonosta vaikutuksesta: Redis on 'edistynyt avainarvon välimuisti ja säilö. Sitä kutsutaan usein tietorakenteen palvelimeksi. ' Tämä suosittu työkalu tukee pub / sub-mallia ja on erittäin vakaa. Jos sitä käytetään etäpalvelimella (ei samalla palvelimella, jolla Rails-sovellus on käytössä), se johtaa valtavaan suorituskyvyn menetykseen verkon yleiskustannusten vuoksi.
Toisaalta Wisperillä on useita sovittimia asynkronista tapahtumien käsittelyä varten, kuten viisas-selluloidi , viisas-sidekiq ja viisas-aktiivinen työ . Nämä työkalut tukevat asynkronisia tapahtumia ja ketjutettuja suorituksia. mikä soveltuu asianmukaisesti, voi valtavasti parantaa sovelluksen suorituskykyä.
Bottom Line
Jos tavoitat ylimääräistä mailia suorituskyvyssä, pub / sub-malli voi auttaa sinua saavuttamaan sen. Mutta vaikka et löydä suorituskyvyn parannusta tällä Rails-suunnittelumallilla, se auttaa silti pitämään koodin järjestyksessä ja tekemään siitä ylläpidettävämmän. Loppujen lopuksi kuka voi huolehtia sellaisen koodin suorituskyvystä, jota ei voida ylläpitää tai joka ei ensinnäkään toimi?
Pub-Sub-toteutuksen haitat
Kuten kaikissa asioissa, myös pub-sub-mallissa on joitain mahdollisia haittoja.
Löysä kytkentä (joustamaton semanttinen kytkentä)
Suurimmat pubi- / alakuvion vahvuudet ovat myös sen suurimmat heikkoudet. Julkaistun datan (tapahtuman hyötykuorma) on oltava hyvin määritelty, ja siitä tulee nopeasti melko joustamatonta. Julkaistun hyötykuorman tietorakenteen muokkaamiseksi on tiedettävä kaikista tilaajista ja joko muokattava niitä tai varmistettava, että muutokset ovat yhteensopivia vanhempien versioiden kanssa. Tämä vaikeuttaa Publisher-koodin uudelleenrakentamista.
Jos haluat välttää tämän, sinun on oltava erityisen varovainen määritellessäsi julkaisijoiden hyötykuormaa. Tietysti, jos sinulla on loistava testipaketti, joka testaa hyötykuorman, kuten aiemmin mainittiin, sinun ei tarvitse huolehtia paljon siitä, että järjestelmä kaatuu julkaisijan hyötykuorman tai tapahtuman nimen muuttamisen jälkeen.
Viestiväylän vakaus
Kustantajilla ei ole tietoa tilaajan tilasta ja päinvastoin. Yksinkertaisten pub / sub-työkalujen avulla ei ehkä ole mahdollista varmistaa itse viestiväylän vakautta ja varmistaa, että kaikki julkaistut viestit ovat oikein jonossa ja toimitettuina.
Vaihdettavien viestien lisääntyvä määrä johtaa järjestelmän epävakauteen käytettäessä yksinkertaisia työkaluja, eikä kaikkien tilaajien toimitus ole ehkä mahdollista varmistaa ilman joitain kehittyneempiä protokollia. Riippuen siitä, kuinka monta viestiä vaihdetaan, ja suorituskykyparametreista, jotka haluat saavuttaa, voit harkita palvelujen kuten KaniMQ , PubNub , Työnnä , CloudAMQP , IronMQ tai monia muita vaihtoehtoja. Nämä vaihtoehdot tarjoavat ylimääräisen toiminnallisuuden ja ovat vakaampia kuin Wisper monimutkaisemmissa järjestelmissä. Niiden toteuttaminen vaatii kuitenkin myös jonkin verran ylimääräistä työtä. Voit lukea lisää viestivälittäjien toiminnasta tässä
Äärettömät tapahtumasilmukat
Kun järjestelmää ohjaavat täysin tapahtumat, sinun on oltava erityisen varovainen, ettei sinulla ole tapahtumasilmukoita. Nämä silmukat ovat aivan kuin loputtomat silmukat, joita voi tapahtua koodissa. Niitä on kuitenkin vaikea havaita etukäteen, ja ne voivat saattaa järjestelmän pysähtymään. Ne voivat olla olemassa ilman erillistä ilmoitusta, kun järjestelmässä on julkaistu ja tilattu monia tapahtumia.
Rails-opetusohjelman johtopäätös
Publise-tilaa-malli ei ole hopeamerkki kaikille Rails-ongelmillesi ja koodihajuillesi, mutta se on todella hyvä suunnittelumalli, joka auttaa erottamaan järjestelmän eri komponentit ja tekemään siitä ylläpidettävämmän, luettavamman ja skaalautuvamman.
Yhdistettynä yhden vastuun palveluobjekteihin (SRSO) pub-sub voi myös todella auttaa sisällyttämään liiketoimintalogiikkaa ja estämään erilaisten liiketoiminnallisten huolenaiheiden pääsyn malleihin tai ohjaimiin.
Suorituskyvyn kasvu tämän mallin käytön jälkeen riippuu enimmäkseen käytetystä taustalla olevasta työkalusta, mutta suorituskyvyn kasvua voidaan parantaa huomattavasti joissakin tapauksissa, ja useimmissa tapauksissa se ei varmasti vahingoita suorituskykyä.
Pub-sub-mallin käyttöä tulisi kuitenkin tutkia ja suunnitella huolellisesti, koska löysän kytkennän suuren voiman mukana tulee ylläpito ja uudelleenrakentaminen löysästi kytketyt komponentit.
Koska tapahtumat voivat helposti kasvaa hallinnasta, yksinkertainen pubi / alikirjasto ei välttämättä takaa viestivälittäjän vakautta.
Ja lopuksi, on olemassa vaara, että otetaan käyttöön ääretön tapahtumasilmukka, joka jää huomaamatta, kunnes on liian myöhäistä.
Olen käyttänyt tätä mallia melkein vuoden ajan, ja minun on vaikea kuvitella koodin kirjoittamista ilman sitä. Minulle se on liima, joka saa taustatyöt, palveluobjektit, huolenaiheet, ohjaimet ja mallit kommunikoimaan keskenään puhtaasti ja toimimaan yhdessä kuin viehätys.
Toivon, että olet oppinut yhtä paljon kuin minä tämän koodin tarkistamisesta, ja että tunnet innoituksen antaa Publish-Subscribe-mallille mahdollisuus tehdä Rails-sovelluksestasi mahtava.
Lopuksi suuri kiitos @krisleech hänen mahtavasta työstään Wisper .