搭建纯rack app环境

先创建一个session secret。进入irb执行以下命令

require 'securerandom'
File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }

创建Gemfile,并bundle install

source 'https://rubygems.org/'

gem 'rack', '2.2.4'
gem 'sidekiq', path: '/home/z/project/sidekiq'

然后创建config.ru。里面要增加一个Rack::Session::Cookie

require "sidekiq/web"

use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
run Sidekiq::Web

启动

bundle exec rackup --host 192.168.31.200 -p 5000

Sidekiq::Web

Sidekiq::Web.call其实是交由Sidekiq::WebApplication

# sidekiq-6.5.7/lib/sidekiq/web.rb
module Sidekiq
  class Web
    def call(env)
      app.call(env)
    end

    def self.call(env)
      @app ||= new
      @app.call(env)
    end

    def app
      @app ||= build
    end

    def build
      klass = self.class
      m = middlewares

      rules = []
      rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]

      ::Rack::Builder.new do
        use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
          root: ASSETS,
          cascade: true,
          header_rules: rules
        m.each { |middleware, block| use(*middleware, &block) }
        use Sidekiq::Web::CsrfProtection unless $TESTING
        run WebApplication.new(klass)
      end
    end
  end
end

路由

Sidekiq::WebApplication.extend WebRouter后,获得了定义路由的方法

# sidekiq-6.5.7/lib/sidekiq/web/application.rb
module Sidekiq
  class WebApplication
    extend WebRouter

    head "/" do
      # ...
    end

    get "/" do
      # ...
    end
  end
end

这些方法来自sidekiq-6.5.7/lib/sidekiq/web/router.rb。在其中把路径打印出来

 module Sidekiq
   module WebRouter
     def head(path, &block)
       route(HEAD, path, &block)
     end

     def get(path, &block)
       route(GET, path, &block)
     end

     # ...

     def route(method, path, &block)
       @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []} 
+      puts "#{method} #{path}"
       @routes[method] << WebRoute.new(method, path, block)
     end
   end
 end

可得

HEAD /
GET /
GET /metrics
GET /metrics/:name
GET /busy
POST /busy
GET /queues
GET /queues/:name
POST /queues/:name
POST /queues/:name/delete
GET /morgue
GET /morgue/:key
POST /morgue
POST /morgue/all/delete
POST /morgue/all/retry
POST /morgue/:key
GET /retries
GET /retries/:key
POST /retries
POST /retries/all/delete
POST /retries/all/retry
POST /retries/all/kill
POST /retries/:key
GET /scheduled
GET /scheduled/:key
POST /scheduled
POST /scheduled/:key
GET /dashboard/stats
GET /stats
GET /stats/queues

GET / 首页,取历史数据

取多天的stat:processed:xxx和stat:failed:xxx,生成曲线

get "/" do
  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
  days = (params["days"] || 30).to_i
  return halt(401) if days < 1 || days > 180

  stats_history = Sidekiq::Stats::History.new(days)
  @processed_history = stats_history.processed
  @failed_history = stats_history.failed

  erb(:dashboard)
end

GET /stats 首页轮询,更新每X秒的曲线

会用本次processed/failed减上次processed/failed,生成曲线

get "/stats" do
  sidekiq_stats = Sidekiq::Stats.new
  redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
  json(
    sidekiq: {
      processed: sidekiq_stats.processed, # 执行次数
      failed: sidekiq_stats.failed, # 失败次数
      busy: sidekiq_stats.workers_size, # 执行中的任务数
      processes: sidekiq_stats.processes_size, # 进程数
      enqueued: sidekiq_stats.enqueued, # 所有队列当前总长
      scheduled: sidekiq_stats.scheduled_size, # 定时任务当前数量
      retries: sidekiq_stats.retry_size, # 重试任务当前数量
      dead: sidekiq_stats.dead_size, # 死任务当前数量
      default_latency: sidekiq_stats.default_queue_latency # default队列的延迟
    },
    redis: redis_stats, # 调用redis的info命令所返回的数据
    server_utc_time: server_utc_time
  )
end