先描述下需求:项目中用 sidekiq 做后台任务,大概有十几个xxxJob
的 class,每个 job 都定义好了 perform 方法。
现在需要对每个 job 做些 log 操作。
我的做法是定义一个 module,然后让每个 job include 这个 module。
module JobLogger
extend ActiveSupport::Concern
included do
alias_method :perform_without_log, :perform
undef perform
def perform(params)
perform_without_log(params)
log操作
end
end
end
这么做的问题是,在每个 xxxJob 里,必须要像下面这样
class XXXJob
def perform
xxx
end
include JobLogger
end
感觉把 include 写在中间有些别扭,有没有更优雅的实现方式?
module JobLogger
def perform
log操作
end
end
class XXXJob
include JobLogger
def perform
xxx
super
end
end
没验证,大致如上
好像基本差不多了,继承是肯定不能接受的。
如果真要改进的话,我的看法是,相比把动作隐含在included
中,我比较偏向于明确地表达。
module JobLogger
extend ActiveSupport::Concern
module ClassMethods
def hook_log
alias_method :perform_without_log, :perform
define_method :perform do |params|
perform_without_log params
log_it
end
end
end
end
class XXXJob
include JobLogger
def perform
xxx
end
hook_log
end
class XXXJob
prepend JobLogger
def perform
xxx
end
end
module JobLogger
def perform
puts 'do someting before'
super
puts 'do someting after'
end
end
仅限 ruby2.0
有点违反最小化类职责,Concern 应该不知道其他类的方法。 如果非 job 类也要使用你的 JobLogger,会不会出错?测试代码貌似也有点麻烦。 Corcern 主是用来给 Model Controller 减肥用的,这个为什么要用 Concern 来实现?好像普通模块更好。
require 'active_support/concern'
module JobLogger
extend ActiveSupport::Concern
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def create_perform(&block)
define_method :perform do |&block|
puts :log_start
yield
puts :log_end
end
end
end
end
class A
include JobLogger
create_perform { puts :hello }
end
@leomayleomay 不好意思,我没看到这里已经有 Concern 了,那 alias_method_chain 在这是肯定可以使用的。
@loveltyoic 其实不用 Concern 更好,普通手段完全可以实现,因为任务是在 Rails 之外的。没必要为了这个增加对 ActiveSupport 的依赖。
看到大家的回帖急死我了,楼上的同学都没认真读过 Sidekiq 的文档。
@loveltyoic 这里最合适的做法应该是使用 Sidekiq 提供的Server Middleware
Server Middleware 的类似 Rack Middleware,它是在一个 Worker 执行 Job 时,提供外层注入一些逻辑。Sidekiq 基于此实现了
在我们公司的一个项目是里加入了这样一个 Middleware
module Middleware
class JobMonitor
def call worker, msg, queue
ActiveSupport::Notifications.instrument("background_job.process", msg.dup) do
yield
end
end
end
end
这里通过 AS::Notifications 把这里发生的事情发布出去,然后另外一个地方会进行 subscribe,负责发送 Job 的执行情况到我们的数据收集系统里。
楼主你的代码可以简单实现成这样,Job 那部分不用改动
# lib/job_logger.rb
class YourJob
def call worker, msg, queue
yield
put_your_log_opertation_here
end
end
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add JobLogger
end
end