droid 1.0.2pre → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|