Một công việc định kỳ cho đường ray: thực hành tốt nhất?


295

Cách tốt nhất để chạy các tác vụ theo lịch trình trong môi trường Rails là gì? Kịch bản / người chạy? Cào? Tôi muốn chạy nhiệm vụ cứ sau vài phút.


149
Đối với những người đến đây từ Google, hãy nhìn xa hơn câu trả lời được chấp nhận để có cách tiếp cận tốt hơn.
jrdioko

4
Câu trả lời bất cứ khi nào có vẻ hợp lý hơn câu trả lời được chấp nhận, đó là một bản hack cũ.
Cướp

2
Cũng xin lưu ý rằng ít nhất một câu trả lời cho rằng bạn đã cài đặt một viên ngọc nhất định.
Tass

Một vài trong số (những gì tôi phát hiện ra) các thực hành tốt được tóm tắt ở đây Wisecashhq.com/blog/wr-reliable-cron-jobs
Thibaut Barrère

Trong nhiều trường hợp, công việc cron là một mùi hôi. Viết lịch trình tốt hơn thông qua sidekiq / resque (hoặc nhân viên nền khác), hoặc viết một daemon (ít chức năng và có thể theo dõi). Các công việc định kỳ có ít nhất một vài điều xấu: 1) khóa cho một trường hợp là một nỗi đau; 2) giám sát không thể được thực hiện dễ dàng; 3) xử lý ngoại lệ nên được viết lại một cách thủ công; 4) không dễ dàng để khởi động lại; 5) tất cả các vấn đề trên dễ dàng giải quyết bởi nhân viên nền.
Dmitry Polushkin

Câu trả lời:


110

Tôi đang sử dụng phương pháp cào (được hỗ trợ bởi heroku )

Với một tệp có tên lib / task / cron.rake ..

task :cron => :environment do
  puts "Pulling new requests..."
  EdiListener.process_new_messages
  puts "done."
end

Để thực hiện từ dòng lệnh, đây chỉ là "rake cron". Lệnh này sau đó có thể được đưa vào bộ lập lịch cron / tác vụ của hệ điều hành như mong muốn.

Cập nhật đây là một câu hỏi và câu trả lời khá cũ! Một số thông tin mới:

  • dịch vụ cron heroku mà tôi tham chiếu đã được thay thế bởi Heroku Lập lịch
  • đối với các tác vụ thường xuyên (đặc biệt là nơi bạn muốn tránh chi phí khởi động môi trường Rails), cách tiếp cận ưa thích của tôi là sử dụng cron hệ thống để gọi một tập lệnh sẽ (a) chọc API webhook an toàn / riêng tư để gọi tác vụ được yêu cầu trong nền hoặc (b) trực tiếp thực hiện một nhiệm vụ trên hệ thống xếp hàng của bạn

Mục cron nên làm gì cho trường hợp này, để HĐH biết đường dẫn chính xác đến tác vụ cào?
jrdioko

13
NB: những ngày này tôi đang sử dụng bất cứ khi nào (xem câu trả lời của Jim Garvin), nhưng một mục cron thô để chạy tác vụ cào sẽ có dạng như: 30 4 * * * / bin / bash -l -c 'cd / opt / railsapp && RAILS_ENV = cào sản xuất cron --silent '
chậm trễ

1
Làm thế nào để bạn gọi điều này từ bàn điều khiển? Tôi đã làm load "#{Rails.root}/lib/tasks/cron.rake"rake cron, nhưng có NameError: không xác định biến cục bộ hoặc phương thức `cron 'cho main: Object
B Seven

3
Vấn đề với phương pháp này là sự :environmentphụ thuộc. Chúng tôi có một ứng dụng Rails rất nặng, mất nhiều thời gian để khởi động, Rake của chúng tôi được gọi mỗi phút và tiêu tốn nhiều tài nguyên hơn bắt đầu từ môi trường Rails thực thi tác vụ . Tôi muốn có một môi trường Rails đã được khởi động để được gọi thông qua cron, phải là một cái gì đó giữa phương pháp điều khiểnmôi trường cào .
fguillen

Thời gian của nhiệm vụ này là gì? Tôi đang sử dụng một điều kiện if. Tôi muốn biết làm thế nào thường xuyên này được chạy. Tôi không thể tìm thấy bất kỳ thông tin nào về điều này trong trang web heroku.
Shubham Chaudhary

254

Tôi đã sử dụng cực kỳ phổ biến Bất cứ khi nào vào các dự án phụ thuộc nhiều vào các nhiệm vụ theo lịch trình, và nó thật tuyệt. Nó cung cấp cho bạn một DSL đẹp để xác định các tác vụ theo lịch trình của bạn thay vì phải xử lý định dạng crontab. Từ README:

Bất cứ khi nào là một viên ngọc Ruby cung cấp một cú pháp rõ ràng để viết và triển khai các công việc định kỳ.

Ví dụ từ README:

every 3.hours do
  runner "MyModel.some_process"       
  rake "my:rake:task"                 
  command "/usr/bin/my_great_command"
end

every 1.day, :at => '4:30 am' do 
  runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end

22
Nếu nó được chạy mỗi phút, môi trường sẽ được khởi động lại mọi lúc, điều này có thể tốn kém. Có vẻ như github.com/ssoroka/scheduler_daemon tránh điều này.
lulalala

3
+1 để giữ cấu hình cron trong hệ thống kiểm soát phiên bản của bạn
brittohalloran

3
Tôi nghĩ rằng đây là giải pháp tốt nhất. Nếu bạn đang sử dụng đường ray, tôi nghĩ tốt hơn là viết mọi thứ vào đường ray. Với phương pháp này, bạn cũng có thể quên đi tác vụ cron khi thay đổi máy chủ, nó di chuyển cùng với ứng dụng.
Adrian Matteo

Có một Railscast tuyệt vời về Bất cứ khi nào thực sự hữu ích (một phiên bản miễn phí cũ hơn cũng sẽ xuất hiện).
aceofbassgreg

@Tony, Bất cứ khi nào về cơ bản là một ngôn ngữ cụ thể cho miền để viết các công việc định kỳ. Nó biên dịch thành cú pháp cron thông thường trên máy chủ rails của bạn và cron là những gì thực thi các công việc bạn chỉ định (thường thông qua người chạy đường ray).
Greg

19

Trong dự án của chúng tôi lần đầu tiên chúng tôi sử dụng bất cứ khi nào đá quý, nhưng phải đối mặt với một số vấn đề.

Sau đó, chúng tôi đã chuyển sang đá quý RUFUS SCHEDULER , hóa ra rất dễ dàng và đáng tin cậy để lên lịch các tác vụ trong Rails.

Chúng tôi đã sử dụng nó để gửi thư hàng tuần và hàng ngày, và thậm chí để chạy một số nhiệm vụ cào định kỳ hoặc bất kỳ phương pháp nào.

Mã được sử dụng trong này là như sau:

    require 'rufus-scheduler'

    scheduler = Rufus::Scheduler.new

    scheduler.in '10d' do
      # do something in 10 days
    end

    scheduler.at '2030/12/12 23:30:00' do
      # do something at a given point in time
    end

    scheduler.every '3h' do
      # do something every 3 hours
    end

    scheduler.cron '5 0 * * *' do
      # do something every day, five minutes after midnight
      # (see "man 5 crontab" in your terminal)
    end

Để tìm hiểu thêm: https://github.com/jmettraux/rufus-scheduler


1
Đối với rufus, vì tôi đã sử dụng nó cho cả các dự án ruby ​​đơn giản hoặc các ứng dụng đường ray đầy đủ.
Paulo Fidalgo

8
Bạn có thể nói rõ hơn một chút về các vấn đề bạn gặp phải khi nào không?
Công tước

câu trả lời tuyệt vời nhất
Darlan Dieterich

17

Giả sử các nhiệm vụ của bạn không mất quá nhiều thời gian để hoàn thành, chỉ cần tạo một bộ điều khiển mới với một hành động cho mỗi nhiệm vụ. Thực hiện logic của tác vụ dưới dạng mã của bộ điều khiển, sau đó thiết lập một cronjob ở cấp hệ điều hành sử dụng wget để gọi URL của bộ điều khiển này và hành động theo các khoảng thời gian thích hợp. Ưu điểm của phương pháp này là bạn:

  1. Có quyền truy cập đầy đủ vào tất cả các đối tượng Rails của bạn giống như trong một bộ điều khiển bình thường.
  2. Có thể phát triển và kiểm tra giống như bạn làm những hành động bình thường.
  3. Cũng có thể gọi nhiệm vụ của bạn adhoc từ một trang web đơn giản.
  4. Không tiêu thụ thêm bộ nhớ bằng cách kích hoạt các quy trình ruby ​​/ rails bổ sung.

12
Làm thế nào để ngăn chặn người khác truy cập vào nhiệm vụ này? Nếu tác vụ lấy cpu và gọi nó thường xuyên sẽ gây ra vấn đề.
sarunw

44
Tôi biết điều này đã được một thời gian trước đây, nhưng đây chắc chắn không phải là cách tốt nhất để làm công việc cron nữa. Tại sao phải đi qua giao diện web, vi phạm những gì giao diện thực sự đại diện, khi có nhiều cách khác để truy cập vào môi trường Rails?
Matchu

6
Trình độ chuyên môn "giả sử nhiệm vụ của bạn không mất quá nhiều thời gian để hoàn thành" có vẻ như rất lớn. Sẽ không tốt hơn nếu sử dụng một cách tiếp cận thường hữu ích hơn, và không chỉ trong những trường hợp mà các tác vụ rất nhanh? Bằng cách đó, bạn sẽ không liên tục đánh giá lại liệu nhiệm vụ này hay nhiệm vụ đó cần phải được viết lại bằng một cách tiếp cận khác.
iconoclast

77
Câu hỏi cũ này là kết quả hàng đầu của google cho "rails cron". Câu trả lời này là xa cách tiếp cận tốt nhất. Xin vui lòng xem các phản ứng khác cho đề nghị lành mạnh hơn.
Jim Garvin

2
Không phải là cách tốt nhất. Bạn có nhiều cách khác để truy cập Rails env thông qua công việc định kỳ mà không cần gọi dịch vụ REST. Phương pháp cào chắc chắn tốt hơn
Shine

10

các tác vụ kịch bản / chạy và cào là hoàn toàn tốt để chạy như các công việc định kỳ.

Đây là một điều rất quan trọng bạn phải nhớ khi chạy các công việc định kỳ. Họ có thể sẽ không được gọi từ thư mục gốc của ứng dụng của bạn. Điều này có nghĩa là tất cả các yêu cầu của bạn đối với các tệp (trái ngược với thư viện) phải được thực hiện với đường dẫn rõ ràng: ví dụ: File.dirname (__ FILE__) + "/ other_file". Điều này cũng có nghĩa là bạn phải biết cách gọi chúng một cách rõ ràng từ một thư mục khác :-)

Kiểm tra xem mã của bạn có hỗ trợ được chạy từ thư mục khác không

# from ~
/path/to/ruby /path/to/app/script/runner -e development "MyClass.class_method"
/path/to/ruby /path/to/rake -f /path/to/app/Rakefile rake:task RAILS_ENV=development

Ngoài ra, các công việc định kỳ có thể không chạy như bạn, vì vậy đừng phụ thuộc vào bất kỳ phím tắt nào bạn đặt .bashrc. Nhưng đó chỉ là một mẹo cron tiêu chuẩn ;-)


Bạn có thể chạy công việc như bất kỳ người dùng nào (chỉ cần đặt mục nhập crontab cho người dùng bạn muốn) nhưng bạn đã đúng rằng hồ sơ và tập lệnh đăng nhập sẽ không chạy và bạn sẽ không bắt đầu trong thư mục chính của mình. Vì vậy, thông thường bắt đầu lệnh bằng "cd" như trong bình luận của @ luke-franci's
Tom Wilson

10

Vấn đề với bất cứ khi nào (và cron) là nó tải lại môi trường đường ray mỗi khi nó được thực thi, đây là một vấn đề thực sự khi các tác vụ của bạn thường xuyên hoặc có rất nhiều công việc khởi tạo phải làm. Tôi đã có vấn đề trong sản xuất vì điều này và phải cảnh báo bạn.

Bộ lập lịch Rufus làm điều đó cho tôi ( https://github.com/jmettraux/rufus-scheduler )

Khi tôi có các công việc dài để chạy, tôi sử dụng nó với delay_job ( https://github.com/collectiveidea/delayed_job )

Tôi hi vọng cái này giúp được!


10

Tôi là một fan hâm mộ lớn của resque / scheduler resque . Bạn không chỉ có thể chạy lặp lại các tác vụ giống như cron mà cả các tác vụ vào những thời điểm cụ thể. Nhược điểm là, nó yêu cầu máy chủ Redis.


10

Đó là điều thú vị không ai nhắc đến Sidetiq . Đây là một bổ sung tuyệt vời nếu bạn đã sử dụng Sidekiq.

Sidetiq cung cấp một API đơn giản để xác định các nhân viên định kỳ cho Sidekiq.

Công việc sẽ như thế này:

class MyWorker
  include Sidekiq::Worker
  include Sidetiq::Schedulable

  recurrence { hourly.minute_of_hour(15, 45) }

  def perform
    # do stuff ...
  end
end

8

Cả hai sẽ hoạt động tốt. Tôi thường sử dụng kịch bản / người chạy.

Đây là một ví dụ:

0 6 * * * cd /var/www/apps/your_app/current; ./script/runner --environment production 'EmailSubscription.send_email_subscriptions' >> /var/www/apps/your_app/shared/log/send_email_subscriptions.log 2>&1

Bạn cũng có thể viết tập lệnh Ruby thuần để thực hiện việc này nếu bạn tải đúng tệp cấu hình để kết nối với cơ sở dữ liệu của mình.

Một điều cần lưu ý nếu bộ nhớ là quý giá là tập lệnh / nhân vật chạy (hoặc tác vụ Rake phụ thuộc vào 'môi trường') sẽ tải toàn bộ môi trường Rails. Nếu bạn chỉ cần chèn một số bản ghi vào cơ sở dữ liệu, điều này sẽ sử dụng bộ nhớ mà bạn không thực sự phải làm. Nếu bạn viết kịch bản của riêng bạn, bạn có thể tránh điều này. Tôi chưa thực sự cần phải làm điều này, nhưng tôi đang xem xét nó.


8

Sử dụng Craken (công việc cron centric centre)


1
viết công việc cron rất khó, tốt hơn là tải xuống một viên đá quý cho điều đó
f0ster

1
nó không khó - nhưng để chúng được lưu trữ trong git và luôn cập nhật khi triển khai là một điểm cộng lớn khi một người làm việc trong một nhóm.
Thibaut Barrère

5

Tôi sử dụng nền tảng.

http://backgroundrb.rubyforge.org/

Tôi sử dụng nó để chạy các tác vụ theo lịch trình cũng như các tác vụ mất quá nhiều thời gian cho mối quan hệ máy khách / máy chủ bình thường.


3

Đây là cách tôi đã thiết lập các nhiệm vụ cron của mình. Tôi có một cái để tạo các bản sao lưu hàng ngày của cơ sở dữ liệu SQL (sử dụng rake) và một cái khác để hết hạn bộ nhớ cache mỗi tháng một lần. Bất kỳ đầu ra nào được ghi vào một tệp nhật ký / cron_log. Crontab của tôi trông như thế này:

crontab -l # command to print all cron tasks
crontab -e # command to edit/add cron tasks

# Contents of crontab
0 1 * * * cd /home/lenart/izziv. whiskas.si/current; /bin/sh cron_tasks >> log/cron_log 2>&1
0 0 1 * * cd /home/lenart/izziv.whiskas.si/current; /usr/bin/env /usr/local/bin/ruby script/runner -e production lib/monthly_cron.rb >> log/cron_log 2>&1

Tác vụ cron đầu tiên thực hiện sao lưu db hàng ngày. Nội dung của cron_t Nhiệm vụ như sau:

/usr/local/bin/rake db:backup RAILS_ENV=production; date; echo "END OF OUTPUT ----";

Tác vụ thứ hai được thiết lập sau đó và sử dụng tập lệnh / nhân vật để hết hạn bộ nhớ cache mỗi tháng một lần (lib / tháng_cron.rb):

#!/usr/local/bin/ruby
# Expire challenge cache
Challenge.force_expire_cache
puts "Expired cache for Challenges (Challenge.force_expire_cache) #{Time.now}"

Tôi đoán tôi có thể sao lưu cơ sở dữ liệu theo một cách khác nhưng cho đến nay nó hoạt động với tôi :)

Các đường dẫn đến cào và ruby ​​có thể khác nhau trên các máy chủ khác nhau. Bạn có thể thấy họ đang ở đâu bằng cách sử dụng:

whereis ruby # -> ruby: /usr/local/bin/ruby
whereis rake # -> rake: /usr/local/bin/rake

3

Sử dụng một cái gì đó Sidekiq hoặc Resque là một giải pháp mạnh mẽ hơn nhiều. Cả hai đều hỗ trợ thử lại công việc, độc quyền với khóa REDIS, giám sát và lập lịch.

Hãy nhớ rằng Resque là một dự án đã chết (không được duy trì tích cực), vì vậy Sidekiq là một cách thay thế tốt hơn. Nó cũng hiệu quả hơn: Sidekiq điều hành một số công nhân trong một quy trình đa luồng duy nhất trong khi Resque điều hành mỗi công nhân trong một quy trình riêng biệt.


Đó là một câu trả lời đúng. Nhiều người có thể quên đi các tính năng hay, mà sidekiq hoặc resque đang cung cấp, như giao diện web để theo dõi những gì đang xảy ra: số lượng công việc đang chạy, thất bại hoặc lên lịch, khởi động lại chúng dễ dàng, khóa cho công nhân độc đáo, điều chỉnh và giới hạn, v.v.
Dmitry Polushkin 12/03/2015

3

Gần đây tôi đã tạo ra một số công việc định kỳ cho các dự án tôi đang làm.

Tôi thấy rằng Đồng hồ đá quý rất hữu ích.

require 'clockwork'

module Clockwork
  every(10.seconds, 'frequent.job')
end

Bạn thậm chí có thể lên lịch công việc nền của bạn bằng cách sử dụng đá quý này. Để biết tài liệu và trợ giúp thêm, hãy tham khảo https://github.com/Rykian/clockwork



2

Một khi tôi đã phải đưa ra quyết định tương tự và tôi thực sự hạnh phúc với quyết định đó ngày hôm nay. Sử dụng bộ lập lịch resque vì không chỉ một redis riêng biệt sẽ tải tải từ db của bạn, bạn cũng sẽ có quyền truy cập vào nhiều plugin như resque-web cung cấp giao diện người dùng tuyệt vời. Khi hệ thống của bạn phát triển, bạn sẽ có nhiều nhiệm vụ hơn để lên lịch để bạn có thể kiểm soát chúng từ một nơi duy nhất.


1

Có lẽ cách tốt nhất để làm điều đó là sử dụng rake để viết các tác vụ bạn cần và chỉ cần thực hiện nó thông qua dòng lệnh.

Bạn có thể xem một video rất hữu ích tại railscasts

Ngoài ra hãy xem tài nguyên khác này:


Tôi đã cố gắng không thành công để sử dụng cú pháp trong hướng dẫn này. Nhiệm vụ đã không được thực hiện.
Tass

1

Tôi đã sử dụng đá quý đồng hồ và nó hoạt động khá tốt đối với tôi. Ngoài ra còn có clockworkdđá quý cho phép một kịch bản chạy như một daemon.


0

Tôi không thực sự chắc chắn, tôi đoán nó phụ thuộc vào nhiệm vụ: tần suất chạy, mức độ phức tạp và mức độ liên lạc trực tiếp với dự án đường ray là cần thiết, v.v ... Tôi đoán nếu chỉ có "Một cách tốt nhất" để làm gì đó , sẽ không có nhiều cách khác nhau để làm điều đó.

Ở công việc cuối cùng của tôi trong một dự án Rails, chúng tôi cần phải tạo một thư mời hàng loạt (thư mời khảo sát, không gửi thư rác) để gửi thư theo kế hoạch bất cứ khi nào máy chủ có thời gian. Tôi nghĩ rằng chúng tôi sẽ sử dụng các công cụ daemon để chạy các tác vụ cào mà tôi đã tạo.

Thật không may, công ty chúng tôi có một số vấn đề về tiền và đã bị đối thủ chính "mua" nên dự án chưa bao giờ được hoàn thành, vì vậy tôi không biết cuối cùng chúng tôi sẽ sử dụng cái gì.


0

Tôi sử dụng tập lệnh để chạy cron, đó là cách tốt nhất để chạy cron. Đây là một số ví dụ cho cron,

Mở CronTab -> sudo crontab -e

Và dán dòng Bellow:

00 00 * * * wget https: // your_host / some_API_end_point

Đây là một số định dạng cron, sẽ giúp bạn

::CRON FORMAT::

bảng định dạng cron

Examples Of crontab Entries
15 6 2 1 * /home/melissa/backup.sh
Run the shell script /home/melissa/backup.sh on January 2 at 6:15 A.M.

15 06 02 Jan * /home/melissa/backup.sh
Same as the above entry. Zeroes can be added at the beginning of a number for legibility, without changing their value.

0 9-18 * * * /home/carl/hourly-archive.sh
Run /home/carl/hourly-archive.sh every hour, on the hour, from 9 A.M. through 6 P.M., every day.

0 9,18 * * Mon /home/wendy/script.sh
Run /home/wendy/script.sh every Monday, at 9 A.M. and 6 P.M.

30 22 * * Mon,Tue,Wed,Thu,Fri /usr/local/bin/backup
Run /usr/local/bin/backup at 10:30 P.M., every weekday. 

Hy vọng điều này sẽ giúp bạn :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.