この記事は、Middleware - github.com/mperham/sidekiqの翻訳です。
Sidekiq ミドルウェア
最新バージョン 2019/02/27 編集者 Nate Salisbury
Sidekiqには、Rackと同様のミドルウェアの概念があります。この仕組みを利用することで、小さな機能を実装してSidekiqに追加することができます。Sidekiqは、ミドルウェアとして見たときにクライアントとサーバーの2つの側面があります。
-
クライアントミドルウェア ジョブをRedisに入れる前の段階で動きます。この仕組みを利用することで、ジョブがRedisに送られる前に、ジョブを 修正/ストップ することができます。
-
サーバーミドルウェア ジョブが実行される前後で動きます。
カスタムミドルウェア
Sidekiqのバージョン5以降では、最初から付属するミドルウェアはありません。様々なGemやサービスを使って、ジョブを追跡したり追加の機能を付与するようなミドルウェアをインストールすることができます。また、あなた自信でミドルウェアを追加することもできます。
クライアントミドルウェア
クライアントミドルウェアを使用することで、Redisにジョブを入れる前に、ジョブにメタデータを追加することができます。追加されたメタデータは、サーバーミドルウェアでジョブが実行される前に利用することができます。例えば、現在のロケール、マルチテナントアプリでの現在のテナント、などが利用例として挙げられます。
クライアントミドルウェアは、クラスオブジェクトまたはクラス名を含む文字列を引数として受け取ります。
yield
を呼ばなかったり、falseまたはnilを返すと、それ以上ミドルウェアは呼び出されません。そして、ジョブがキューにプッシュされることもありません。
class Middleware::Sidekiq::Client::CustomerJobAttribute
# @param [String, Class] worker_class エンキューされる予定のクラス名の文字列またはクラスオブジェクト
# @param [Hash] job ジョブのペイロード
# * @see https://github.com/mperham/sidekiq/wiki/Job-Format
# @param [String] queue ジョブが取り出されたキューの名前
# @param [ConnectionPool] redis_pool Redisのコネクションプール
# @return [Hash, FalseClass, nil] もしfalseまたはnilを返すと、
# ジョブはRedisにエンキューされません。そうでない場合は、ブロックの戻り値が
# そのままこのメソッドの戻り値になります。
# @yield ミドルウェアチェーンの次のミドルウェア、または、ジョブのエンキュー
def call(worker_class, job, queue, redis_pool)
# return false/nil to stop the job from going to redis
return false if queue != 'default'
job['customer'] = Customer.current_id
yield
end
end
サーバーミドルウェア
サーバーミドルウェアを使用することで、ジョブが実行される前後に任意の処理を実行することができます。下記のコードでは、ジョブで起きた例外のログを取っています。
yield
を呼ばなかった場合は、それ以上ミドルウェアは呼び出されません。そして、ジョブの perform
メソッドは実行されません。
class Middleware::Sidekiq::Server::ErrorLogger
# @param [Object] worker ワーカーインスタンス
# @param [Hash] job ジョブのペイロード
# * @see https://github.com/mperham/sidekiq/wiki/Job-Format
# @param [String] queueジョブが取り出されたキューの名前
# @yield ミドルウェアチェーンの次のミドルウェア、または、ワーカーのperformメソッド
# @return [Void]
def call(worker, job, queue)
begin
yield
rescue => ex
puts ex.message
end
end
end
ミドルウェアのinitializeメソッドを利用することで、ミドルウェアにオプションを渡すこともできます。このオプションは、ミドルウェアの登録時に生成します。
class Middleware::Sidekiq::Server::ErrorLogger
def initialize(options=nil)
# options == { :foo => 1, :bar => 2 }
end
def call(worker, msg, queue)
begin
yield
rescue => ex
puts ex.message
end
end
end
ミドルウェアの登録
自分で作成したミドルウェアをミドルウェアチェーンの一部に追加する方法は下記の通りです。
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add Middleware::Sidekiq::Client::CustomerJobAttribute
end
end
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Middleware::Sidekiq::Server::ErrorLogger, :foo => 1, :bar => 2
end
end
SidekiqをRailsと一緒に使っている場合は、上記のコードを config/initializers/sidekiq.rb
に置くことをおすすめです。
クライアントミドルウェアはサーバーとクライアントの両方で登録すること
Sidekiqサーバーで実行されているジョブは、それ自体が新しいジョブをSidekiqにプッシュできます。このことから、SidekiqサーバーはSidekiqクライアントも兼ねているということになります。つまり、もしあなたがクライアントミドルウェアを作成したなら、そのミドルウェアは configure_client
ブロックだけでなく、 configure_server
ブロックでも設定しなくてはいけません。
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add Middleware::Sidekiq::Client::CustomerJobAttribute
end
end
Sidekiq.configure_server do |config|
config.client_middleware do |chain|
chain.add Middleware::Sidekiq::Client::CustomerJobAttribute
end
config.server_middleware do |chain|
chain.add Middleware::Sidekiq::Server::ErrorLogger, :foo => 1, :bar => 2
end
end
ミドルウェアを削除する
もしミドルウェアを削除するときは、下記のようにすればOKです。
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.remove Some::Middleware
end
end
Sidekiqの起動時にミドルウェアの順番を調査する
-v
オプションを付けてSidekiqを起動すれば、設定済みのクライアントミドルウェアとサーバーミドルウェアを出力することができます。
注意点が1つあります。yield
ブロックが途中で実行されなかった場合は、チェーン上の残りのミドルウェアも実行されなくなる点にご注意ください。このことから、ミドルウェアによってはチェーン上での実行順に特に注意を払う必要があります。
その他
複数のサービス間で1つのRedisインスタンスを共有し、さらに、Sidekiqサーバーでジョブのプッシュを行うとき、意図しないキューのジョブを処理してしまう可能性があることに注意してください。
Sidekiqは1つのRedis セット型を使用してスケジュールされたジョブを処理します。そして、全てのSidekiqサーバーは、設計者の意図とは異なるキューに対してジョブをキューイングする可能性があります。
その場合、現在のRubyコンテキストに存在しないワーカー名がミドルウェアに渡され、存在しないクラス名に対する例外やそれに類するエラーが起きてしまうことがあります。