JQ Blog

PubSubとObserver

PubSub

PubSubとは

出版-購読型モデル(しゅっぱん-こうどくがたモデル、英: Publish/subscribe)は、非同期メッセージングパラダイムの一種であり、メッセージの送信者(出版側)が特定の受信者(購読側)を想定せずにメッセージを送るようプログラムされたものである。出版されたメッセージにはクラス分けされ、購読者に関する知識を持たない。購読側は興味のあるクラスを指定しておき、そのクラスに属するメッセージだけを受け取り、出版者についての知識を持たない。出版側と購読側の結合度が低いため、スケーラビリティがよく、動的なネットワーク構成に対応可能である。

参照:出版-購読型モデル(ウィキペディア)

利点

出版側と購読側は疎結合されており、相手の存在を知る必要もない。トピックに関して通信さえできれば、両者はシステムのネットワーク構成も知る必要がない。また、相手の状態がどうであろうと個々のシステムは正常に稼動し続ける。一般的な密結合のクライアントサーバモデルでは、クライアントはサーバ上でサーバプロセスが動作していないときはメッセージを送ることが出来ないし、サーバはクライアントが動作していないときはメッセージを受け取れない。出版-購読型モデルでは、出版側と購読側を位置的に分離すると同時に、時間的にも分離する。出版-購読型システムの戦略として、出版側がダウンしていても、バックログを使って購読側が動作し続けるようにできる(帯域幅調整の一種)。

欠点

ブローカー(サーバ)を使った出版-購読型システムでは、購読側がブローカーに対してメッセージ送信を要求するのは帯域内で行われ、セキュリティ問題が発生する可能性がある。ブローカーを騙して間違ったクライアントにメッセージを送らせたり、クライアントがサービスを受けられないようにすることが考えられる。購読の正当性を検証するようにした場合、ブローカーが過負荷に陥る可能性もある。ブローカーを使わないシステムでも、購読側が認証されていないメッセージを受信する可能性がある。認証されていない出版側は不正で損害を与えるようなメッセージをシステム内に送り込む可能性がある。これはシステムがブロードキャストやマルチキャストを使っているために発生する。このような不正アクセスに対する防御策としては、今のところ暗号化(SSL/TLSなど)しかない。

RailsのPubSub

Railsでは前記事を書いてたActionCableがPubSubモデルを利用して使われている。
RailsGuide(日本語版)にはこう書いてある。

Action Cableは、 WebSocketとRailsのその他の部分をシームレスに統合するためのものです。Action Cable が導入されたことで、Rails アプリケーションの効率の良さとスケーラビリティを損なわずに、通常のRailsアプリケーションと同じスタイル・方法でリアルタイム機能をRubyで記述できます。クライアント側のJavaScriptフレームワークとサーバー側のRubyフレームワークを同時に提供する、フルスタックのフレームワークです。Active RecordなどのORMで書かれたすべてのドメインモデルにアクセスできます。

Pub/Subは出版-購読型モデルとも呼ばれる、メッセージキューのパラダイムです。出版側(Publisher)が、購読側(Subscriber)の抽象クラスに情報を送信します。 このとき、個別の受信者を指定しません。Action Cableでは、このアプローチを採用してサーバーと多数のクライアント間で通信を行います。

参照:RailsGuide(日本語版)

Observer

Observerパターンとは

ウィキペディアにはObserverパターンに対してこう書いてある。

Observer パターン(オブザーバ・パターン)とは、プログラム内のオブジェクトの状態を観察(英: observe)するようなプログラムで使われるデザインパターンの一種。出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。

分散イベント処理システムの実装に主に使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。リアルタイムのアプリケーション配置の手段として興味深い機能である。

参考:Observer パターン(ウィキペディア)

つまり、出版者(Subject)に対して購読者(Observer)は監視するし、出版者に何かの変更があればそれに対する処理を行うということになる。

RailsのObserverパターン

RubyにはobservableというModuleがあって監視できるようになっている。

サンプルコード

  • Employee(サブジェクト):従業員を表す
  • Payroll(オブザーバ1):給与の小切手の発行を行う
  • TaxMan(オブザーバ2):税金の請求書の発行を行う

まずは従業員を表すEmployeeクラスは、name, title, salaryといったデータと、salaryの変更を受け付けるメソッドを持っている。 さらに、Employeeクラスにobservableincludeする。 observableで用いるメソッドは次のとなる。

  • add_observerメソッドで通知する先のオブジェクトを追加
  • changedメソッドとnotify_observersメソッドでオブジェクトに通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'observer'
class Employee
  include Observable
  attr_reader :name, :title, :salary
  def initialize(name, title, salary)
    @name   = name
    @title  = title
    @salary = salary
    payroll = Payroll.new
    taxman  = TaxMan.new
    add_observer(payroll)
    add_observer(taxman)
  end

  def salary=(new_salary)
    @salary = new_salary
    changed
    notify_observers(self)
  end
end

次に給与の小切手の発行を行うPayrollクラスと、税金の請求書の発行を行うTaxManクラスを作成する。

1
2
3
4
5
6
7
8
9
10
11
class Payroll
  def update(changed_employee)
    puts "彼の給料は#{changed_employee.salary}になりました!#{changed_employee.name}のために新しい小切手を切ります。"
  end
end

class TaxMan
  def update(changed_employee)
    puts "#{changed_employee.name}に新しい税金の請求書を送ります"
  end
end

コードはここまでになるので、コンソールで確認してみる。

1
2
3
4
5
6
7
jo = Employee.new('Jo', 'System Team member', 5000)
jo.salary = 6000
#=> 彼の給料は6000になりました!Joのために新しい小切手を切ります。
#=> Joに新しい税金の請求書を送ります
jo.salary = 7000
#=> 彼の給料は7000になりました!Joのために新しい小切手を切ります。
#=> Joに新しい税金の請求書を送ります

こういうふうにRubyでObserverの機能を使える。

参照

出版-購読型モデル(ウィキペディア)
Observer パターン(ウィキペディア)
RailsGuide(日本語版)