droid 1.0.2pre → 1.0.2
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/Rakefile +22 -22
- data/VERSION +1 -1
- data/bin/bleedq +1 -1
- data/droid.gemspec +2 -2
- data/examples/async_reply.rb +15 -15
- data/examples/heroku_async_reply.rb +12 -12
- data/examples/sync.rb +13 -13
- data/examples/worker.rb +46 -46
- data/lib/droid.rb +122 -122
- data/lib/droid/em.rb +53 -53
- data/lib/droid/heroku.rb +83 -83
- data/lib/droid/json_server.rb +104 -104
- data/lib/droid/monkey.rb +6 -6
- data/lib/droid/publish.rb +22 -25
- data/lib/droid/queue.rb +194 -194
- data/lib/droid/request.rb +106 -106
- data/lib/droid/sync.rb +74 -74
- data/lib/droid/utilization.rb +96 -96
- data/lib/droid/utils.rb +107 -107
- data/spec/publish_spec.rb +20 -20
- data/spec/response_spec.rb +43 -43
- data/spec/utils_spec.rb +39 -39
- data/spec/wait_for_port_spec.rb +9 -9
- metadata +5 -24
data/lib/droid/request.rb
CHANGED
@@ -1,110 +1,110 @@
|
|
1
1
|
require 'droid/utils'
|
2
2
|
|
3
3
|
class Droid
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
4
|
+
class UnknownReplyTo < RuntimeError; end
|
5
|
+
|
6
|
+
class Request
|
7
|
+
attr_reader :qobj, :header, :raw_message
|
8
|
+
attr_reader :droid_headers, :msg, :start
|
9
|
+
|
10
|
+
def initialize(qobj, header, raw_message)
|
11
|
+
@qobj = qobj
|
12
|
+
@header = header
|
13
|
+
@raw_message = raw_message
|
14
|
+
|
15
|
+
@droid_headers = Droid::Utils.parse_custom_headers(header.headers)
|
16
|
+
@msg = Droid::Utils.parse_message(raw_message)
|
17
|
+
|
18
|
+
@start = Time.now.getgm.to_i
|
19
|
+
|
20
|
+
@acked = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def q
|
24
|
+
qobj.q
|
25
|
+
end
|
26
|
+
|
27
|
+
def ex
|
28
|
+
qobj.ex
|
29
|
+
end
|
30
|
+
|
31
|
+
def mq
|
32
|
+
qobj.mq
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](field)
|
36
|
+
msg[field.to_s]
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :params :msg
|
40
|
+
|
41
|
+
def age
|
42
|
+
return -1 unless droid_headers[:published_on]
|
43
|
+
start - droid_headers[:published_on]
|
44
|
+
end
|
45
|
+
|
46
|
+
def ttl
|
47
|
+
droid_headers[:ttl] || -1
|
48
|
+
end
|
49
|
+
|
50
|
+
def expired?
|
51
|
+
return false if ttl == -1
|
52
|
+
age > ttl
|
53
|
+
end
|
54
|
+
|
55
|
+
def acked?
|
56
|
+
@acked == true
|
57
|
+
end
|
58
|
+
|
59
|
+
def ack
|
60
|
+
return if acked?
|
61
|
+
header.ack
|
62
|
+
@acked = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def reply(data, opts={}, popts={})
|
66
|
+
opts.merge!(default_publish_opts)
|
67
|
+
reply_to = droid_headers[:reply_to] || self.msg['reply_to']
|
68
|
+
raise UnknownReplyTo unless reply_to
|
69
|
+
Droid.publish_to_q(reply_to, data, opts, popts)
|
70
|
+
end
|
71
|
+
|
72
|
+
def publish(name, data, opts={}, popts={}, &block)
|
73
|
+
opts = default_publish_opts.merge(opts)
|
74
|
+
if block
|
75
|
+
opts[:reply_to] ||= Droid::Utils.generate_reply_to(name)
|
76
|
+
Droid::ReplyQueue.new(opts[:reply_to]).subscribe(&block)
|
77
|
+
end
|
78
|
+
Droid.publish(name, data, opts, popts)
|
79
|
+
end
|
80
|
+
|
81
|
+
def requeue(ropts={})
|
82
|
+
h = droid_headers.dup
|
83
|
+
h[:requeued] = true
|
84
|
+
h.delete(:ttl)
|
85
|
+
popts = { :headers => h }
|
86
|
+
ropts[:ttl] ||= 10
|
87
|
+
Droid.publish_to_q(q.name, msg, ropts, popts)
|
88
|
+
end
|
89
|
+
|
90
|
+
def defer(&blk)
|
91
|
+
EM.defer(lambda do
|
92
|
+
begin
|
93
|
+
blk.call
|
94
|
+
rescue => e
|
95
|
+
Droid.handle_error(e)
|
96
|
+
end
|
97
|
+
end)
|
98
|
+
end
|
99
|
+
|
100
|
+
def default_publish_opts
|
101
|
+
{
|
102
|
+
:event_hash => droid_headers[:event_hash]
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def data_summary
|
107
|
+
Droid::Utils.format_data_summary(msg, droid_headers)
|
108
|
+
end
|
109
|
+
end
|
110
110
|
end
|
data/lib/droid/sync.rb
CHANGED
@@ -3,86 +3,86 @@ require 'bunny'
|
|
3
3
|
require 'system_timer'
|
4
4
|
|
5
5
|
class Droid
|
6
|
-
|
6
|
+
class SyncException < RuntimeError; end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def self.reset_bunny
|
9
|
+
@@bunny = nil
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
def self.bunny
|
13
|
+
@@bunny ||= begin
|
14
|
+
b = Bunny.new(default_config)
|
15
|
+
b.start
|
16
|
+
b
|
17
|
+
end
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def self.start(name, opts={})
|
21
|
+
raise SyncException, "start block is not allowed under sync operation"
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
24
|
+
def self.reconnect_on_error
|
25
|
+
SystemTimer::timeout(20) do
|
26
|
+
begin
|
27
|
+
yield if block_given?
|
28
|
+
rescue Bunny::ProtocolError
|
29
|
+
sleep 0.5
|
30
|
+
retry
|
31
|
+
rescue Bunny::ConnectionError
|
32
|
+
sleep 0.5
|
33
|
+
reset_bunny
|
34
|
+
retry
|
35
|
+
rescue Bunny::ServerDownError
|
36
|
+
sleep 0.5
|
37
|
+
reset_bunny
|
38
|
+
retry
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
43
|
+
def self.pop(q)
|
44
|
+
begin
|
45
|
+
loop do
|
46
|
+
result = q.pop
|
47
|
+
result = result[:payload] if result.is_a?(Hash)
|
48
|
+
return JSON.parse(result) unless result == :queue_empty
|
49
|
+
sleep 0.1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
54
|
+
def self.call(queue_name, data, opts={}, popts={})
|
55
|
+
opts[:reply_to] ||= Droid::Utils.generate_reply_to(queue_name)
|
56
|
+
q = nil
|
57
|
+
begin
|
58
|
+
reconnect_on_error do
|
59
|
+
q = bunny.queue(opts[:reply_to], :auto_delete => true)
|
60
|
+
publish_to_ex(queue_name, data, opts, popts)
|
61
|
+
pop(q)
|
62
|
+
end
|
63
|
+
ensure
|
64
|
+
if q
|
65
|
+
q.delete rescue nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
70
|
+
# override publish methods
|
71
|
+
def self.publish_to_q(queue_name, data, opts={}, popts={})
|
72
|
+
reconnect_on_error do
|
73
|
+
q = bunny.queue(queue_name)
|
74
|
+
json, popts = Droid::Utils.format_publish(data, opts, popts)
|
75
|
+
q.publish(json, popts)
|
76
|
+
end
|
77
|
+
log.info "amqp_publish queue=#{queue_name} #{Droid::Utils.format_data_summary(data, popts[:headers])}" unless opts[:log] == false
|
78
|
+
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
80
|
+
def self.publish_to_ex(ex_name, data, opts={}, popts={})
|
81
|
+
reconnect_on_error do
|
82
|
+
ex = bunny.exchange(ex_name)
|
83
|
+
json, popts = Droid::Utils.format_publish(data, opts, popts)
|
84
|
+
ex.publish(json, popts)
|
85
|
+
end
|
86
|
+
log.info "amqp_publish exchange=#{ex_name} #{Droid::Utils.format_data_summary(data, popts[:headers])}" unless opts[:log] == false
|
87
|
+
end
|
88
88
|
end
|
data/lib/droid/utilization.rb
CHANGED
@@ -1,113 +1,113 @@
|
|
1
1
|
class Droid
|
2
|
-
|
3
|
-
|
2
|
+
module Utilization
|
3
|
+
extend self
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
@latency = 0.0
|
6
|
+
def latency=(val); @latency = val; end
|
7
|
+
def latency; @latency; end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
@@start = Time.now.getutc
|
10
|
+
@@data = { }
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def start
|
13
|
+
@@start
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def reinit
|
17
|
+
@@start = Time.now.getutc
|
18
|
+
@@data = { }
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
def data(topic)
|
22
|
+
@@data[topic] ||= {
|
23
|
+
'msgs' => 0,
|
24
|
+
'time' => 0.0
|
25
|
+
}
|
26
|
+
@@data[topic]
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def topics
|
30
|
+
@@data.keys
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
def record(topic, secs)
|
34
|
+
d = data(topic)
|
35
|
+
d['msgs'] += 1
|
36
|
+
d['time'] += secs
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
39
|
+
def calc_utilization(topic, t2=nil)
|
40
|
+
d = data(topic)
|
41
|
+
t1 = @@start
|
42
|
+
t2 ||= Time.now.getutc.to_i
|
43
|
+
secs = (t2 - t1)
|
44
|
+
secs = 1 if secs <= 0.0
|
45
|
+
if d['msgs'] == 0
|
46
|
+
avg = 0.0
|
47
|
+
else
|
48
|
+
avg = d['time'] / d['msgs']
|
49
|
+
end
|
50
|
+
utilization = d['time'] / secs
|
51
|
+
{
|
52
|
+
'avg' => avg,
|
53
|
+
'secs' => secs,
|
54
|
+
'utilization' => utilization,
|
55
|
+
'msgs' => d['msgs'],
|
56
|
+
'msgs_per_sec' => d['msgs'] / secs
|
57
|
+
}
|
58
|
+
end
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
def monitor(topic, opts={})
|
61
|
+
topic = 'temporary' if opts[:temp]
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
t1 = Time.now.getutc
|
64
|
+
begin
|
65
|
+
yield if block_given?
|
66
|
+
ensure
|
67
|
+
t2 = Time.now.getutc
|
68
|
+
record(topic, t2 - t1)
|
69
|
+
end
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
72
|
+
def report_data
|
73
|
+
data = {}
|
74
|
+
t2 = Time.now.getutc
|
75
|
+
topics.each do |topic|
|
76
|
+
data[topic] = calc_utilization(topic, t2)
|
77
|
+
end
|
78
|
+
data
|
79
|
+
end
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
def report_summary(data=nil)
|
82
|
+
data ||= report_data
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
84
|
+
summary = {
|
85
|
+
'avg' => 0.0,
|
86
|
+
'utilization' => 0.0,
|
87
|
+
'msgs' => 0,
|
88
|
+
'msgs_per_sec' => 0.0,
|
89
|
+
'secs' => 0.0
|
90
|
+
}
|
91
|
+
data.each do |topic, d|
|
92
|
+
summary['utilization'] += d['utilization']
|
93
|
+
summary['msgs'] += d['msgs']
|
94
|
+
summary['msgs_per_sec'] += d['msgs_per_sec']
|
95
|
+
summary['avg'] += d['avg']
|
96
|
+
summary['secs'] += d['secs']
|
97
|
+
end
|
98
|
+
if data.size < 1
|
99
|
+
summary['avg'] = 0.0
|
100
|
+
else
|
101
|
+
summary['avg'] /= data.size
|
102
|
+
end
|
103
103
|
|
104
|
-
|
105
|
-
|
104
|
+
summary
|
105
|
+
end
|
106
106
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
def report
|
108
|
+
data = report_data
|
109
|
+
summary = report_summary(data)
|
110
|
+
{ 'data' => data, 'summary' => summary }
|
111
|
+
end
|
112
|
+
end
|
113
113
|
end
|