12/08/2018, 15:00

Publish-Subcribe Pattern on Rails

Publish-Subcribe là mẫu gửi thông điệp mà publishers không gửi trực tiếp đến subscribers. Thay vào đó, gửi các thông điệp (sự kiện) mà không hề biết gì về bên nhận. Bên nhận chỉ nhận thông điệp mong muốn, mà không hề biết về thông tin bên gửi. Để thực hiện được điều đó, message brocker orevent ...

Publish-Subcribe là mẫu gửi thông điệp mà publishers không gửi trực tiếp đến subscribers. Thay vào đó, gửi các thông điệp (sự kiện) mà không hề biết gì về bên nhận.

Bên nhận chỉ nhận thông điệp mong muốn, mà không hề biết về thông tin bên gửi.

Để thực hiện được điều đó, message brocker orevent bus tiếp nhận các sự kiện sau đó chuyển tới những bên nhận mong muốn.

Một số công cụ giúp phát triển mô hình Publish-Subcribe Pattern:

  • Wisper
  • EventBus
  • EventBGBus
  • RabbitMQ
  • Redis

Điểm mạnh của Publish-Subcribe Pattern là:

  • Giảm thiểu độ phình của Model/Controller

  • Giảm số lượng callbacks

Việc có nhiều callbacks giữa các models khiến cho chúng bị ràng buộc và rất khó duy trì hay phát triển.

Ví dụ một như model Entries sau:

app/models/entri.rb

class Entri
  field: content, type: String
 
  after_create :create_entri, :notify_followers

  def create_entri
    Post.create!(self)
  end

  def notify_followers
    User::NotifyFollowers.call(self)
  end
end

app/controllers/api/v1/entries_controller.rb

class Api::V1::EntriesController < Api::V1::ApiController
  def create
   @entri = current_user.entries.build entri_params
    if @entri.save
      render_created @entri
    else
      render_unprocessable_entity @entri.errors
    end
  end
end

Ta thấy model Entri có các callbacks nhưng với pub/sub, viết lại với Wisper như sau:

app/models/entri.rb

class Entri
  field: content, type: String
end

Publisher tạo event

app/controllers/api/v1/entries_controller.rb

class Api::V1::EntriesController < Api::V1::ApiController

  include Wisper::Publisher
  def create
    @entri = current_user.entries.build entri_params
    if @entri.save
      publish(:entri_create, @entri)
      render_created(@entri)
    else
      publish(:entri_errors, @entri)
      render_unprocessable_entity(@entri.errors)
    end
  end
  # ...
end

Subscribers nhận events cần thiết

app/listener/post_listener.rb

class PostListener
  def entri_create entri
    Post.create!(entri)
  end
end

app/listener/user_listener.rb

class UserListener
  def entri_create entri
    User::NotifyFollowers.call(self)
  end
end

Event Bus đăng kí tạo các subscribers khác nhau trong hệ thống.

config/initializers/wisper.rb

Wisper.subscribe(PostListener.new)
Wisper.subscribe(UserListener.new)

Như vậy model Entri đã hoàn toàn loại bỏ các callbacks và giúp hoạt động hoàn toàn độc lập với những model khác.

Publish/Subscribe giúp cho code được sạch sẽ, dễ đọc, dễ maintain.

Ví dụ:

Publisher

app/service/financial/suggest.rb

class Financial::Suggest
  include Wisper::Publisher
  # ...
  def self.call(suggest)
    if suggest.approved?
      publish(:suggest_create, suggest)
    else
      publish(:suggest_decline, suggest)
    end
  end

Subscribers

app/listener/client_listener.rb

class ClientListener
  def suggest_create suggest
    Client::Charge.call(suggest)
    Inventory::UpdateStock.call(suggest)
  end

  def suggest_decline suggest
    Client::NotifyDeclinedOrder(suggest)
  end
end

Performance

Tùy vào cách sử dụng mà có những tích cực hoặc tiêu cực: ví dụ: Khi sử dụng nhiều pub/sub để xử lý dữ liệu ở một server remote thi performance sẽ có hiệu quả không tốt, vì nó phải tải qua network. Để cải thiện về performance chúng ta có thể sử dụng Wisper, vì nó có rất nhiều adapters hỗ trợ xử lý các sự kiện không đồng bộ và thực thi nhiều threads

Tham khảo: https://www.toptal.com/ruby-on-rails/the-publish-subscribe-pattern-on-rails?utm_source=Engineering+Blog+Subscribers&utm_campaign=c760329af1-blog_post_publish_subscribe_pattern&utm_medium=email&utm_term=0_af8c2cde60-c760329af1-111583969 https://techmaster.vn/posts/24981/hoc-lap-trinh-ruby-on-rails-3

0