jobster 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/jobster +36 -0
- data/lib/jobster.rb +47 -0
- data/lib/jobster/job.rb +40 -0
- data/lib/jobster/version.rb +1 -1
- data/lib/jobster/worker.rb +144 -0
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f97863176fe30a7cef64b7fad42838ea0df3de1
|
4
|
+
data.tar.gz: 05d04d055613758d20d3be44f50379640b912263
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ad5c5c3c8780ba623c8284b6ce6eeee482bbb24d655e6d327f771442ff2e8bd7bc540923bdc647ac0e9dc3ceb5d722adc823ceaf3992b86b166bfe3d7373532
|
7
|
+
data.tar.gz: 826bcfaadb5641153f9a1296400cfac9c91fa8d043e1b5aafa4d613b7064a0ba163da62037234c6116f22ad958bd660e652edfcc6c111bb1663d6dd5f93d63cc
|
data/bin/jobster
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'jobster'
|
5
|
+
|
6
|
+
$stdout.sync = true
|
7
|
+
$stderr.sync = true
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.version = Jobster::VERSION
|
13
|
+
opts.banner = "Usage: jobster [options]"
|
14
|
+
opts.on("-c", "--config PATH", "The path to your jobster config file") do |config|
|
15
|
+
options[:config] = config
|
16
|
+
end
|
17
|
+
opts.on("-q", "--queues QUEUE1,QUEUE2", "Additional queues that you wish to subscribe to") do |queues|
|
18
|
+
queues.split(/,/).uniq.each do |queue|
|
19
|
+
Jobster::Worker.queues << queue
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end.parse!
|
24
|
+
|
25
|
+
if options[:config]
|
26
|
+
if File.file?(options[:config])
|
27
|
+
file = File.expand_path(options[:config])
|
28
|
+
require file
|
29
|
+
else
|
30
|
+
puts "Jobster config file not found at #{options[:config]}"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
worker = Jobster::Worker.new
|
36
|
+
worker.work
|
data/lib/jobster.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
require 'jobster/job'
|
3
|
+
require 'jobster/worker'
|
4
|
+
require 'jobster/version'
|
5
|
+
|
6
|
+
module Jobster
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def bunny
|
11
|
+
@bunny ||= begin
|
12
|
+
connection = Bunny.new
|
13
|
+
connection.start
|
14
|
+
connection
|
15
|
+
end
|
16
|
+
end
|
17
|
+
attr_writer :bunny
|
18
|
+
|
19
|
+
def logger
|
20
|
+
@logger ||= Logger.new(STDOUT)
|
21
|
+
end
|
22
|
+
attr_writer :logger
|
23
|
+
|
24
|
+
def ttl
|
25
|
+
@ttl ||= 60 * 60 * 10
|
26
|
+
end
|
27
|
+
attr_writer :ttl
|
28
|
+
|
29
|
+
def queue_prefix
|
30
|
+
@queue_prefix ||= "jobster"
|
31
|
+
end
|
32
|
+
attr_writer :queue_prefix
|
33
|
+
|
34
|
+
def channel
|
35
|
+
@channel ||= bunny.create_channel
|
36
|
+
end
|
37
|
+
|
38
|
+
def queue(name)
|
39
|
+
@queues ||= {}
|
40
|
+
@queues[name] ||= begin
|
41
|
+
channel.queue("#{queue_prefix}-#{name}", :durable => true, :arguments => {'x-message-ttl' => self.ttl})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/jobster/job.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Jobster
|
2
|
+
class Job
|
3
|
+
|
4
|
+
class Abort < StandardError; end
|
5
|
+
|
6
|
+
attr_reader :id
|
7
|
+
attr_reader :params
|
8
|
+
|
9
|
+
def initialize(id, params = {})
|
10
|
+
@id = id
|
11
|
+
@params = params.with_indifferent_access
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform
|
15
|
+
# Override in child jobs
|
16
|
+
end
|
17
|
+
|
18
|
+
def log(text)
|
19
|
+
Jobster.logger.info "[#{@id}] #{text}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.queue(queue = {}, params = {})
|
23
|
+
|
24
|
+
if queue.is_a?(Hash)
|
25
|
+
params = queue
|
26
|
+
queue = :main
|
27
|
+
end
|
28
|
+
|
29
|
+
job_id = SecureRandom.uuid[0,8]
|
30
|
+
job_payload = {'params' => params, 'class_name' => self.name, 'id' => job_id, 'queue' => queue}
|
31
|
+
Jobster.queue(queue).publish(job_payload.to_json, :persistent => false)
|
32
|
+
job_id
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.perform(params = {})
|
36
|
+
new(nil, params).perform
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/jobster/version.rb
CHANGED
@@ -0,0 +1,144 @@
|
|
1
|
+
module Jobster
|
2
|
+
class Worker
|
3
|
+
|
4
|
+
def initialize(queues = nil)
|
5
|
+
@initial_queues = queues || self.class.queues || [:main]
|
6
|
+
@active_queues = {}
|
7
|
+
@process_name = $0
|
8
|
+
end
|
9
|
+
|
10
|
+
def work
|
11
|
+
logger.info "Jobster worker started"
|
12
|
+
self.class.run_callbacks(:after_start)
|
13
|
+
|
14
|
+
@running_job = false
|
15
|
+
Signal.trap("INT") { @exit = true }
|
16
|
+
Signal.trap("TERM") { @exit = true }
|
17
|
+
|
18
|
+
Jobster.channel.prefetch(1)
|
19
|
+
@initial_queues.uniq.each { |queue | join_queue(queue) }
|
20
|
+
|
21
|
+
exit_checks = 0
|
22
|
+
loop do
|
23
|
+
if @exit && @running_job == false
|
24
|
+
logger.info "Exiting immediately because no job running"
|
25
|
+
self.class.run_callbacks(:before_quit, :immediate)
|
26
|
+
exit 0
|
27
|
+
elsif @exit
|
28
|
+
if exit_checks >= 300
|
29
|
+
logger.info "Job did not finish in a timely manner. Exiting"
|
30
|
+
self.class.run_callbacks(:before_quit, :timeout)
|
31
|
+
exit 0
|
32
|
+
end
|
33
|
+
if exit_checks == 0
|
34
|
+
logger.info "Exit requested but job is running. Waiting for job to finish."
|
35
|
+
end
|
36
|
+
sleep 5
|
37
|
+
exit_checks += 1
|
38
|
+
else
|
39
|
+
sleep 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def receive_job(delivery_info, properties, body)
|
47
|
+
@running_job = true
|
48
|
+
begin
|
49
|
+
message = JSON.parse(body) rescue nil
|
50
|
+
if message && message['class_name']
|
51
|
+
start_time = Time.now
|
52
|
+
$0 = "#{@process_name} (running #{message['class_name']})"
|
53
|
+
Thread.current[:job_id] = message['id']
|
54
|
+
logger.info "[#{message['id']}] Started processing \e[34m#{message['class_name']}\e[0m job"
|
55
|
+
begin
|
56
|
+
klass = Object.const_get(message['class_name']).new(message['id'], message['params'])
|
57
|
+
self.class.run_callbacks(:before_job, klass)
|
58
|
+
klass.perform
|
59
|
+
self.class.run_callbacks(:after_job, klass)
|
60
|
+
rescue Job::Abort => e
|
61
|
+
logger.info "[#{message['id']}] Job aborted (#{e.message})"
|
62
|
+
rescue => e
|
63
|
+
logger.warn "[#{message['id']}] \e[31m#{e.class}: #{e.message}\e[0m"
|
64
|
+
e.backtrace.each do |line|
|
65
|
+
logger.warn "[#{message['id']}] " + line
|
66
|
+
end
|
67
|
+
self.class.run_callbacks(:after_job, klass, e)
|
68
|
+
self.class.error_handlers.each { |handler| handler.call(e, klass) }
|
69
|
+
ensure
|
70
|
+
logger.info "[#{message['id']}] Finished processing \e[34m#{message['class_name']}\e[0m job in #{Time.now - start_time}s"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
ensure
|
74
|
+
Thread.current[:job_id] = nil
|
75
|
+
$0 = @process_name
|
76
|
+
Jobster.channel.ack(delivery_info.delivery_tag)
|
77
|
+
@running_job = false
|
78
|
+
if @exit
|
79
|
+
logger.info "Exiting because a job has ended."
|
80
|
+
self.class.run_callbacks(:before_quit, :job_completed)
|
81
|
+
exit 0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def join_queue(queue)
|
87
|
+
if @active_queues[queue]
|
88
|
+
logger.info "Attempted to join queue #{queue} but already joined."
|
89
|
+
else
|
90
|
+
self.class.run_callbacks(:before_queue_join, queue)
|
91
|
+
consumer = Jobster.queue(queue).subscribe(:manual_ack => true) do |delivery_info, properties, body|
|
92
|
+
receive_job(delivery_info, properties, body)
|
93
|
+
end
|
94
|
+
@active_queues[queue] = consumer
|
95
|
+
self.class.run_callbacks(:after_queue_join, queue, consumer)
|
96
|
+
logger.info "Joined \e[32m#{queue}\e[0m queue"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def leave_queue(queue)
|
101
|
+
if consumer = @active_queues[queue]
|
102
|
+
consumer.cancel
|
103
|
+
@active_queues.delete(queue)
|
104
|
+
logger.info "Left \e[32m#{queue}\e[0m queue"
|
105
|
+
else
|
106
|
+
logger.info "Not joined #{queue} so cannot leave"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def logger
|
111
|
+
Jobster.logger
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.queues
|
115
|
+
@queues ||= [:main]
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.error_handlers
|
119
|
+
@error_handlers ||= []
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.register_error_handler(&block)
|
123
|
+
error_handlers << block
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.callbacks
|
127
|
+
@callbacks ||= {}
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.add_callback(event, &block)
|
131
|
+
callbacks[event] ||= []
|
132
|
+
callbacks[event] << block
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.run_callbacks(event, *args)
|
136
|
+
if callbacks[event]
|
137
|
+
callbacks[event].each do |callback|
|
138
|
+
callback.call(*args)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jobster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Cooke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -33,11 +33,16 @@ dependencies:
|
|
33
33
|
description: A RabbitMQ-based job queueing system
|
34
34
|
email:
|
35
35
|
- me@adamcooke.io
|
36
|
-
executables:
|
36
|
+
executables:
|
37
|
+
- jobster
|
37
38
|
extensions: []
|
38
39
|
extra_rdoc_files: []
|
39
40
|
files:
|
41
|
+
- bin/jobster
|
42
|
+
- lib/jobster.rb
|
43
|
+
- lib/jobster/job.rb
|
40
44
|
- lib/jobster/version.rb
|
45
|
+
- lib/jobster/worker.rb
|
41
46
|
homepage: https://github.com/adamcooke/jobster
|
42
47
|
licenses:
|
43
48
|
- MIT
|
@@ -58,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
63
|
version: '0'
|
59
64
|
requirements: []
|
60
65
|
rubyforge_project:
|
61
|
-
rubygems_version: 2.5.
|
66
|
+
rubygems_version: 2.5.1
|
62
67
|
signing_key:
|
63
68
|
specification_version: 4
|
64
69
|
summary: A RabbitMQ-based job queueing system
|