Header

  1. View current page

    딥뿔이 자라나는 노트

Profile_image?t=1225424611&type=big
나를 바꾼 똑똑한 생활 습관, 스프링노트 - 여러분도 지금 시작해보세요!
71

레일스에서 특정 작업 비동기로 처리하기- Delayed Job 플러그인

GTD(Getting Things Done) 플로우를 보면 이런 부분이 있다.

 

그림_13.png

그림 출처: 3 more GTD wallpapers!

 

일을 할 때는 다음 작업을 찾아서, 2분안에 할 수 있으면 당장 해버리고 그렇지 않으면 미루거나 위임하라는 것이다. 이런 원칙은 빠른 응답이 생명인 요즘 웹 개발에도 똑같이 적용된다. 오래 걸릴만한 일은 미루고 일단 사용자에게 응답을 보내라는 것이다.

 

약간 억지스러웠지만, 어쨌든 웹 애플리케이션을 개발할 때 비동기 작업이 필요한 경우는 꽤 많다. 예를 들어 스프링노트처럼 내 데이터 전체를 내려받게 해주는 기능이 있고 해보자. 이 기능은 오래 걸리기도 하지만, DB 쿼리도 많고, 압축이라도 한다면 CPU, Disk를 많이 소모하는 무거운 기능이다. 이런 기능은 비동기로 처리하는게 좋다.

 

비동기로 작업이 떠오르는 경우는 이럴때다.

 

  • '짧은 시간'안에 끝날 것이라는 보장할 수 없는 작업
  • 너무 많은 시스템 리소스를 소비하는 무거운 작업

 

특히나 레일스 애플리케이션은 비동기 작업이 더 절실한데.. 그 이유는 몽그렐 클러스터가 필요한 이유에서 찾아보기 바란다.

 

당신의 선택은?

비동기 처리를 위해 사용할 수 있는 솔루션은 너무나도 많고 다양하다. 많이 언급되는 것들 중 몇가지를 선택해서 테스트한 후 고르는 것이 좋겠다. 아니면 일반화해서 나중에 플러그인만 바꿔 끼울 수 있도록 하는 것도 좋은 접근이다!

 

그림_14.png

<그림> 선택이 너무 많다. 출처는 Handling Long-Running Tasks in Rails

 

내가 솔루션을 선택하는 방법은 일단 내가 가진 요구사항을 충족하는 '가장 가벼운' 것을 고르는 것이다. 혹시 나중에 필요할지 모르는 기능은 머릿속에서 지운다. 그리고 나중에 진짜 그 기능이 필요해지면, 그 때 고민해도 좋다. 그런 근거로 내가 선택한 것은 Shopify의 일부로 개발되었다는 Delayed Job(DJ)이다. github팀의 이 글에도 큰 영향을 받았다. 이 팀에서 DJ를 선택한 이유를 아래처럼 설명하고 있다.

 

It is simple, required no research beyond the short README, works wonderfully with Rails, is fast, is hackable, solves both the queue and the worker problems, and has no external dependecies. Also, it’s hosted on GitHub!

 

Delayed Job 설정 및 사용법

DJ는 DB를 Job Queue로 사용하므로, 아래처럼 마이그레이션을 수행해줘야 한다.

 

  1. create_table :delayed_jobs, :force => true do |table|
      table.integer :priority, :default => 0
      table.integer :attempts, :default => 0
      table.text :handler
      table.string :last_error
      table.datetime :run_at
      table.datetime :locked_at
      table.datetime :failed_at
      table.string :locked_by
      table.timestamps
    end

 

그리고 Job 객체를 만들어 위 테이블에 직렬화해서 넣어준다.

 

  1. class NewsletterJob < Struct.new(:text, :emails)
      def perform
       emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
      end
    end

 

큐에 작업을 넣는 코드는 아래와 같다.

 

  1. Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))

 

이게 DJ 사용법의 거의 전부다. 별도의 S/W를 설치할 필요가 없고, 사용법이 간결한 것이 가장 큰 장점이다.

 

Job Runner

이제, Job Queue에 담긴 내용을 꺼내서 실행하는 job_runner를 만들어준다. 아래와 같은 내용으로 script/job_runner를 작성한다.

 

  1. #!/usr/bin/env ruby

    unless ARGV.size == 2
      $stderr.puts "USAGE: #{$0} environment pid_file_path"
      exit 1
    end

    RAILS_ENV = ARGV[0]
    File.open(ARGV[1], 'w+') {|f| f.write("#{$$}") }

    require File.dirname(__FILE__) + '/../config/environment'
    Delayed::Worker.new.start

 

monit을 이용한 모니터링과 배포

44570084_5be779d27d_m.jpg이 작업을 하기전에 가장 궁금했던 것이, 어떻게 job_runner를 관리하는가 였다. 여기서 관리라 함은,

 

  • 배포 후 최신 버전을 띄운다
  • job_runner가 잘 돌아가고 있는지 모니터링 한다.

 

먼저 job_runner를 구동하는 컨트롤 프로그램을 작성한다. /bin/job_runner_ctl이다.

 

  1. #!/bin/bash
    cd /path/to/app

    if [ "$1" == "start" ]; then
      nohup /usr/bin/ruby script/job_runner production /path/to/log/job_runner.pid &
    else
      ps ux | awk '/script\/job_runner/ && !/awk/ {print $2}' |xargs -i kill {}
      rm /path/to/log/job_runner.pid
    fi

 

그리고 job_runner_ctl을 monit에 등록한다.

 

  1. check process job_runner with pidfile /path/to/log/job_runner.pid
      start program = "/bin/job_runner_ctl start"
      stop program = "/bin/job_runner_ctl stop"
     
      if totalmem is greater than 1024.0 MB for 5 cycles then restart # eating up memory?
      if cpu is greater than 50% for 2 cycles then alert # s an email to admin
      if cpu is greater than 80% for 3 cycles then restart # hung process?
      if loadavg(5min) greater than 10 for 8 cycles then restart # bad, bad, bad
      if 3 restarts within 5 cycles then timeout # something is wrong, call the sys-admin

 

마지막으로 할 일은 Capistrano의 restart 작업에 job_runner를 재시작하는 명령을 추가하는 일이다.

 

  1. desc "restart web application servers"
    task :restart, :roles => :app do
      # (..omitted..)
      run "monit restart job_runner"
    end

 

마치며

꽤 걱정을 했는데, 그에 비해 별 문제없이 Delayed Job을 서비스에 적용할 수 있었다. 조금 더 대용량을 다루는 사이트가 되면 그때는 Beanstalkd, starling, ActiveMQ 등을 고려해야할까? 일단은 defer it!

 

 

Tags

History

Last edited on 12/08/2008 17:42 by deepblue

Comments (1)

You must log in to leave a comment. Please sign in.