Rails'de Yayınla-Abone Ol Modeli: Bir Uygulama Eğitimi

Yayınlanan: 2022-03-11

Yayınla-abone ol modeli (veya kısaca pub/sub), mesaj gönderenlerin (yayıncılar) mesajları doğrudan belirli alıcılara (abonelere) gönderilecek şekilde programlamadığı bir Ruby on Rails mesajlaşma modelidir. Bunun yerine programcı, herhangi bir abonenin bilgisi olmadan mesajları (olayları) "yayınlar".

Benzer şekilde, aboneler bir veya daha fazla etkinliğe ilgi gösterirler ve herhangi bir yayıncının bilgisi olmadan yalnızca ilgilendikleri mesajları alırlar.

Bunu başarmak için, “mesaj aracısı” veya “olay veriyolu” olarak adlandırılan bir aracı, yayınlanan mesajları alır ve ardından bunları almak için kayıtlı abonelere iletir.

Başka bir deyişle, pub-sub, farklı sistem bileşenleri arasında, bu bileşenler birbirlerinin kimliği hakkında hiçbir şey bilmeden mesajları iletmek için kullanılan bir kalıptır.

Bu Rails eğitiminde, yayınla-abone ol tasarım deseni bu şemada gösterilmektedir.

Bu tasarım modeli yeni değil, ancak Rails geliştiricileri tarafından yaygın olarak kullanılmamaktadır. Bu tasarım modelini kod tabanınıza dahil etmenize yardımcı olacak birçok araç vardır, örneğin:

  • Wisper (kişisel olarak tercih ettiğim ve daha fazla tartışacağım)
  • Etkinlik Otobüsü
  • EventBGBus (bir EventBus çatalı)
  • TavşanMQ
  • redis

Tüm bu araçların altında yatan farklı pub-sub uygulamaları vardır, ancak hepsi bir Rails uygulaması için aynı büyük avantajları sunar.

Pub-Sub Uygulamasının Avantajları

Model/Denetleyici Şişkinliğini Azaltma

Rails uygulamanızda bazı şişman modellere veya kontrolörlere sahip olmak yaygın bir uygulamadır, ancak en iyi uygulama değildir.

pub/sub modeli, yağ modellerini veya denetleyicileri kolayca ayrıştırmaya yardımcı olabilir.

Daha Az Geri Arama

Modeller arasında çok sayıda iç içe geçmiş geri arama olması, iyi bilinen bir kod kokusudur ve yavaş yavaş modelleri birbirine sıkı sıkıya bağlayarak bakımını veya genişletmesini zorlaştırır.

Örneğin, bir Post modeli aşağıdaki gibi görünebilir:

 # 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

Ve Post denetleyicisi aşağıdaki gibi görünebilir:

 # app/controllers/api/v1/posts_controller.rb class Api::V1::PostsController < Api::V1::ApiController # ... def create @post = current_user.posts.build(post_params) if @post.save render_created(@post) else render_unprocessable_entity(@post.errors) end end # ... end

Gördüğünüz gibi, Post modelinde, modeli hem Feed modeline hem de User::NotifyFollowers hizmetine veya endişesine sıkıca bağlayan geri aramalar vardır. Herhangi bir pub/sub kalıbı kullanılarak önceki kod, Wisper kullanan aşağıdaki gibi bir şey olacak şekilde yeniden faktörlenebilir:

 # app/models/post.rb class Post # ... field: content, type: String # ... # no callbacks in the models! end

Yayıncılar , olayı gerekli olabilecek olay yükü nesnesiyle yayınlar.

 # app/controllers/api/v1/posts_controller.rb # corresponds to the publisher in the previous figure class Api::V1::PostsController < Api::V1::ApiController include Wisper::Publisher # ... def create @post = current_user.posts.build(post_params) if @post.save # Publish event about post creation for any interested listeners publish(:post_create, @post) render_created(@post) else # Publish event about post error for any interested listeners publish(:post_errors, @post) render_unprocessable_entity(@post.errors) end end # ... end

Aboneler yalnızca yanıt vermek istedikleri olaylara abone olurlar.

 # 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

Event Bus , farklı aboneleri sisteme kaydeder.

 # config/initializers/wisper.rb Wisper.subscribe(FeedListener.new) Wisper.subscribe(UserListener.new)

Bu örnekte, pub-sub modeli, Post modelindeki geri aramaları tamamen ortadan kaldırdı ve modellerin birbirinden bağımsız olarak, birbirleri hakkında minimum bilgi ile çalışmasına yardımcı olarak gevşek bağlantı sağladı. Davranışı ek eylemlere genişletmek, yalnızca istenen olaya bağlanma meselesidir.

Tek Sorumluluk İlkesi (SRP)

Tek Sorumluluk İlkesi, temiz bir kod tabanı sağlamak için gerçekten yararlıdır. Buna bağlı kalmanın sorunu, bazen sınıfın sorumluluğunun olması gerektiği kadar net olmamasıdır. Bu, özellikle MVC'ler (Rails gibi) söz konusu olduğunda yaygındır.

Modeller kalıcılığı, çağrışımları ve başka bir şeyi ele almamalıdır.

Denetleyiciler , kullanıcı isteklerini ele almalı ve iş mantığı (Hizmet Nesneleri) etrafında bir sarmalayıcı olmalıdır.

Hizmet Nesneleri , iş mantığının sorumluluklarından birini kapsamalı, harici hizmetler için bir giriş noktası sağlamalı veya model endişelerine alternatif olarak hareket etmelidir.

Bağlantıyı azaltma gücü sayesinde, pub-sub tasarım deseni, iş mantığını kapsüllemeye yardımcı olmak ve iş mantığının modellere veya denetleyicilere sızmasını engellemek için tek sorumluluk hizmeti nesneleri (SRSO'lar) ile birleştirilebilir. Bu, kod tabanını temiz, okunabilir, bakımı yapılabilir ve ölçeklenebilir tutar.

Aşağıda, pub/sub kalıbı ve hizmet nesneleri kullanılarak uygulanan bazı karmaşık iş mantığına bir örnek verilmiştir:

Yayımcı

 # 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 # ...

aboneler

 # 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

Yayınla-abone ol modelini kullanarak, kod tabanı neredeyse otomatik olarak SRSO'lar halinde düzenlenir. Ayrıca, karmaşık iş akışları için kod uygulamak, okunabilirlik, sürdürülebilirlik veya ölçeklenebilirlikten ödün vermeden olaylar etrafında kolayca organize edilebilir.

Test yapmak

Şişman modelleri ve denetleyicileri ayrıştırarak ve çok sayıda SRSO'ya sahip olarak, kod tabanının test edilmesi çok, çok daha kolay bir süreç haline gelir. Bu, özellikle entegrasyon testi ve modüller arası iletişim söz konusu olduğunda geçerlidir. Test, olayların doğru bir şekilde yayınlanmasını ve alınmasını sağlamalıdır.

Wisper, farklı bileşenlerin test edilmesini kolaylaştırmak için RSpec eşleştiriciler ekleyen bir test mücevherine sahiptir.

Önceki iki örnekte (Örnek Post ve Order örneği), test şunları içermelidir:

yayıncılar

 # 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

aboneler

 # 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

Ancak, yayıncı denetleyici olduğunda yayınlanan olayları test etmenin bazı sınırlamaları vardır.

Fazladan yol kat etmek istiyorsanız, yükün de test edilmesi, daha da iyi bir kod tabanının korunmasına yardımcı olacaktır.

Gördüğünüz gibi, pub-sub tasarım desen testi basittir. Sadece farklı olayların doğru bir şekilde yayınlanmasını ve alınmasını sağlamak meselesidir.

Verim

Bu daha olası bir avantajdır. Yayınla-abone ol tasarım modelinin kendisi, kod performansı üzerinde büyük bir doğal etkiye sahip değildir. Ancak, kodunuzda kullandığınız herhangi bir araçta olduğu gibi, pub/sub uygulama araçlarının da performans üzerinde büyük etkisi olabilir. Bazen kötü bir etkisi olabilir, ama bazen çok iyi olabilir.

İlk olarak, kötü bir etki örneği: Redis, "gelişmiş bir anahtar/değer önbelleği ve deposudur. Genellikle bir veri yapısı sunucusu olarak anılır.” Bu popüler araç, pub/sub modelini destekler ve çok kararlıdır. Ancak, uzak bir sunucuda (Rails uygulamasının dağıtıldığı sunucuda değil) kullanılıyorsa, ağ yükü nedeniyle büyük bir performans kaybına neden olur.

Öte yandan Wisper, wisper-celluloid, wisper-sidekiq ve wisper-activejob gibi asenkron olay işleme için çeşitli adaptörlere sahiptir. Bu araçlar, eşzamansız olayları ve iş parçacıklı yürütmeleri destekler. uygun şekilde uygulanırsa, uygulamanın performansını büyük ölçüde artırabilir.

Alt çizgi

Performansta ekstra mil hedefliyorsanız, pub/sub modeli buna ulaşmanıza yardımcı olabilir. Ancak bu Rails tasarım modeliyle bir performans artışı bulamasanız bile, yine de kodun düzenli tutulmasına ve daha sürdürülebilir hale getirilmesine yardımcı olacaktır. Sonuçta, korunamayan veya ilk etapta çalışmayan kodun performansı hakkında kim endişe duyabilir?

Pub-Sub Uygulamasının Dezavantajları

Her şeyde olduğu gibi, pub-sub modelinde de bazı olası dezavantajlar vardır.

Gevşek Birleştirme (Esnek Olmayan Anlamsal Birleştirme)

Pub/sub modelinin en güçlü yanları, aynı zamanda en büyük zayıflıklarıdır. Yayınlanan verilerin yapısı (olay yükü) iyi tanımlanmalıdır ve hızlı bir şekilde esnek değildir. Yayınlanan yükün veri yapısını değiştirmek için, tüm Aboneler hakkında bilgi sahibi olmak ve onları da değiştirmek veya değişikliklerin eski sürümlerle uyumlu olduğundan emin olmak gerekir. Bu, Yayıncı kodunun yeniden düzenlenmesini çok daha zor hale getirir.

Bundan kaçınmak istiyorsanız, yayıncıların yükünü tanımlarken ekstra dikkatli olmalısınız. Tabii ki, daha önce belirtildiği gibi yükü test eden harika bir test paketiniz varsa, yayıncının yükünü veya olay adını değiştirdikten sonra sistemin çökmesi konusunda fazla endişelenmenize gerek yoktur.

Mesajlaşma Veri Yolu Kararlılığı

Yayıncıların abonenin durumu hakkında bilgisi yoktur ve bunun tersi de geçerlidir. Basit pub/sub araçlarını kullanarak, mesajlaşma veri yolunun kendisinin kararlılığını sağlamak ve yayınlanan tüm mesajların doğru şekilde sıraya alınmasını ve teslim edilmesini sağlamak mümkün olmayabilir.

Değişen mesaj sayısının artması, basit araçlar kullanıldığında sistemde kararsızlıklara yol açmaktadır ve bazı daha karmaşık protokoller olmadan tüm abonelere iletimin sağlanması mümkün olmayabilir. Kaç mesaj alışverişi yapıldığına ve ulaşmak istediğiniz performans parametrelerine bağlı olarak RabbitMQ, PubNub, Pusher, CloudAMQP, IronMQ gibi servisleri veya daha birçok alternatifi kullanmayı düşünebilirsiniz. Bu alternatifler ekstra işlevsellik sağlar ve daha karmaşık sistemler için Wisper'dan daha kararlıdır. Bununla birlikte, uygulamak için bazı ekstra çalışmalara da ihtiyaç duyarlar. Mesaj brokerlerinin nasıl çalıştığı hakkında daha fazla bilgiyi buradan okuyabilirsiniz.

Sonsuz Olay Döngüleri

Sistem tamamen olaylar tarafından yönetildiğinde, olay döngülerinin olmaması için ekstra dikkatli olmalısınız. Bu döngüler, kodda meydana gelebilecek sonsuz döngüler gibidir. Ancak, önceden tespit edilmeleri daha zordur ve sisteminizi durma noktasına getirebilirler. Sistemde yayınlanan ve abone olunan çok sayıda etkinlik olduğunda sizin haberiniz olmadan var olabilirler.

Raylar Eğitimi Sonuç

Yayınla-abone ol modeli, tüm Rails sorunlarınız ve kod kokularınız için gümüş bir kurşun değildir, ancak farklı sistem bileşenlerini ayırmaya ve onu daha sürdürülebilir, okunabilir ve ölçeklenebilir hale getirmeye yardımcı olan gerçekten iyi bir tasarım modelidir.

Tek sorumluluk hizmeti nesneleri (SRSO'lar) ile birleştirildiğinde, pub-sub ayrıca iş mantığını kapsüllemede ve farklı işle ilgili endişelerin modellere veya denetleyicilere sızmasını önlemede gerçekten yardımcı olabilir.

Bu kalıbı kullandıktan sonra performans artışı, çoğunlukla kullanılan temel araca bağlıdır, ancak bazı durumlarda performans kazancı önemli ölçüde iyileştirilebilir ve çoğu durumda kesinlikle performansa zarar vermez.

Bununla birlikte, pub-sub modelinin kullanımı incelenmeli ve dikkatli bir şekilde planlanmalıdır, çünkü gevşek bağlantının büyük gücü ile birlikte gevşek bağlı bileşenlerin bakımı ve yeniden düzenlenmesi büyük sorumluluk getirir.

Olaylar kolayca kontrolden çıkabileceğinden, basit bir yayın/alt kitaplık, ileti aracısının kararlılığını sağlamayabilir.

Ve son olarak, çok geç olana kadar fark edilmeyen sonsuz olay döngülerinin ortaya çıkma tehlikesi vardır.


Bu kalıbı neredeyse bir yıldır kullanıyorum ve onsuz kod yazmayı hayal etmek benim için zor. Benim için arka plan işlerini, hizmet nesnelerini, endişeleri, denetleyicileri ve modellerin birbirleriyle temiz bir şekilde iletişim kurmasını ve çekicilik gibi birlikte çalışmasını sağlayan yapıştırıcıdır.

Umarım bu kodu gözden geçirirken benim kadar çok şey öğrenmişsinizdir ve Yayınla-Abone Ol modeline Rails uygulamanızı harika kılmak için bir şans vermek için ilham aldığınızı hissediyorsunuzdur.

Son olarak, Wisper'ı uygulayan harika çalışması için @krisleech'e çok teşekkür ederiz.