istox 0.1.120 → 0.1.121
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.idea/istox-gem.iml +1693 -2
- data/Gemfile.lock +27 -8
- data/istox.gemspec +2 -0
- data/lib/istox.rb +2 -0
- data/lib/istox/helpers/bunny_boot.rb +155 -73
- data/lib/istox/helpers/publisher.rb +153 -62
- data/lib/istox/helpers/redis.rb +37 -0
- data/lib/istox/helpers/subscriber.rb +140 -0
- data/lib/istox/version.rb +1 -1
- metadata +33 -3
data/Gemfile.lock
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
istox (0.1.
|
4
|
+
istox (0.1.121)
|
5
5
|
awesome_print
|
6
6
|
binding_of_caller
|
7
7
|
bunny (>= 2.12.0)
|
8
8
|
graphlient
|
9
9
|
gruf
|
10
|
+
hashie (~> 3.5.7)
|
10
11
|
istox_gruf
|
11
12
|
listen (~> 3.0.5)
|
12
13
|
ougai
|
13
14
|
paranoia (~> 2.2)
|
14
15
|
redis (>= 2.0.0)
|
15
16
|
redis-namespace (>= 1.0.0)
|
17
|
+
redis-rails (~> 5.0.2)
|
16
18
|
vault (~> 0.1)
|
17
19
|
|
18
20
|
GEM
|
@@ -90,23 +92,23 @@ GEM
|
|
90
92
|
redis (>= 3.2, < 5.0)
|
91
93
|
faraday (0.17.3)
|
92
94
|
multipart-post (>= 1.2, < 3)
|
93
|
-
faraday_middleware (0.
|
95
|
+
faraday_middleware (0.14.0)
|
94
96
|
faraday (>= 0.7.4, < 1.0)
|
95
|
-
ffi (1.
|
97
|
+
ffi (1.12.2)
|
96
98
|
globalid (0.4.2)
|
97
99
|
activesupport (>= 4.2.0)
|
98
|
-
google-protobuf (3.11.
|
100
|
+
google-protobuf (3.11.3-universal-darwin)
|
99
101
|
googleapis-common-protos-types (1.0.4)
|
100
102
|
google-protobuf (~> 3.0)
|
101
103
|
graphlient (0.3.7)
|
102
104
|
faraday
|
103
105
|
faraday_middleware
|
104
106
|
graphql-client
|
105
|
-
graphql (1.
|
107
|
+
graphql (1.10.2)
|
106
108
|
graphql-client (0.16.0)
|
107
109
|
activesupport (>= 3.0)
|
108
110
|
graphql (~> 1.8)
|
109
|
-
grpc (1.26.0)
|
111
|
+
grpc (1.26.0-universal-darwin)
|
110
112
|
google-protobuf (~> 3.8)
|
111
113
|
googleapis-common-protos-types (~> 1.0)
|
112
114
|
grpc-tools (1.26.0)
|
@@ -116,6 +118,7 @@ GEM
|
|
116
118
|
grpc (~> 1.10)
|
117
119
|
grpc-tools (~> 1.10)
|
118
120
|
slop (~> 4.6)
|
121
|
+
hashie (3.5.7)
|
119
122
|
i18n (0.9.5)
|
120
123
|
concurrent-ruby (~> 1.0)
|
121
124
|
istox_gruf (2.7.1)
|
@@ -143,7 +146,7 @@ GEM
|
|
143
146
|
nio4r (2.3.1)
|
144
147
|
nokogiri (1.10.1)
|
145
148
|
mini_portile2 (~> 2.4.0)
|
146
|
-
oj (3.10.
|
149
|
+
oj (3.10.2)
|
147
150
|
ougai (1.8.2)
|
148
151
|
oj (~> 3.4)
|
149
152
|
paranoia (2.4.2)
|
@@ -180,8 +183,24 @@ GEM
|
|
180
183
|
rb-inotify (0.10.1)
|
181
184
|
ffi (~> 1.0)
|
182
185
|
redis (4.1.3)
|
186
|
+
redis-actionpack (5.1.0)
|
187
|
+
actionpack (>= 4.0, < 7)
|
188
|
+
redis-rack (>= 1, < 3)
|
189
|
+
redis-store (>= 1.1.0, < 2)
|
190
|
+
redis-activesupport (5.2.0)
|
191
|
+
activesupport (>= 3, < 7)
|
192
|
+
redis-store (>= 1.3, < 2)
|
183
193
|
redis-namespace (1.7.0)
|
184
194
|
redis (>= 3.0.4)
|
195
|
+
redis-rack (2.0.6)
|
196
|
+
rack (>= 1.5, < 3)
|
197
|
+
redis-store (>= 1.2, < 2)
|
198
|
+
redis-rails (5.0.2)
|
199
|
+
redis-actionpack (>= 5.0, < 6)
|
200
|
+
redis-activesupport (>= 5.0, < 6)
|
201
|
+
redis-store (>= 1.2, < 2)
|
202
|
+
redis-store (1.8.1)
|
203
|
+
redis (>= 4, < 5)
|
185
204
|
rspec (3.8.0)
|
186
205
|
rspec-core (~> 3.8.0)
|
187
206
|
rspec-expectations (~> 3.8.0)
|
@@ -203,7 +222,7 @@ GEM
|
|
203
222
|
rspec-mocks (~> 3.8.0)
|
204
223
|
rspec-support (~> 3.8.0)
|
205
224
|
rspec-support (3.8.0)
|
206
|
-
slop (4.
|
225
|
+
slop (4.8.0)
|
207
226
|
sprockets (3.7.2)
|
208
227
|
concurrent-ruby (~> 1.0)
|
209
228
|
rack (> 1, < 3)
|
data/istox.gemspec
CHANGED
@@ -36,6 +36,8 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency 'graphlient'
|
37
37
|
spec.add_dependency 'gruf'
|
38
38
|
spec.add_dependency 'istox_gruf'
|
39
|
+
spec.add_dependency 'hashie', '~> 3.5.7'
|
40
|
+
spec.add_dependency 'redis-rails', '~> 5.0.2'
|
39
41
|
spec.add_dependency 'listen', '~> 3.0.5'
|
40
42
|
spec.add_dependency 'ougai'
|
41
43
|
spec.add_dependency 'paranoia', '~> 2.2'
|
data/lib/istox.rb
CHANGED
@@ -16,6 +16,7 @@ module Istox
|
|
16
16
|
require 'istox/helpers/logger'
|
17
17
|
require 'istox/interfaces/chainhub/transaction'
|
18
18
|
require 'istox/helpers/publisher'
|
19
|
+
require 'istox/helpers/subscriber'
|
19
20
|
require 'istox/helpers/bunny_boot'
|
20
21
|
require 'istox/helpers/rate_limit'
|
21
22
|
require 'istox/helpers/vault'
|
@@ -31,6 +32,7 @@ module Istox
|
|
31
32
|
require 'istox/helpers/common_helper'
|
32
33
|
require 'istox/helpers/regex_helper'
|
33
34
|
require 'istox/helpers/result_handler'
|
35
|
+
require 'istox/helpers/redis'
|
34
36
|
require 'istox/models/blockchain_receipt'
|
35
37
|
require 'istox/models/concerns/blockchain_receipt_query'
|
36
38
|
require 'istox/consumers/blockchain_status_handler'
|
@@ -1,128 +1,210 @@
|
|
1
1
|
require 'bunny'
|
2
|
+
require 'hashie/mash'
|
2
3
|
require 'istox/helpers/logger'
|
3
4
|
|
4
5
|
module Istox
|
5
6
|
class BunnyBoot
|
6
7
|
class << self
|
7
|
-
#
|
8
|
-
|
9
|
-
|
8
|
+
# Create physical connection to RabbitMQ
|
9
|
+
# During failover of RabbitMQ cluster or temporary failure, there may be error and needs retry in loop
|
10
|
+
def connection
|
11
|
+
conn = nil
|
12
|
+
loop do
|
13
|
+
conn = Bunny.new(data['connect'].symbolize_keys).tap(&:start)
|
14
|
+
break
|
15
|
+
rescue Bunny::TCPConnectionFailed, Bunny::TCPConnectionFailedForAllHosts => e
|
16
|
+
log.error "Fails to create connection to RabbitMQ with err: #{e}"
|
17
|
+
log.info 'Reconnect after 2s ...'
|
18
|
+
sleep 2
|
19
|
+
end
|
10
20
|
|
11
|
-
|
21
|
+
conn
|
12
22
|
end
|
13
23
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
# Create virtual channel on established connection
|
25
|
+
# Configure pool_size, prefetch, confirm mode according to opts
|
26
|
+
def channel(conn, opts = {})
|
27
|
+
ch = conn.create_channel(nil, opts['pool_size'] || 1)
|
28
|
+
ch.prefetch(opts['prefetch']) unless opts['prefetch'].nil?
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
subscribing_consumer_keys = [consumer_key]
|
24
|
-
end
|
30
|
+
# Put channel in confirmation mode
|
31
|
+
ch.confirm_select if opts[:confirm]
|
32
|
+
log.debug "channel unconfirmed_set is #{ch.unconfirmed_set}"
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
34
|
+
ch
|
35
|
+
end
|
36
|
+
|
37
|
+
def exchange(eid)
|
38
|
+
type = data[:exchanges][eid][:type]
|
39
|
+
name = eid
|
40
|
+
settings = { durable: data[:exchanges][eid][:durable] || false }
|
41
|
+
confirm = data[:exchanges][eid][:confirm] || false
|
42
|
+
[type, name, settings, confirm]
|
43
|
+
rescue
|
44
|
+
nil
|
29
45
|
end
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
47
|
+
def queues_keys_for_subscribe()
|
48
|
+
data['queues'].keys
|
49
|
+
end
|
34
50
|
|
35
|
-
def
|
36
|
-
|
51
|
+
def subscriber_queue_from_key(key)
|
52
|
+
data['queues'][key]
|
53
|
+
end
|
54
|
+
|
55
|
+
def search_exchange_of_routing_key!(routing_key)
|
56
|
+
data['publish'].each do |k, v|
|
57
|
+
v.each do |k2, _v2|
|
58
|
+
return k if k2 == routing_key
|
59
|
+
end
|
60
|
+
end
|
37
61
|
|
38
|
-
|
39
|
-
|
62
|
+
raise "Cannot find exchange of routing key #{routing_key}, have you forgotten to define it in exchange section in amqp.yml"
|
63
|
+
end
|
40
64
|
|
41
|
-
|
65
|
+
# Default for exchange durable attr: true
|
66
|
+
def exchange_durable?(exchange_name)
|
67
|
+
exchange_config!(exchange_name)['durable'] || true
|
68
|
+
rescue => e
|
69
|
+
raise e
|
42
70
|
end
|
43
71
|
|
44
|
-
#
|
45
|
-
def
|
46
|
-
|
47
|
-
|
72
|
+
# Default for channel confirm attr: false
|
73
|
+
def exchange_confirm?(exchange_name)
|
74
|
+
exchange_config!(exchange_name)['confirm'] || false
|
75
|
+
end
|
48
76
|
|
49
|
-
|
77
|
+
def exchange_type(exchange_name)
|
78
|
+
exchange_config!(exchange_name)['type']
|
50
79
|
end
|
51
80
|
|
52
|
-
def
|
53
|
-
|
81
|
+
def exchange_name(consumer_key)
|
82
|
+
queue_config_from_consumer_key!(consumer_key)['exchange']
|
83
|
+
rescue
|
84
|
+
nil
|
85
|
+
end
|
54
86
|
|
55
|
-
|
87
|
+
def queue_name(consumer_key)
|
88
|
+
queue_config_from_consumer_key!(consumer_key)['queue_name']
|
89
|
+
rescue
|
90
|
+
nil
|
91
|
+
end
|
56
92
|
|
57
|
-
|
58
|
-
|
59
|
-
|
93
|
+
def ruby_class(consumer_key)
|
94
|
+
queue_config_from_consumer_key!(consumer_key)['ruby_class']
|
95
|
+
rescue
|
96
|
+
nil
|
97
|
+
end
|
60
98
|
|
61
|
-
|
62
|
-
|
99
|
+
# Default value: belonged exchange durable attr
|
100
|
+
def queue_durable?(consumer_key)
|
101
|
+
durable = queue_config_from_consumer_key!(consumer_key)['durable']
|
102
|
+
if durable.nil?
|
103
|
+
exchange_name = exchange_name consumer_key
|
104
|
+
durable = exchange_durable? exchange_name
|
105
|
+
end
|
63
106
|
|
64
|
-
|
107
|
+
durable
|
108
|
+
rescue
|
109
|
+
nil
|
110
|
+
end
|
65
111
|
|
66
|
-
|
112
|
+
def queue_manual_ack?(consumer_key)
|
113
|
+
manual_ack = queue_config_from_consumer_key!(consumer_key)['manual_ack']
|
114
|
+
return true if manual_ack.nil?
|
67
115
|
|
68
|
-
|
116
|
+
manual_ack
|
117
|
+
end
|
69
118
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
119
|
+
# By default retry-limit is 3
|
120
|
+
# If -1, means no limit
|
121
|
+
def queue_retry_limit(consumer_key)
|
122
|
+
retry_limit = queue_config_from_consumer_key!(consumer_key)['retry_limit']
|
123
|
+
|
124
|
+
if retry_limit.nil?
|
125
|
+
3
|
76
126
|
else
|
77
|
-
|
127
|
+
retry_limit.to_i
|
78
128
|
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# By default retry-limit is 3000
|
132
|
+
# If 0, means no gap and retry immediately
|
133
|
+
# To protect system, we can't allow unlimited retry with zero-gap
|
134
|
+
def queue_retry_gap(consumer_key)
|
135
|
+
retry_gap = queue_config_from_consumer_key!(consumer_key)['retry_gap']
|
79
136
|
|
80
|
-
|
81
|
-
|
137
|
+
if retry_gap.nil? || (retry_gap == 0 && queue_retry_limit(consumer_key) == -1)
|
138
|
+
3000
|
139
|
+
else
|
140
|
+
retry_gap.to_i
|
141
|
+
end
|
142
|
+
end
|
82
143
|
|
83
|
-
|
144
|
+
def channel_pool_size(consumer_key)
|
145
|
+
channel = channel_config(consumer_key)
|
146
|
+
return data['channel_pool_size'] || 1 if channel.nil?
|
84
147
|
|
85
|
-
|
86
|
-
|
148
|
+
channel['channel_pool_size']
|
149
|
+
end
|
87
150
|
|
88
|
-
|
89
|
-
|
151
|
+
def channel_prefetch(consumer_key)
|
152
|
+
channel = channel_config(consumer_key)
|
153
|
+
return data['channel_prefetch'] || 1 if channel.nil?
|
90
154
|
|
91
|
-
|
155
|
+
channel['channel_prefetch']
|
156
|
+
end
|
92
157
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
158
|
+
def publish(e, message, options = {})
|
159
|
+
persistent = false
|
160
|
+
mandatory = false
|
161
|
+
# Set Mandatory & Persistent flag for non-DLX and non-manual msg
|
162
|
+
unless ['dlx', 'manual'].include? options[:type]
|
163
|
+
persistent = data['publish'][e.name][options[:routing_key]]['persistent'] || false if options[:routing_key].present?
|
164
|
+
mandatory = data['publish'][e.name][options[:routing_key]]['mandatory'] || false
|
98
165
|
end
|
99
|
-
|
166
|
+
options.merge!(persistent: persistent) if options[:routing_key].present?
|
167
|
+
options.merge!(mandatory: mandatory)
|
100
168
|
|
101
|
-
|
102
|
-
|
169
|
+
# message.merge!(locale: I18n.locale)
|
170
|
+
message = JSON.dump message
|
103
171
|
|
104
|
-
|
172
|
+
log.debug "Publish options are: #{options}"
|
173
|
+
log.debug "Publish message payload #{message}"
|
174
|
+
e.publish(message, options)
|
175
|
+
|
176
|
+
options[:message_id]
|
105
177
|
end
|
106
178
|
|
107
|
-
|
108
|
-
return @connection if @connection.present?
|
179
|
+
private
|
109
180
|
|
110
|
-
|
181
|
+
def data
|
182
|
+
@data ||= Hashie::Mash.new(
|
183
|
+
YAML.safe_load(
|
184
|
+
ERB.new(File.read(ENV['AMQP_CONFIG'] || 'config/amqp.yml')).result
|
185
|
+
)
|
186
|
+
)
|
187
|
+
end
|
111
188
|
|
112
|
-
|
113
|
-
|
189
|
+
def queue_config_from_consumer_key!(consumer_key)
|
190
|
+
queue_config = data['queues'][consumer_key]
|
191
|
+
|
192
|
+
raise "Queue for key #{consumer_key} config not found, have you forgotten to define the queue in amqp.yml?" if queue_config.nil?
|
114
193
|
|
115
|
-
|
194
|
+
queue_config
|
116
195
|
end
|
117
196
|
|
118
|
-
def exchange_config!(
|
119
|
-
exchange_name = data['queues'][consumer_key]['exchange']
|
197
|
+
def exchange_config!(exchange_name)
|
120
198
|
exchange_config = data['exchanges'][exchange_name]
|
121
199
|
|
122
200
|
raise "Exchange #{exchange_name} config not found, have you forgotten to define the exchange in amqp.yml?" if exchange_config.nil?
|
123
201
|
|
124
202
|
exchange_config
|
125
203
|
end
|
204
|
+
|
205
|
+
def channel_config(consumer_key)
|
206
|
+
queue_config_from_consumer_key!(consumer_key)['channel']
|
207
|
+
end
|
126
208
|
end
|
127
209
|
end
|
128
210
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'securerandom'
|
1
2
|
require 'istox/helpers/logger'
|
2
3
|
|
3
4
|
module Istox
|
@@ -11,105 +12,195 @@ module Istox
|
|
11
12
|
# extra publish options: {
|
12
13
|
# publish_confirm: true/false
|
13
14
|
# }
|
14
|
-
def publish(exchange: nil, routing_key: nil, message
|
15
|
+
def publish(exchange: nil, routing_key: nil, message:)
|
15
16
|
raise 'Exchange and routing key cannot be nil at the same time.' if exchange.nil? && routing_key.nil?
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
exchange = search_exchange_of_routing_key!(routing_key) if exchange.nil?
|
20
|
-
queue_publish_config = queue_publish_config!(routing_key: routing_key, exchange: exchange)
|
21
|
-
end
|
22
|
-
|
23
|
-
exchange_config = exchange_config!(exchange)
|
24
|
-
exchange_durable = exchange_config['durable'].nil? ? true : exchange_config['durable']
|
25
|
-
|
26
|
-
queue_durable = queue_publish_config.present? ? queue_publish_config['durable'] : nil
|
27
|
-
queue_durable = exchange_config['durable'].nil? ? true : exchange_config['durable'] if queue_durable.nil?
|
28
|
-
|
29
|
-
options[:durable] = queue_durable
|
30
|
-
options[:persistent] = options[:persistent].nil? ? true : options[:persistent]
|
18
|
+
exchange = ::Istox::BunnyBoot.search_exchange_of_routing_key!(routing_key) if routing_key.present? && exchange.nil?
|
19
|
+
ex = exchange(exchange)
|
31
20
|
|
32
|
-
|
33
|
-
exchange_type: exchange_config['type'], exchange_durable: exchange_durable, options: options)
|
21
|
+
do_publish ex, routing_key, message
|
34
22
|
end
|
35
23
|
|
36
24
|
# publish without defining exchange and queue in amqp.yml
|
37
25
|
def manual_publish(exchange:, routing_key: nil, message:, exchange_type: 'direct', exchange_durable: true, options: {})
|
38
|
-
|
39
|
-
|
26
|
+
ex = exchange(exchange, type: exchange_type, durable: exchange_durable, options: options)
|
27
|
+
# TODO: Implement mandatory & persistent flag for manual publish
|
28
|
+
do_publish ex, routing_key, message
|
29
|
+
end
|
30
|
+
|
31
|
+
def re_publish
|
40
32
|
|
41
|
-
send_message(exchange: exchange, routing_key: routing_key, message: message,
|
42
|
-
exchange_type: exchange_type, exchange_durable: exchange_durable, options: options)
|
43
33
|
end
|
44
34
|
|
45
35
|
private
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
37
|
+
# Create physical connection for publisher
|
38
|
+
# Connection creation must be thread-safe
|
39
|
+
def connection
|
40
|
+
return @connection if @connection.present?
|
41
|
+
|
42
|
+
log.info 'No connection yet, create connection to RabbitMQ node ... ...'
|
43
|
+
@mutex = Mutex.new unless @mutex.present?
|
44
|
+
@mutex.synchronize do
|
45
|
+
if @connection.present?
|
46
|
+
log.info 'Connection already existed, cancel creation'
|
47
|
+
return @connection
|
48
|
+
end
|
53
49
|
|
54
|
-
|
55
|
-
channel.direct(exchange, durable: exchange_durable).publish(message.to_json, options)
|
56
|
-
when 'fanout'
|
57
|
-
channel.fanout(exchange, durable: exchange_durable).publish(message.to_json, options)
|
58
|
-
else
|
59
|
-
raise "Exchange type #{exchange_config.type} is not valid/supported."
|
50
|
+
@connection = ::Istox::BunnyBoot.connection
|
60
51
|
end
|
52
|
+
end
|
61
53
|
|
62
|
-
|
54
|
+
def close_connection
|
55
|
+
connection.close
|
56
|
+
@connection = nil
|
57
|
+
@channel = nil
|
58
|
+
@exchanges = nil
|
59
|
+
end
|
63
60
|
|
64
|
-
|
65
|
-
|
61
|
+
# For channels in publisher connection, it should be two-channel-per-thread
|
62
|
+
# One channel in confirm mode while other in non-confirm mode
|
63
|
+
# Return all channels for current thread
|
64
|
+
def channel
|
65
|
+
return @channel[Thread.current.object_id] if @channel.present? && @channel[Thread.current.object_id].present?
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
log.info 'No channel yet, create 2 channels confirm-mode and non-confirm-mode ... ...'
|
68
|
+
@channel = Hash.new if @channel.nil?
|
69
|
+
@channel[Thread.current.object_id] = Hash.new
|
70
|
+
@channel[Thread.current.object_id]['confirm'] = ::Istox::BunnyBoot.channel(connection, confirm: true)
|
71
|
+
@channel[Thread.current.object_id]['noconfirm'] = ::Istox::BunnyBoot.channel(connection, confirm: false)
|
70
72
|
|
71
|
-
|
73
|
+
@channel[Thread.current.object_id]
|
72
74
|
end
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
+
# This is version that multiple threads sharing same channel
|
77
|
+
# It is wrong usage, but only used for *TEST* !!!
|
78
|
+
def channel_fake
|
79
|
+
return @channel_fake if @channel_fake.present?
|
80
|
+
|
81
|
+
log.info 'No channel yet, create Channel to Existing connection ... ...'
|
82
|
+
@mutex_fake = Mutex.new unless @mutex_fake.present?
|
83
|
+
@mutex_fake.synchronize do
|
84
|
+
if @channel_fake.present?
|
85
|
+
log.info 'Channel already existed, cancel creation'
|
86
|
+
return @channel_fake
|
87
|
+
end
|
88
|
+
|
89
|
+
@channel_fake = ::Istox::BunnyBoot.channel(connection, confirm: true)
|
90
|
+
end
|
76
91
|
end
|
77
92
|
|
78
|
-
|
79
|
-
|
93
|
+
# return confirm-mode chanel for current thread
|
94
|
+
def channel_confirm_mode
|
95
|
+
channel['confirm']
|
96
|
+
# channel_fake
|
97
|
+
end
|
80
98
|
|
81
|
-
|
99
|
+
# return no-confirm-mode chanel for current thread
|
100
|
+
def channel_noconfirm_mode
|
101
|
+
channel['noconfirm']
|
102
|
+
# channel_fake
|
103
|
+
end
|
104
|
+
|
105
|
+
def exchanges
|
106
|
+
return @exchanges[Thread.current.object_id] if @exchanges.present? && @exchanges[Thread.current.object_id].present?
|
107
|
+
|
108
|
+
@exchanges = Hash.new if @exchanges.nil?
|
109
|
+
@exchanges[Thread.current.object_id] = { default: channel['confirm'].default_exchange }
|
110
|
+
|
111
|
+
@exchanges[Thread.current.object_id]
|
112
|
+
# @exchanges ||= { default: channel['confirm'].default_exchange }
|
113
|
+
# @exchanges ||= { default: channel_fake.default_exchange }
|
114
|
+
end
|
115
|
+
|
116
|
+
def exchange(id, type: nil, durable: nil, options: nil)
|
117
|
+
return exchanges[id] if exchanges[id].present?
|
118
|
+
|
119
|
+
log.info "Declare exchange #{id} on channel"
|
120
|
+
e = ::Istox::BunnyBoot.exchange(id)
|
121
|
+
e = [type, id, { durable: durable || true }, options['publish_confirm'] || true] if e.nil?
|
122
|
+
c = e.pop ? channel_confirm_mode : channel_noconfirm_mode
|
123
|
+
ex = c.send *e
|
124
|
+
|
125
|
+
# For mandatory flag, listen to returned message
|
126
|
+
# [2020-01-13T00:34:36.111+08:00] DEBUG: Got a returned message info: {:reply_code=>312, :reply_text=>"NO_ROUTE", :exchange=>"sample.exchange.direct.durable.confirm", :routing_key=>"sample_routing_key"}
|
127
|
+
# [2020-01-13T00:34:36.111+08:00] DEBUG: Got a returned message properties: {:content_type=>"application/octet-stream", :delivery_mode=>2, :priority=>0, :message_id=>"68c74e57e2874d24d48bdf3d891a059c"}
|
128
|
+
# [2020-01-13T00:34:36.111+08:00] DEBUG: Got a returned message content: "{\"sample_key\":\"abc\"}"
|
129
|
+
# when an unroutable message is returned, the BasicReturn is fired first and then an ack is sent, firing the BasicAck second.
|
130
|
+
# So if the current status is unroutable then we need to make sure that we don't overwrite that status with Success (ack).
|
131
|
+
ex.on_return do |return_info, properties, content|
|
132
|
+
log.debug "Got a returned message info: #{return_info}"
|
133
|
+
log.debug "Got a returned message properties: #{properties}"
|
134
|
+
log.debug "Got a returned message content: #{content}"
|
135
|
+
|
136
|
+
find_trackers("message_id:#{properties[:message_id]}").each do |key|
|
137
|
+
log.debug("On_return, update key #{key} in redis on status and retry counter")
|
138
|
+
update_tracker_by_status(key, 2)
|
139
|
+
update_tracker_incr_retry(key)
|
140
|
+
end
|
82
141
|
|
83
|
-
|
142
|
+
# publish(return_info[:exchange], return_info[:routing_key], content)
|
143
|
+
end
|
84
144
|
|
85
|
-
|
145
|
+
exchanges[id] = ex
|
86
146
|
end
|
87
147
|
|
88
|
-
def
|
89
|
-
|
148
|
+
def channel_confirm?(ch)
|
149
|
+
ch.using_publisher_confirmations?
|
150
|
+
end
|
90
151
|
|
91
|
-
|
152
|
+
def channel_next_tag(ch)
|
153
|
+
ch.next_publish_seq_no
|
92
154
|
end
|
93
155
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
156
|
+
def do_publish(ex, routing_key, message)
|
157
|
+
ch = ex.channel
|
158
|
+
if channel_confirm? ch
|
159
|
+
delivery_tag = channel_next_tag ch
|
160
|
+
message_id = SecureRandom.hex
|
161
|
+
# update_message_tracker(message_id, delivery_tag, 0, message)
|
162
|
+
end
|
163
|
+
|
164
|
+
::Istox::BunnyBoot.publish(ex, message, routing_key: routing_key, message_id: message_id, type: 'manual')
|
165
|
+
|
166
|
+
if ex.channel.using_publisher_confirmations?
|
167
|
+
log.debug 'Confirm mode channel, wait for confirmation'
|
168
|
+
success = ex.channel.wait_for_confirms
|
169
|
+
if success
|
170
|
+
log.debug "Confirm mode result: #{success}"
|
171
|
+
::Istox::RedisBoot.flushdb
|
172
|
+
else
|
173
|
+
ex.channel.nacked_set.each do |n|
|
174
|
+
log.debug "Confirm mode UnAcked Delivery Tag: #{n}"
|
175
|
+
::Istox::RedisBoot.incr("confirm:#{n.to_s}")
|
176
|
+
publish(exchange: ex.name, routing_key: routing_key, message: message, )
|
177
|
+
end
|
98
178
|
end
|
99
179
|
end
|
180
|
+
rescue Bunny::ConnectionClosedError => e
|
181
|
+
log.debug "Publish fails due to #{e}"
|
182
|
+
rescue => e
|
183
|
+
log.debug "Timeout error happens #{e}"
|
184
|
+
end
|
100
185
|
|
101
|
-
|
186
|
+
def create_tracker(message_id, delivery_tag, status, payload)
|
187
|
+
log.debug "Create track: message_id #{message_id}, delivery_tag #{delivery_tag}, status #{status}"
|
188
|
+
|
189
|
+
::Istox::RedisBoot.set("status:delivery_tag:#{delivery_tag.to_s}:message_id:#{message_id}", status)
|
190
|
+
::Istox::RedisBoot.set("payload:delivery_tag:#{delivery_tag.to_s}:message_id:#{message_id}", payload.to_s)
|
191
|
+
::Istox::RedisBoot.set("retry:delivery_tag:#{delivery_tag.to_s}:message_id:#{message_id}", 0)
|
102
192
|
end
|
103
193
|
|
104
|
-
def
|
105
|
-
|
194
|
+
def find_trackers(key)
|
195
|
+
::Istox::RedisBoot.keys("status:*#{key}*")
|
196
|
+
end
|
106
197
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
198
|
+
def update_tracker_by_status(key, status)
|
199
|
+
::Istox::RedisBoot.set(key, status)
|
200
|
+
end
|
111
201
|
|
112
|
-
|
202
|
+
def update_tracker_incr_retry(key)
|
203
|
+
::Istox::RedisBoot.incr(key)
|
113
204
|
end
|
114
205
|
end
|
115
206
|
end
|