Sidekiq高级特性:中间件与扩展开发
Sidekiq的中间件机制是其架构中最强大和灵活的特性之一,借鉴了Rack中间件的设计理念,允许开发者在作业处理的生命周期中插入自定义逻辑。中间件分为客户端中间件和服务器中间件两种类型,分别处理作业入队和执行过程中的不同阶段,采用链式调用模式,每个中间件都是独立的处理单元,按照配置顺序依次执行,提供了极高的灵活性。
Sidekiq中间件机制原理与自定义开发
Sidekiq的中间件机制是其架构中最强大和灵活的特性之一,它借鉴了Rack中间件的设计理念,允许开发者在作业处理的生命周期中插入自定义逻辑。中间件在Sidekiq中分为客户端中间件和服务器中间件两种类型,分别处理作业入队和执行过程中的不同阶段。
中间件架构设计原理
Sidekiq中间件采用链式调用模式,每个中间件都是一个独立的处理单元,按照配置顺序依次执行。这种设计模式提供了极高的灵活性,开发者可以在不修改核心代码的情况下扩展Sidekiq的功能。
中间件类型与执行时机
Sidekiq支持两种类型的中间件,每种类型在不同的执行阶段发挥作用:
| 中间件类型 | 执行时机 | 主要用途 | 参数签名 |
|---|---|---|---|
| 客户端中间件 | 作业入队到Redis之前 | 作业参数处理、验证、增强 | call(job_class, job_payload, queue, redis_pool) |
| 服务器中间件 | 作业从Redis取出后执行前 | 作业执行环境准备、监控、错误处理 | call(job_instance, job_payload, queue) |
中间件链的执行流程
中间件链的执行采用递归遍历模式,确保每个中间件都能正确处理yield操作:
# lib/sidekiq/middleware/chain.rb 中的核心执行逻辑
def traverse(chain, index, args, &block)
if index >= chain.size
yield
else
chain[index].call(*args) do
traverse(chain, index + 1, args, &block)
end
end
end
这种设计确保了中间件可以:
- 在yield前执行预处理逻辑
- 在yield后执行后处理逻辑
- 完全跳过后续中间件的执行(不调用yield)
- 修改传递给后续中间件的参数
内置中间件模块
Sidekiq提供了两个核心模块来简化中间件开发:
# lib/sidekiq/middleware/modules.rb
module Sidekiq::ServerMiddleware
attr_accessor :config
def redis_pool
config.redis_pool
end
def logger
config.logger
end
def redis(&block)
config.redis(&block)
end
end
# 客户端中间件目前与服务器中间件使用相同的模块
Sidekiq::ClientMiddleware = Sidekiq::ServerMiddleware
自定义中间件开发实战
1. 性能监控中间件
下面是一个实用的性能监控中间件示例,用于记录作业执行时间:
class PerformanceMonitoringMiddleware
include Sidekiq::ServerMiddleware
def initialize(metrics_client = nil)
@metrics_client = metrics_client
end
def call(job_instance, job_payload, queue)
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
job_class = job_payload['class']
# 记录到日志
logger.info "Job #{job_class} completed in #{duration.round(3)}s"
# 发送到监控系统(如果配置了)
if @metrics_client
@metrics_client.timing("sidekiq.job.#{job_class}.duration", duration)
@metrics_client.increment("sidekiq.job.#{job_class}.processed")
end
rescue => e
# 错误处理
logger.error "Performance monitoring failed: #{e.message}"
raise
end
end
2. 请求ID传播中间件
在分布式系统中保持请求链路的完整性非常重要:
class RequestIdPropagationMiddleware
include Sidekiq::ClientMiddleware
include Sidekiq::ServerMiddleware
def call(job_class, job_payload, queue, redis_pool = nil)
# 客户端:传播请求ID
if defined?(::RequestStore) && ::RequestStore.store[:request_id]
job_payload['request_id'] = ::RequestStore.store[:request_id]
end
result = yield
# 服务器端:恢复请求ID
if job_payload['request_id'] && defined?(::RequestStore)
::RequestStore.store[:request_id] = job_payload['request_id']
end
result
end
end
3. 数据库连接管理中间件
确保作业执行时数据库连接处于健康状态:
class DatabaseConnectionMiddleware
include Sidekiq::ServerMiddleware
def call(job_instance, job_payload, queue)
if defined?(ActiveRecord::Base)
# 检查连接状态,必要时重新连接
if ActiveRecord::Base.connection.active?
ActiveRecord::Base.clear_active_connections!
else
logger.warn "Database connection lost, reconnecting..."
ActiveRecord::Base.establish_connection
end
end
yield
ensure
# 确保清理连接
if defined?(ActiveRecord::Base)
ActiveRecord::Base.clear_active_connections!
end
end
end
中间件配置与管理
中间件链的配置方法
Sidekiq提供了灵活的中间件配置API:
# 配置客户端中间件
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add RequestIdPropagationMiddleware
chain.add CustomValidationMiddleware, max_size: 10.kilobytes
end
end
# 配置服务器中间件
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.prepend PerformanceMonitoringMiddleware, metrics_client
chain.add DatabaseConnectionMiddleware
chain.insert_before Sidekiq::Middleware::I18n::Server, RequestIdPropagationMiddleware
# 移除不需要的中间件
chain.remove SomeUnwantedMiddleware
end
end
中间件链操作方法
| 方法 | 描述 | 示例 |
|---|---|---|
add | 添加到链末尾 | chain.add MyMiddleware |
prepend | 添加到链开头 | chain.prepend MyMiddleware |
insert_before | 在指定中间件前插入 | chain.insert_before Target, MyMiddleware |
insert_after | 在指定中间件后插入 | chain.insert_after Target, MyMiddleware |
remove | 移除指定中间件 | chain.remove UnwantedMiddleware |
exists? | 检查中间件是否存在 | chain.exists?(MyMiddleware) |
高级中间件模式
1. 条件执行中间件
class ConditionalMiddleware
include Sidekiq::ServerMiddleware
def initialize(condition_proc)
@condition_proc = condition_proc
end
def call(job_instance, job_payload, queue)
if @condition_proc.call(job_payload)
# 只在满足条件时执行
start_time = Time.now
result = yield
duration = Time.now - start_time
logger.info "Conditional processing took #{duration}s"
result
else
# 直接跳过
yield
end
end
end
# 使用示例:只监控重要作业
important_jobs = ->(job) { job['class'] =~ /Important/ }
chain.add ConditionalMiddleware, important_jobs
2. 断路器模式中间件
class CircuitBreakerMiddleware
include Sidekiq::ServerMiddleware
def initialize(failure_threshold: 5, reset_timeout: 60)
@failure_threshold = failure_threshold
@reset_timeout = reset_timeout
@failures = 0
@last_failure = nil
@mutex = Mutex.new
end
def call(job_instance, job_payload, queue)
if circuit_open?
logger.warn "Circuit open for #{job_payload['class']}, skipping execution"
# 可以在这里实现降级逻辑
return nil
end
begin
result = yield
reset_circuit
result
rescue => e
record_failure
raise e
end
end
private
def circuit_open?
@mutex.synchronize do
return false if @failures < @failure_threshold
return true if @last_failure && Time.now - @last_failure < @reset_timeout
# 超时后重置
@failures = 0
@last_failure = nil
false
end
end
def record_failure
@mutex.synchronize do
@failures += 1
@last_failure = Time.now
end
end
def reset_circuit
@mutex.synchronize do
@failures = 0
@last_failure = nil
end
end
end
测试与调试中间件
中间件单元测试
# test/unit/middleware/performance_monitoring_test.rb
require 'test_helper'
class PerformanceMonitoringMiddlewareTest < Minitest::Test
def setup
@middleware = PerformanceMonitoringMiddleware.new
@job_payload = {'class' => 'TestJob', 'args' => [1, 2, 3]}
@recorder = []
end
def test_records_execution_time
@middleware.call(nil, @job_payload, 'default') do
sleep(0.1)
@recorder << :executed
end
assert_includes @recorder, :executed
# 可以在这里添加对日志输出的断言
end
def test_handles_exceptions_gracefully
assert_raises(RuntimeError) do
@middleware.call(nil, @job_payload, 'default') do
raise 'test error'
end
end
end
end
集成测试
# test/integration/middleware_integration_test.rb
require 'test_helper'
class MiddlewareIntegrationTest < Sidekiq::IntegrationTest
def test_middleware_chain_execution_order
$execution_order = []
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add ->(job, payload, queue) { $execution_order << :first; yield }
chain.add ->(job, payload, queue) { $execution_order << :second; yield }
end
end
TestJob.perform_async
Sidekiq::Worker.drain_all
assert_equal [:first, :second], $execution_order
end
end
最佳实践与注意事项
- 保持中间件轻量级:中间件在每次作业执行时都会运行,应避免重量级操作
- 正确处理异常:确保中间件不会吞掉原始异常,同时要处理自身的异常
- 避免状态共享:Sidekiq为每个作业创建新的中间件实例,不要依赖实例变量在作业间共享状态
- 注意执行顺序:中间件的执行顺序很重要,使用
prepend、insert_before等方法来控制顺序 - 性能监控:对自定义中间件进行性能监控,确保不会成为性能瓶颈
通过深入理解Sidekiq中间件机制,开发者可以构建高度可定制和健壮的后台作业处理系统。中间件提供了强大的扩展点,使得在不修改Sidekiq核心代码的情况下,能够实现复杂的业务需求,如监控、审计、安全控制等。
客户端与服务端中间件实战应用
Sidekiq的中间件系统是其最强大的扩展机制之一,允许开发者在作业处理的生命周期中插入自定义逻辑。客户端中间件在作业推送到Redis队列时执行,而服务端中间件在作业实际执行时运行。这种设计模式类似于Rack中间件,提供了极大的灵活性和控制力。
中间件架构与执行流程
Sidekiq中间件的执行遵循经典的链式调用模式,每个中间件都可以在yield前后执行自定义逻辑。下面是中间件执行的流程图:
客户端中间件实战案例
客户端中间件主要用于作业入队前的预处理、参数验证、指标收集等场景。以下是一个实用的客户端中间件示例:
class MetricsClientMiddleware
include Sidekiq::ClientMiddleware
def initialize(statsd_client: nil)
@statsd_client = statsd_client
end
def call(job_class, job_payload, queue, redis_pool)
start_time = Time.now
# 记录作业入队指标
increment_metric("sidekiq.job.enqueued", tags: {
job_class: job_class.to_s,
queue: queue
})
result = yield
# 记录入队耗时
duration = (Time.now - start_time) * 1000
timing_metric("sidekiq.job.enqueue_time", duration, tags: {
job_class: job_class.to_s,
queue: queue
})
result
end
private
def increment_metric(name, tags: {})
@statsd_client&.increment(name, tags: tags) if @statsd_client
end
def timing_metric(name, value, tags: {})
@statsd_client&.timing(name, value, tags: tags) if @statsd_client
end
end
# 配置客户端中间件
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add MetricsClientMiddleware, statsd_client: StatsD.client
end
end
服务端中间件实战案例
服务端中间件用于作业执行过程中的监控、错误处理、资源管理等场景。以下是一个综合性的服务端中间件示例:
class MonitoringServerMiddleware
include Sidekiq::ServerMiddleware
def initialize(monitoring_service: nil)
@monitoring_service = monitoring_service
end
def call(worker_instance, job_payload, queue)
job_class = job_payload['class']
job_id = job_payload['jid']
# 记录作业开始执行
log_job_start(job_id, job_class, queue)
begin
result = yield
# 记录作业成功完成
log_job_success(job_id, job_class, queue)
result
rescue StandardError => error
# 记录作业失败
log_job_failure(job_id, job_class, queue, error)
raise error # 重新抛出异常以保持原有行为
ensure
# 确保资源清理
cleanup_resources
end
end
private
def log_job_start(job_id, job_class, queue)
logger.info "Starting job #{job_id} (#{job_class}) on queue #{queue}"
@monitoring_service&.record_event("job_started", {
job_id: job_id,
job_class: job_class,
queue: queue,
timestamp: Time.now.iso8601
})
end
def log_job_success(job_id, job_class, queue)
logger.info "Completed job #{job_id} (#{job_class}) successfully"
@monitoring_service&.record_event("job_completed", {
job_id: job_id,
job_class: job_class,
queue: queue,
status: "success",
timestamp: Time.now.iso8601
})
end
def log_job_failure(job_id, job_class, queue, error)
logger.error "Job #{job_id} (#{job_class}) failed: #{error.message}"
@monitoring_service&.record_event("job_failed", {
job_id: job_id,
job_class: job_class,
queue: queue,
error: error.message,
backtrace: error.backtrace&.first(5),
timestamp: Time.now.iso8601
})
end
def cleanup_resources
# 清理数据库连接、文件句柄等资源
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord)
end
end
# 配置服务端中间件
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add MonitoringServerMiddleware, monitoring_service: AppMonitoring.service
end
end
中间件链管理最佳实践
Sidekiq提供了灵活的中间件链管理方法,支持添加、删除、插入等操作:
# 添加中间件到链尾
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add MyMiddleware
chain.add AnotherMiddleware, config: { timeout: 30 }
end
end
# 插入中间件到特定位置
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.insert_before Sidekiq::Middleware::I18n::Server, MyCustomMiddleware
chain.insert_after Sidekiq::Middleware::I18n::Server, MyOtherMiddleware
end
end
# 移除中间件
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.remove UnwantedMiddleware
end
end
# 前置添加中间件
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.prepend FirstMiddleware # 添加到链首
end
end
实战场景:分布式追踪中间件
在现代微服务架构中,分布式追踪是必不可少的。以下是一个实现分布式追踪的中间件示例:
class DistributedTracingMiddleware
include Sidekiq::ServerMiddleware
def call(worker, job, queue)
trace_id = job['trace_id'] || generate_trace_id
span_id = generate_span_id
# 设置追踪上下文
OpenTelemetry::Trace.with_span(create_span(trace_id, span_id, job, queue)) do |span|
# 添加作业相关信息到span
span.set_attribute('job.id', job['jid'])
span.set_attribute('job.class', job['class'])
span.set_attribute('job.queue', queue)
span.set_attribute('job.retry_count', job['retry_count'].to_i)
yield
end
end
private
def create_span(trace_id, span_id, job, queue)
tracer = OpenTelemetry.tracer_provider.tracer('sidekiq')
tracer.start_span(
"sidekiq.#{job['class']}",
attributes: {
'sidekiq.job_id' => job['jid'],
'sidekiq.queue' => queue,
'sidekiq.worker' => job['class']
}
)
end
def generate_trace_id
SecureRandom.uuid
end
def generate_span_id
SecureRandom.hex(8)
end
end
中间件执行顺序与依赖管理
理解中间件的执行顺序对于确保系统正确运行至关重要。以下表格展示了常见的中间件执行顺序:
| 执行顺序 | 中间件类型 | 典型用途 | 是否必需 |
|---|---|---|---|
| 1 | 客户端前置中间件 | 参数验证、指标收集 | 可选 |
| 2 | 客户端核心中间件 | 作业序列化、队列选择 | 必需 |
| 3 | 客户端后置中间件 | 最终检查、日志记录 | 可选 |
| 4 | 服务端前置中间件 | 上下文设置、资源分配 | 可选 |
| 5 | 服务端核心中间件 | 作业执行、错误处理 | 必需 |
| 6 | 服务端后置中间件 | 资源清理、指标上报 | 可选 |
性能优化与最佳实践
在实现中间件时,需要注意性能影响和资源管理:
- 避免阻塞操作:中间件中的操作应该尽可能快速,避免长时间阻塞
- 资源清理:确保在ensure块中清理所有分配的资源
- 错误处理:妥善处理异常,不要吞没原始错误信息
- 内存管理:避免在中间件中创建大量临时对象
class OptimizedMiddleware
include Sidekiq::ServerMiddleware
# 使用线程安全的缓存避免重复初始化
CACHE = Concurrent::Map.new
def call(worker, job, queue)
# 使用缓存避免重复计算
config = CACHE.fetch_or_store(:config) { load_configuration }
# 轻量级操作
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = yield
# 异步记录指标,避免阻塞
record_metrics_async(start_time, job, queue)
result
end
private
def record_metrics_async(start_time, job, queue)
Thread.new do
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
MetricsService.record(job: job, queue: queue, duration: duration)
end
end
end
通过合理运用客户端和服务端中间件,开发者可以在不修改核心业务代码的情况下,为Sidekiq作业处理流程添加丰富的功能和监控能力。这种设计模式遵循了开闭原则,使得系统扩展更加灵活和可控。
Sidekiq插件系统与扩展开发指南
Sidekiq的插件系统基于强大的中间件架构,为开发者提供了灵活的扩展能力。通过中间件机制,您可以在作业处理的各个阶段注入自定义逻辑,实现监控、日志记录、错误处理、性能追踪等高级功能。
中间件架构概述
Sidekiq的中间件系统采用链式设计模式,类似于Rack中间件的工作方式。中间件链分为客户端中间件和服务器端中间件两种类型:
客户端中间件在作业推送到Redis队列时执行,而服务器端中间件在作业实际处理时执行。每个中间件都可以在作业处理前后执行自定义逻辑。
中间件开发基础
服务器端中间件
服务器端中间件需要包含Sidekiq::ServerMiddleware模块,该模块提供了访问Sidekiq核心资源的方法:
class CustomServerMiddleware
include Sidekiq::ServerMiddleware
def initialize(options = {})
@options = options
end
def call(job_instance, job_payload, queue)
# 作业执行前逻辑
start_time = Time.now
logger.info "Starting job: #{job_payload['class']}"
# 执行作业
result = yield
# 作业执行后逻辑
duration = Time.now - start_time
logger.info "Completed job in #{duration.round(2)}s"
result
rescue => e
# 异常处理
logger.error "Job failed: #{e.message}"
raise
end
end
客户端中间件
客户端中间件处理作业入队过程,需要包含Sidekiq::ClientMiddleware模块:
class CustomClientMiddleware
include Sidekiq::ClientMiddleware
def initialize(rate_limiter)
@rate_limiter = rate_limiter
end
def call(job_class, job_payload, queue, redis_pool)
# 限流检查
unless @rate_limiter.allow?(job_class)
raise Sidekiq::JobRateLimited, "Rate limit exceeded for #{job_class}"
end
# 记录入队指标
logger.info "Enqueuing job: #{job_class}"
# 执行入队操作
result = yield
# 更新限流器状态
@rate_limiter.track(job_class)
result
end
end
中间件配置与管理
Sidekiq提供了灵活的中间件配置API,支持添加、删除、插入和前置操作:
# 配置客户端中间件
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add CustomClientMiddleware, RedisRateLimiter.new
chain.add MetricsMiddleware, statsd: Statsd.client
chain.remove SomeUnwantedMiddleware
end
end
# 配置服务器端中间件
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.prepend RequestIdMiddleware
chain.insert_before Sidekiq::JobLogger, TimingMiddleware
chain.add ErrorTrackingMiddleware, sentry: Raven
chain.add CustomServerMiddleware, slow_job_threshold: 5.0
end
end
高级中间件模式
条件中间件执行
您可以根据作业属性决定是否执行中间件逻辑:
class ConditionalMiddleware
include Sidekiq::ServerMiddleware
def call(job_instance, job_payload, queue)
# 只处理特定队列的作业
return yield unless queue == 'critical'
# 只处理特定类型的作业
return yield unless job_payload['class'] == 'ImportantJob'
# 执行中间件逻辑
monitor_performance { yield }
end
private
def monitor_performance(&block)
# 性能监控逻辑
end
end
中间件链操作
Sidekiq允许动态操作中间件链:
# 检查中间件是否存在
if Sidekiq.server_middleware.exists?(CustomMiddleware)
# 执行特定逻辑
end
# 清空中间件链(谨慎使用)
Sidekiq.client_middleware.clear
实用中间件示例
性能监控中间件
class PerformanceMonitor
include Sidekiq::ServerMiddleware
def call(job_instance, job_payload, queue)
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
memory_before = memory_usage
result = yield
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
memory_after = memory_usage
log_performance(job_payload, queue, duration, memory_after - memory_before)
result
end
private
def memory_usage
`ps -o rss= -p #{Process.pid}`.to_i / 1024.0
end
def log_performance(job_payload, queue, duration, memory_delta)
metrics = {
job_class: job_payload['class'],
queue: queue,
duration: duration.round(3),
memory_delta: memory_delta.round(2),
timestamp: Time.now.iso8601
}
redis do |conn|
conn.rpush('sidekiq:performance', Sidekiq.dump_json(metrics))
end
end
end
重试策略中间件
class SmartRetryMiddleware
include Sidekiq::ServerMiddleware
def call(job_instance, job_payload, queue)
yield
rescue NetworkError => e
# 网络错误时使用指数退避重试
handle_network_retry(job_payload, e)
rescue DatabaseError => e
# 数据库错误时等待连接恢复
handle_database_retry(job_payload, e)
rescue => e
# 其他错误使用默认重试策略
raise
end
private
def handle_network_retry(job_payload, error)
retry_count = job_payload['retry_count'].to_i
delay = calculate_exponential_backoff(retry_count)
if retry_count < 5
requeue_with_delay(job_payload, delay, error)
else
send_alert(job_payload, error)
raise
end
end
end
中间件测试策略
为确保中间件的可靠性,建议编写完整的测试套件:
# 测试服务器端中间件
RSpec.describe CustomServerMiddleware do
let(:middleware) { described_class.new }
let(:job_instance) { double('job') }
let(:job_payload) { { 'class' => 'TestJob', 'args' => [1, 2, 3] } }
it 'logs job execution time' do
expect(middleware.logger).to receive(:info).twice
middleware.call(job_instance, job_payload, 'default') do
# 模拟作业执行
end
end
it 'handles job failures gracefully' do
expect(middleware.logger).to receive(:error)
expect {
middleware.call(job_instance, job_payload, 'default') do
raise 'Job failed'
end
}.to raise_error('Job failed')
end
end
最佳实践与注意事项
- 性能考虑:中间件会在每个作业执行时运行,确保逻辑轻量级
- 错误处理:妥善处理异常,避免影响其他中间件或作业执行
- 资源管理:及时释放数据库连接、文件句柄等资源
- 线程安全:确保中间件代码是线程安全的
- 配置灵活性:通过初始化参数提供配置选项
# 良好的中间件设计示例
class WellDesignedMiddleware
include Sidekiq::ServerMiddleware
def initialize(options = {})
@timeout = options.fetch(:timeout, 30)
@log_level = options.fetch(:log_level, :info)
@enabled = options.fetch(:enabled, true)
end
def call(job_instance, job_payload, queue)
return yield unless @enabled
Timeout.timeout(@timeout) do
logger.log(@log_level, "Starting job processing")
yield
end
rescue Timeout::Error
logger.error "Job timeout after #{@timeout}s"
raise
end
end
通过掌握Sidekiq的插件系统和中间件开发技术,您可以构建高度定制化的后台作业处理解决方案,满足各种复杂的业务需求。中间件架构提供了无限的扩展可能性,从简单的日志记录到复杂的分布式事务管理,都能通过精心设计的中间件来实现。
自定义作业处理器与高级调度策略
Sidekiq提供了强大的扩展机制,允许开发者自定义作业处理器和实现高级调度策略。通过深入理解Sidekiq的内部架构,我们可以构建高度定制化的后台任务处理系统。
自定义作业处理器实现
Sidekiq的作业处理器核心位于Processor类中,它负责从Redis获取作业、执行中间件链并调用作业的perform方法。我们可以通过继承或组合的方式创建自定义处理器。
基础处理器架构
class CustomProcessor < Sidekiq::Processor
def initialize(capsule, &block)
super
@custom_metrics = CustomMetrics.new
end
private
def process(uow)
@custom_metrics.record_processing_start
super
ensure
@custom_metrics.record_processing_end
end
def execute_job(instance, cloned_args)
@custom_metrics.record_job_execution_start(instance.class)
result = super
@custom_metrics.record_job_execution_success(instance.class)
result
rescue => e
@custom_metrics.record_job_execution_failure(instance.class, e)
raise
end
end
处理器配置与注册
要在Sidekiq中使用自定义处理器,需要在配置中进行设置:
Sidekiq.configure_server do |config|
config[:processor_class] = CustomProcessor
config.on(:processor_start) do |processor|
puts "Processor #{processor.object_id} started"
end
end
高级调度策略实现
Sidekiq的调度系统基于Redis的有序集合(zset)实现,支持复杂的调度策略。
自定义调度器实现
class AdvancedScheduler
include Sidekiq::Component
def initialize(config)
@config = config
@scheduled_jobs = {}
@mutex = Mutex.new
end
def schedule(job_class, args, options = {})
schedule_time = calculate_schedule_time(options)
job_id = generate_job_id
@mutex.synchronize do
@scheduled_jobs[job_id] = {
class: job_class,
args: args,
scheduled_at: schedule_time,
priority: options[:priority] || :normal
}
end
enqueue_to_redis(job_id, schedule_time)
job_id
end
def reschedule(job_id, new_time)
@mutex.synchronize do
if job = @scheduled_jobs[job_id]
job[:scheduled_at] = new_time
update_in_redis(job_id, new_time)
true
else
false
end
end
end
private
def calculate_schedule_time(options)
case options[:schedule]
when :immediate
Time.now
when :delayed
Time.now + (options[:delay] || 0)
when :cron
parse_cron_expression(options[:cron])
else
options[:at] || Time.now
end
end
def enqueue_to_redis(job_id, schedule_time)
redis do |conn|
conn.zadd("custom_schedule", schedule_time.to_f, job_id.to_s)
end
end
def update_in_redis(job_id, new_time)
redis do |conn|
conn.zadd("custom_schedule", new_time.to_f, job_id.to_s, ch: true)
end
end
end
智能作业分发策略
基于业务需求,我们可以实现多种智能分发策略:
基于优先级的作业分发
class PriorityAwareFetcher < Sidekiq::BasicFetch
def retrieve_work
# 检查高优先级队列
high_priority_job = check_priority_queue(:high)
return high_priority_job if high_priority_job
# 检查中优先级队列
medium_priority_job = check_priority_queue(:medium)
return medium_priority_job if medium_priority_job
# 默认处理低优先级队列
super
end
private
def check_priority_queue(priority)
queue_name = "queue:#{priority}_priority"
job = redis { |conn| conn.lpop(queue_name) }
UnitOfWork.new(queue_name, job, config) if job
end
end
负载感知的作业处理
class LoadAwareProcessor < Sidekiq::Processor
MAX_CONCURRENT_JOBS = 10
LOAD_THRESHOLD = 0.8
def process_one
return if system_overloaded?
super
end
private
def system_overloaded?
current_load = calculate_system_load
concurrent_jobs = get_concurrent_job_count
current_load > LOAD_THRESHOLD || concurrent_jobs >= MAX_CONCURRENT_JOBS
end
def calculate_system_load
# 实现系统负载计算逻辑
# 可以基于CPU使用率、内存使用率等指标
`cat /proc/loadavg`.split[0].to_f
end
def get_concurrent_job_count
Sidekiq::Processor::WORK_STATE.size
end
end
调度策略性能优化
批量作业处理
class BatchProcessor
include Sidekiq::Component
BATCH_SIZE = 100
PROCESSING_TIMEOUT = 30.seconds
def process_batch
jobs = fetch_batch_jobs
return if jobs.empty?
process_in_parallel(jobs)
end
private
def fetch_batch_jobs
redis do |conn|
conn.pipelined do |pipeline|
BATCH_SIZE.times do
pipeline.lpop("queue:default")
end
end
end.compact
end
def process_in_parallel(jobs)
threads = jobs.map do |job_data|
Thread.new do
process_single_job(job_data)
end
end
# 设置超时防止线程阻塞
threads.each { |t| t.join(PROCESSING_TIMEOUT) }
end
def process_single_job(job_data)
job_hash = Sidekiq.load_json(job_data)
klass = Object.const_get(job_hash["class"])
instance = klass.new
instance.perform(*job_hash["args"])
rescue => e
handle_job_failure(job_data, e)
end
end
监控与统计集成
自定义指标收集
class ProcessingMetrics
def initialize
@metrics = {
jobs_processed: 0,
jobs_failed: 0,
processing_times: [],
queue_sizes: {}
}
@mutex = Mutex.new
end
def record_job_processed(queue, processing_time)
@mutex.synchronize do
@metrics[:jobs_processed] += 1
@metrics[:processing_times] << processing_time
@metrics[:queue_sizes][queue] ||= 0
@metrics[:queue_sizes][queue] += 1
end
end
def get_stats
@mutex.synchronize do
{
total_processed: @metrics[:jobs_processed],
failure_rate: calculate_failure_rate,
avg_processing_time: calculate_average_time,
queue_distribution: @metrics[:queue_sizes]
}
end
end
private
def calculate_failure_rate
return 0 if @metrics[:jobs_processed] == 0
@metrics[:jobs_failed].to_f / @metrics[:jobs_processed]
end
def calculate_average_time
return 0 if @metrics[:processing_times].empty?
@metrics[:processing_times].sum / @metrics[:processing_times].size
end
end
配置与集成示例
完整配置示例
Sidekiq.configure_server do |config|
# 自定义处理器
config[:processor_class] = LoadAwareProcessor
# 自定义抓取器
config[:fetch_class] = PriorityAwareFetcher
# 调度器配置
config[:average_scheduled_poll_interval] = 2
config[:poll_interval_average] = 30
# 中间件
config.server_middleware do |chain|
chain.add CustomMetricsMiddleware
chain.add CircuitBreakerMiddleware
end
# 生命周期钩子
config.on(:startup) do
AdvancedScheduler.new(config).start
ProcessingMetricsCollector.start
end
end
通过上述自定义处理器和高级调度策略的实现,我们可以构建出高度定制化、性能优异且可靠的后台作业处理系统。这些策略可以根据具体业务需求进行组合和调整,为复杂的应用场景提供强大的后台处理能力。
总结
通过自定义作业处理器与高级调度策略的实现,可以构建出高度定制化、性能优异且可靠的后台作业处理系统。Sidekiq提供了强大的扩展机制,允许开发者基于业务需求实现智能分发策略、负载感知处理、批量作业处理等高级功能。这些策略可以根据具体业务需求进行组合和调整,为复杂的应用场景提供强大的后台处理能力,包括基于优先级的作业分发、系统负载感知、批量处理优化以及完整的监控统计集成。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



