cloudist 0.2.1 → 0.4.1
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.
- data/Gemfile +15 -11
- data/Gemfile.lock +20 -7
- data/README.md +61 -39
- data/VERSION +1 -1
- data/cloudist.gemspec +50 -16
- data/examples/amqp/Gemfile +3 -0
- data/examples/amqp/Gemfile.lock +12 -0
- data/examples/amqp/amqp_consumer.rb +56 -0
- data/examples/amqp/amqp_publisher.rb +50 -0
- data/examples/queue_message.rb +7 -7
- data/examples/sandwich_client_with_custom_listener.rb +77 -0
- data/examples/sandwich_worker_with_class.rb +22 -7
- data/lib/cloudist.rb +113 -56
- data/lib/cloudist/application.rb +60 -0
- data/lib/cloudist/core_ext/class.rb +139 -0
- data/lib/cloudist/core_ext/kernel.rb +13 -0
- data/lib/cloudist/core_ext/module.rb +11 -0
- data/lib/cloudist/encoding.rb +13 -0
- data/lib/cloudist/errors.rb +2 -0
- data/lib/cloudist/job.rb +21 -18
- data/lib/cloudist/listener.rb +108 -54
- data/lib/cloudist/message.rb +97 -0
- data/lib/cloudist/messaging.rb +29 -0
- data/lib/cloudist/payload.rb +45 -105
- data/lib/cloudist/payload_old.rb +155 -0
- data/lib/cloudist/publisher.rb +7 -2
- data/lib/cloudist/queue.rb +152 -0
- data/lib/cloudist/queues/basic_queue.rb +83 -53
- data/lib/cloudist/queues/job_queue.rb +13 -24
- data/lib/cloudist/queues/reply_queue.rb +13 -21
- data/lib/cloudist/request.rb +33 -7
- data/lib/cloudist/worker.rb +9 -2
- data/lib/cloudist_old.rb +300 -0
- data/lib/em/em_timer_utils.rb +55 -0
- data/lib/em/iterator.rb +27 -0
- data/spec/cloudist/message_spec.rb +91 -0
- data/spec/cloudist/messaging_spec.rb +19 -0
- data/spec/cloudist/payload_spec.rb +10 -4
- data/spec/cloudist/payload_spec_2_spec.rb +78 -0
- data/spec/cloudist/queue_spec.rb +16 -0
- data/spec/cloudist_spec.rb +49 -45
- data/spec/spec_helper.rb +0 -1
- data/spec/support/amqp.rb +16 -0
- metadata +112 -102
- data/examples/extending_values.rb +0 -44
- data/examples/sandwich_client.rb +0 -57
- data/lib/cloudist/callback.rb +0 -16
- data/lib/cloudist/callback_methods.rb +0 -19
- data/lib/cloudist/callbacks/error_callback.rb +0 -14
data/lib/cloudist/publisher.rb
CHANGED
@@ -4,10 +4,15 @@ module Cloudist
|
|
4
4
|
class << self
|
5
5
|
def enqueue(queue_name, data)
|
6
6
|
payload = Cloudist::Payload.new(data)
|
7
|
-
|
7
|
+
|
8
8
|
queue = Cloudist::JobQueue.new(queue_name)
|
9
|
+
|
9
10
|
queue.setup
|
10
|
-
|
11
|
+
|
12
|
+
# send_message = proc {
|
13
|
+
queue.publish(payload)
|
14
|
+
# }
|
15
|
+
# EM.next_tick(&send_message)
|
11
16
|
|
12
17
|
return Job.new(payload)
|
13
18
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Cloudist
|
2
|
+
class Queue
|
3
|
+
|
4
|
+
attr_reader :options, :name, :channel, :q, :ex
|
5
|
+
|
6
|
+
class_attribute :cached_queues
|
7
|
+
|
8
|
+
def initialize(name, options = {})
|
9
|
+
self.class.cached_queues ||= {}
|
10
|
+
|
11
|
+
options = {
|
12
|
+
:auto_delete => false,
|
13
|
+
:durable => true
|
14
|
+
}.update(options)
|
15
|
+
|
16
|
+
@name, @options = name, options
|
17
|
+
|
18
|
+
setup
|
19
|
+
p self.cached_queues.keys
|
20
|
+
|
21
|
+
log.debug(tag)
|
22
|
+
purge
|
23
|
+
end
|
24
|
+
|
25
|
+
def purge
|
26
|
+
q.purge
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"<#{self.class.name} name=#{name} exchange=#{ex ? ex.name : 'nil'}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
def log
|
34
|
+
Cloudist.log
|
35
|
+
end
|
36
|
+
|
37
|
+
def tag
|
38
|
+
[].tap { |a|
|
39
|
+
a << "queue=#{q.name}" if q
|
40
|
+
a << "exchange=#{ex.name}" if ex
|
41
|
+
}.join(' ')
|
42
|
+
end
|
43
|
+
|
44
|
+
def publish(msg)
|
45
|
+
raise ArgumentError, "Publish expects a Cloudist::Message object" unless msg.is_a?(Cloudist::Message)
|
46
|
+
|
47
|
+
body, headers = msg.encoded
|
48
|
+
# EM.defer {
|
49
|
+
publish_to_ex(body, headers)
|
50
|
+
# }
|
51
|
+
|
52
|
+
p msg.body.to_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
# def channel
|
56
|
+
# self.class.channel
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def q
|
60
|
+
# self.class.q
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def ex
|
64
|
+
# self.class.ex
|
65
|
+
# end
|
66
|
+
|
67
|
+
def publish_to_ex(body, headers = {})
|
68
|
+
ex.publish(body, headers)
|
69
|
+
end
|
70
|
+
|
71
|
+
def publish_to_q(body, headers = {})
|
72
|
+
q.publish(body, headers)
|
73
|
+
end
|
74
|
+
|
75
|
+
def teardown
|
76
|
+
q.unsubscribe
|
77
|
+
channel.close
|
78
|
+
log.debug "AMQP Unsubscribed: #{tag}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def destroy
|
82
|
+
teardown
|
83
|
+
end
|
84
|
+
|
85
|
+
def subscribe(options = {}, &block)
|
86
|
+
options[:ack] = true
|
87
|
+
q.subscribe(options) do |queue_header, encoded_message|
|
88
|
+
request = Cloudist::Request.new(self, encoded_message, queue_header)
|
89
|
+
|
90
|
+
msg = Cloudist::Message.new(*request.for_message)
|
91
|
+
|
92
|
+
EM.defer do
|
93
|
+
begin
|
94
|
+
raise Cloudist::ExpiredMessage if request.expired?
|
95
|
+
yield msg
|
96
|
+
|
97
|
+
rescue Cloudist::ExpiredMessage
|
98
|
+
log.error "AMQP Message Timeout: #{tag} ttl=#{request.ttl} age=#{request.age}"
|
99
|
+
|
100
|
+
rescue Exception => e
|
101
|
+
Cloudist.handle_error(e)
|
102
|
+
|
103
|
+
ensure
|
104
|
+
request.ack unless Cloudist.closing?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def setup
|
113
|
+
if self.class.cached_queues.keys.include?(name.to_sym)
|
114
|
+
@q = self.class.cached_queues[name.to_sym][:q]
|
115
|
+
@ex = self.class.cached_queues[name.to_sym][:ex]
|
116
|
+
@channel = self.class.cached_queues[name.to_sym][:channel]
|
117
|
+
setup_binding
|
118
|
+
else
|
119
|
+
puts "Setup"
|
120
|
+
|
121
|
+
setup_channel
|
122
|
+
setup_queue
|
123
|
+
setup_exchange
|
124
|
+
|
125
|
+
self.class.cached_queues[name.to_sym] = {:q => q, :ex => ex, :channel => channel}
|
126
|
+
end
|
127
|
+
setup_binding
|
128
|
+
end
|
129
|
+
|
130
|
+
def setup_channel
|
131
|
+
@channel = ::AMQP::Channel.new
|
132
|
+
|
133
|
+
# Set up QOS. If you do not do this then the subscribe in receive_message
|
134
|
+
# will get overwelmd and the whole thing will collapse in on itself.
|
135
|
+
channel.prefetch(1)
|
136
|
+
end
|
137
|
+
|
138
|
+
def setup_queue
|
139
|
+
@q = channel.queue(name, options)
|
140
|
+
end
|
141
|
+
|
142
|
+
def setup_exchange
|
143
|
+
@ex = channel.direct(name)
|
144
|
+
# setup_binding
|
145
|
+
end
|
146
|
+
|
147
|
+
def setup_binding
|
148
|
+
q.bind(ex)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
@@ -4,92 +4,122 @@ module Cloudist
|
|
4
4
|
|
5
5
|
module Queues
|
6
6
|
class BasicQueue
|
7
|
-
attr_reader :queue_name, :
|
8
|
-
attr_reader :
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
attr_reader :queue_name, :options
|
8
|
+
attr_reader :queue, :exchange, :channel, :prefetch
|
9
|
+
|
10
|
+
alias :q :queue
|
11
|
+
alias :ex :exchange
|
12
|
+
alias :mq :channel
|
13
|
+
|
14
|
+
def initialize(queue_name, options = {})
|
15
|
+
@prefetch ||= options.delete(:prefetch) || 1
|
16
|
+
|
17
|
+
options = {
|
12
18
|
:auto_delete => true,
|
13
19
|
:durable => false,
|
14
|
-
:
|
15
|
-
}.update(
|
20
|
+
:nowait => true
|
21
|
+
}.update(options)
|
16
22
|
|
17
|
-
@queue_name, @
|
23
|
+
@queue_name, @options = queue_name, options
|
24
|
+
|
25
|
+
setup
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
"<#{self.class.name} queue_name=#{queue_name}>"
|
18
30
|
end
|
19
31
|
|
20
32
|
def setup
|
21
|
-
return if @setup
|
22
|
-
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
return if @setup.eql?(true)
|
34
|
+
|
35
|
+
@channel ||= AMQP::Channel.new(Cloudist.connection) do
|
36
|
+
channel.prefetch(self.prefetch, false) if self.prefetch.present?
|
37
|
+
end
|
38
|
+
|
39
|
+
@queue = @channel.queue(queue_name, options)
|
40
|
+
|
41
|
+
setup_exchange
|
42
|
+
|
30
43
|
@setup = true
|
31
44
|
end
|
45
|
+
|
46
|
+
def setup_exchange
|
47
|
+
@exchange = channel.direct("")
|
48
|
+
end
|
49
|
+
|
50
|
+
# def setup_exchange
|
51
|
+
# @exchange = channel.direct(queue_name)
|
52
|
+
# queue.bind(exchange)
|
53
|
+
# end
|
32
54
|
|
33
55
|
def log
|
34
56
|
Cloudist.log
|
35
57
|
end
|
36
58
|
|
37
59
|
def tag
|
38
|
-
s = "queue=#{
|
39
|
-
s += " exchange=#{
|
60
|
+
s = "queue=#{queue.name}"
|
61
|
+
s += " exchange=#{exchange.name}" if exchange
|
40
62
|
s
|
41
63
|
end
|
42
|
-
|
43
|
-
def subscribe(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
|
65
|
+
def subscribe(&block)
|
66
|
+
queue.subscribe(:ack => true) do |queue_header, encoded_message|
|
67
|
+
# next if Cloudist.closing?
|
68
|
+
|
69
|
+
request = Cloudist::Request.new(self, encoded_message, queue_header)
|
70
|
+
|
71
|
+
handle_request = proc {
|
72
|
+
begin
|
73
|
+
raise Cloudist::ExpiredMessage if request.expired?
|
74
|
+
# yield request if block_given?
|
75
|
+
block.call(request)
|
76
|
+
|
77
|
+
rescue Cloudist::ExpiredMessage
|
78
|
+
log.error "AMQP Message Timeout: #{tag} ttl=#{request.ttl} age=#{request.age}"
|
79
|
+
|
80
|
+
rescue => e
|
81
|
+
Cloudist.handle_error(e)
|
82
|
+
ensure
|
83
|
+
request.ack
|
84
|
+
# unless Cloudist.closing?
|
85
|
+
# finished = Time.now.utc.to_i
|
86
|
+
# log.debug("Finished Job in #{finished - request.start} seconds")
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
handle_ack = proc {
|
91
|
+
request.ack
|
92
|
+
}
|
93
|
+
|
94
|
+
EM.defer(handle_request, handle_ack)
|
65
95
|
end
|
66
96
|
log.info "AMQP Subscribed: #{tag}"
|
67
97
|
self
|
68
98
|
end
|
69
99
|
|
70
100
|
def print_status
|
71
|
-
|
72
|
-
|
73
|
-
}
|
101
|
+
# queue.status{ |num_messages, num_consumers|
|
102
|
+
# log.info("STATUS: #{queue.name}: JOBS: #{num_messages} WORKERS: #{num_consumers+1}")
|
103
|
+
# }
|
74
104
|
end
|
75
105
|
|
76
106
|
def publish(payload)
|
77
107
|
payload.set_reply_to(queue_name)
|
78
|
-
body, headers = payload.
|
79
|
-
|
80
|
-
|
108
|
+
body, headers = payload.to_a
|
109
|
+
headers.merge!(:routing_key => queue.name)
|
110
|
+
exchange.publish(body, headers)
|
81
111
|
end
|
82
112
|
|
83
113
|
def publish_to_q(payload)
|
84
|
-
body, headers = payload.
|
85
|
-
|
86
|
-
|
114
|
+
body, headers = payload.to_a
|
115
|
+
# headers.merge!(:routing_key => queue.name)
|
116
|
+
queue.publish(body, headers)
|
87
117
|
return headers
|
88
118
|
end
|
89
119
|
|
90
120
|
def teardown
|
91
|
-
@
|
92
|
-
@
|
121
|
+
@queue.unsubscribe
|
122
|
+
@channel.close
|
93
123
|
log.debug "AMQP Unsubscribed: #{tag}"
|
94
124
|
end
|
95
125
|
|
@@ -1,28 +1,17 @@
|
|
1
1
|
module Cloudist
|
2
2
|
class JobQueue < Cloudist::Queues::BasicQueue
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def subscribe(amqp_opts={}, opts={})
|
18
|
-
amqp_opts[:ack] ||= true
|
19
|
-
super(amqp_opts, opts) do |request|
|
20
|
-
begin
|
21
|
-
yield request if block_given?
|
22
|
-
ensure
|
23
|
-
request.ack unless amqp_opts[:auto_ack] == false || Cloudist.closing?
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
3
|
+
|
4
|
+
# def initialize(queue_name, options={})
|
5
|
+
# @prefetch = 1
|
6
|
+
# # opts[:auto_delete] = false
|
7
|
+
#
|
8
|
+
# super(queue_name, options)
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def setup_exchange
|
12
|
+
# @exchange = channel.direct(queue_name)
|
13
|
+
# queue.bind(exchange)
|
14
|
+
# end
|
15
|
+
|
27
16
|
end
|
28
17
|
end
|
@@ -1,31 +1,23 @@
|
|
1
1
|
module Cloudist
|
2
2
|
class ReplyQueue < Cloudist::Queues::BasicQueue
|
3
|
-
def initialize(queue_name,
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
def initialize(queue_name, options={})
|
4
|
+
options[:auto_delete] = true
|
5
|
+
options[:nowait] = true
|
6
|
+
|
7
|
+
@prefetch = 2
|
8
|
+
|
9
|
+
super(queue_name, options)
|
7
10
|
end
|
8
|
-
|
9
|
-
def
|
10
|
-
|
11
|
-
@q = @mq.queue(queue_name, opts)
|
12
|
-
@ex = @mq.direct
|
13
|
-
if key
|
14
|
-
@q.bind(@ex, :key => key)
|
15
|
-
else
|
16
|
-
@q.bind(@ex)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# def subscribe(amqp_opts={}, opts={})
|
21
|
-
# super(amqp_opts, opts) do |request|
|
11
|
+
|
12
|
+
# def subscribe(&block)
|
13
|
+
# super do |request|
|
22
14
|
# yield request if block_given?
|
23
|
-
#
|
15
|
+
# teardown
|
24
16
|
# end
|
25
17
|
# end
|
26
|
-
|
18
|
+
|
27
19
|
# def teardown
|
28
|
-
#
|
20
|
+
# queue.delete
|
29
21
|
# super
|
30
22
|
# end
|
31
23
|
|
data/lib/cloudist/request.rb
CHANGED
@@ -1,31 +1,55 @@
|
|
1
1
|
module Cloudist
|
2
2
|
class Request
|
3
|
-
|
3
|
+
include Cloudist::Encoding
|
4
|
+
|
5
|
+
attr_reader :queue_header, :qobj, :payload, :start, :headers, :body
|
4
6
|
|
5
7
|
def initialize(queue, encoded_body, queue_header)
|
6
8
|
@qobj, @queue_header = queue, queue_header
|
7
9
|
|
10
|
+
@body = decode(encoded_body)
|
11
|
+
@headers = parse_custom_headers(queue_header)
|
12
|
+
|
8
13
|
@payload = Cloudist::Payload.new(encoded_body, queue_header.headers.dup)
|
9
|
-
@headers = @payload.
|
14
|
+
@headers = @payload.headers
|
10
15
|
|
11
|
-
@start = Time.now.utc.
|
16
|
+
@start = Time.now.utc.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_custom_headers(amqp_headers)
|
20
|
+
h = amqp_headers.headers.dup
|
21
|
+
|
22
|
+
h[:published_on] = h[:published_on].to_i
|
23
|
+
|
24
|
+
h[:ttl] = h[:ttl].to_i rescue -1
|
25
|
+
h[:ttl] = -1 if h[:ttl] == 0
|
26
|
+
|
27
|
+
h
|
28
|
+
end
|
29
|
+
|
30
|
+
def for_message
|
31
|
+
[body.dup, queue_header.headers.dup]
|
12
32
|
end
|
13
33
|
|
14
34
|
def q
|
15
|
-
qobj.
|
35
|
+
qobj.queue
|
16
36
|
end
|
17
37
|
|
18
38
|
def ex
|
19
|
-
qobj.
|
39
|
+
qobj.exchange
|
20
40
|
end
|
21
41
|
|
22
42
|
def mq
|
23
|
-
qobj.
|
43
|
+
qobj.channel
|
44
|
+
end
|
45
|
+
|
46
|
+
def channel
|
47
|
+
mq
|
24
48
|
end
|
25
49
|
|
26
50
|
def age
|
27
51
|
return -1 unless headers[:published_on]
|
28
|
-
start - headers[:published_on]
|
52
|
+
start - headers[:published_on].to_f
|
29
53
|
end
|
30
54
|
|
31
55
|
def ttl
|
@@ -45,6 +69,8 @@ module Cloudist
|
|
45
69
|
return if acked?
|
46
70
|
queue_header.ack
|
47
71
|
@acked = true
|
72
|
+
rescue AMQP::ChannelClosedError => e
|
73
|
+
Cloudist.handle_error(e)
|
48
74
|
end
|
49
75
|
|
50
76
|
end
|