사이드킥
참고하기 : sidekiq wiki
Sidekiq runs on 3 components
- The Rails Application
- Sidekiq worker process(es) => sidekiq 프로세스가 멀티쓰레드으로 돌면서 background에서 큐에 들은 worker/job 수행
- Redis => 큐
Sidekiq handles concurrency by using multiple threads in its process. This way, it can process multiple jobs at once, each thread processing one job at a time. By default, Sidekiq uses 10 threads per process. You can configure it to use more threads, thus increasing concurrency.
* 참고로 [ 0 of 5 busy ] 에서
실행시 concurrency를 5로 주었고, 사이드킥 job lifecycle에 따르면 busy는 currently processing인 상태라고 함.
그런데 이 프로세스의 쓰레드를 확인해보니까 왜 15개나 있는거지?
15개는 native thread이고, concurrency로 명시해준 5개는 user level thread 라서 m:n mapping 으로 도는건가....?
1. 레디스 서버 켜기
redis-server
2. gem 설치
gem 'sidekiq'
gem 'sidekiq-cron'
3. dashboard 보기
# routes.rb 파일에 추가하기
# localhost:3000/sidekiq에서 대시보드 확인한다
require 'sidekiq/web'
mount Sidekiq::Web => "/sidekiq"
4. config file
1) config/initializers/sidekiq.rb => 레디스 연결
redis_params = {
url: "redis://localhost:6379",
# namespace: "sidekiq" # 이렇게 쓰려면 'redis-namespace' gem이 필요하다
}
# Server config
Sidekiq.configure_server do |config|
config.redis = redis_params
end
# Client config
Sidekiq.configure_client do |config|
config.redis = redis_params
end
client => 레디스로 엔큐하는 주체들. The client is anything that pushes jobs to Redis, typically your passenger, puma or unicorn process running Rails or Sinatra.
server => 레디스에서 디큐하는 사이드킥 프로세스. The server is the Sidekiq process which pulls jobs from Redis.
a Sidekiq server process can push new jobs to Redis thus acting like a client too!
* 레일즈와 사이드킥 프로세스가 서로 다른 서버에서 동작하는 것도 가능한데, 그러려면 이 config에서 레일즈와 사이드킥이 같은 레디스 서버와 연결되어 있도록 해줘야 한다.
stack overflow 질문 : sidekiq remote server setup
2) config/sidekiq.yml => sidekiq 실행할때의 command line argument 들을 파일로 저장한 것
예를 들면 이렇게 생겼다.
# :pidfile: ./tmp/pids/sidekiq.pid # rails6 부터 pid file creation 없어짐. 이거 적어도 효과 없음
:concurrency: 5
staging:
:concurrency: 10
production:
:concurrency: 10
:queues:
- default
- foo
- bar
default location에 저장하면 sidekiq 실행할 때 알아서 이 파일 읽어들이지만 그게 아니라면 -C flag로 파일 위치를 명시해줘야 한다.
sidekiq -C config/myapp_sidekiq.yml
-c => concurrency ( 쓰레드 개수 인가? )
-q => 큐 [, weight]
기타 option flag
sidekiq -q critical,2 -q default
# ...
:queues:
- [critical, 2]
- default
이거 두개가 같은 효과! ( critical 큐 (는 weight 2로), default 큐에 들어있는 worker / job 을 수행한다 )
yaml 파일에 적어줬든, sidekiq 실행할 때 cli argument로 줬든 명시한 큐에서만 디큐한다.
다른 큐에는 perform_async 등의 명령어로 엔큐해서 쌓여있어도 사이드킥이 거기있는 작업은 수행 안함. 대시보드에서 확인해봐도 그냥 쌓여있음.
5. worker / job 생성하기
Worker/ Job 클래스 안의 perform 메소드 => 백그라운드에서 비동기로 할 작업 정의
( worker / job 클래스).perform_async() 등의 메소드 호출 => 작업 레디스 큐에 엔큐하기
2가지 방식 중 택 1 하면 된다
(1) sidekiq worker 생성 => sidekiq gem에서 제공하는 방식
할일 정의
rails g sidekiq:worker BlogPost
app/workers 디렉토리에 생성된다.
class BlogPostWorker
include Sidekiq::Worker
sidekiq_options queue: :bar
def perform(subject, title, article)
Post.create(subject: subject, title: title, article: article)
end
end
특별히 큐를 명시하지 않으면 default로 들어감
enqueue하기
perform_async ⇒ HardWorker.perform_async('bob', 5)
perform_in ⇒ HardWorker.perform_in(5.minutes, 'bob', 5)
perform_at ⇒ HardWorker.perform_at(5.minutes.from_now, 'bob', 5)
(2) Active Job 생성 => rails의 Active Job의 backend queue를 사이드킥으로 연동해서 사용하는 방식
config/application.rb 파일에 이렇게 적어준다
# config/application.rb
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
이렇게 설정파일에서 따로 queuing backend를 지정해주지 않으면 Active Job은 background에서 비동기로 실행되는게 아니라 그냥 바로 실행되어 버린다. (perform_later 하면 그냥 바로..)
Rails by default comes with an "immediate runner" queuing implementation. That means that each job that has been enqueued will run immediately.
할일 정의
rails g job blog_post
# 큐 지정
rails g job blog_post --queue foo
app/jobs 디렉토리에 생성된다
class BlogPostJob < ApplicationJob
queue_as :default
def perform(subject, title, article)
Post.create(subject: subject, title: title, article: article)
end
end
enqueue 하기
perform_later
GuestsCleanupJob.perform_later guest
GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
6. sidekiq 실행
레디스에서 dequeue 해서 작업 수행 한다. 멀티쓰레드로.
sidekiq을 실행하지 않고 레일즈 코드 내에서나, 레일즈 콘솔에서 perform_async를 하면 큐에 쌓이기만 하고 아직 작업이 수행되진 않는다. sidekiq 프로세스(=일꾼)를 실행해야 비로소 큐가 비고 정의한 작업이 수행된다.
bundle exec sidekiq
# bundle exec 생략하고 그냥 sidekiq 만 적어줘도 됨
이때 위에서 언급한 command line option들을 줄 수 있다.
sidekiq -q foo