gorg_service 4.1.0 → 5.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/LICENSE.txt +1 -1
- data/README.md +24 -32
- data/bin/console +1 -1
- data/bin/worker +1 -1
- data/gorg_service.gemspec +1 -1
- data/lib/gorg_service/configuration.rb +17 -4
- data/lib/gorg_service/consumer/errors.rb +38 -0
- data/lib/gorg_service/consumer/listener.rb +97 -0
- data/lib/gorg_service/consumer/message_handler/base.rb +50 -0
- data/lib/gorg_service/consumer/message_handler/event_handler.rb +12 -0
- data/lib/gorg_service/consumer/message_handler/reply_handler.rb +12 -0
- data/lib/gorg_service/consumer/message_handler/request_handler.rb +48 -0
- data/lib/gorg_service/consumer/message_handler.rb +15 -0
- data/lib/gorg_service/consumer/message_router.rb +49 -0
- data/lib/gorg_service/consumer.rb +46 -0
- data/lib/gorg_service/message.rb +101 -26
- data/lib/gorg_service/producer.rb +26 -0
- data/lib/gorg_service/rabbitmq_env_builder.rb +39 -10
- data/lib/gorg_service/version.rb +1 -1
- data/lib/gorg_service.rb +27 -67
- metadata +14 -7
- data/lib/gorg_service/errors.rb +0 -37
- data/lib/gorg_service/listener.rb +0 -106
- data/lib/gorg_service/message_handler.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23984c4b8e4322a1423a2d6458c7025303da38b7
|
4
|
+
data.tar.gz: 6c3e2e694dc143d926742bab066b31448808b4b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 268e2a10c35eec58d78d6a177d05d53a648ff62e955323419649673fbfa18c99de4dd2705793bc2d18ad8169d1ba83cab2d0f5d7c7d3b583bcd210a91016ef09
|
7
|
+
data.tar.gz: dff239c0d3c80884cf6f8adf47b54c97160ed801c870ea5dffec578dd7dda07fc449a159a1f2a4a36f5d059370de1d7ab3d534090470f6997b41cf9598c6c60e
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2016
|
3
|
+
Copyright (c) 2016 Société des ingénieurs Arts et Métiers
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -45,8 +45,7 @@ GorgService.configure do |c|
|
|
45
45
|
# c.rabbitmq_vhost = "/"
|
46
46
|
#
|
47
47
|
#
|
48
|
-
# c.
|
49
|
-
# c.rabbitmq_exchange_name = "exchange"
|
48
|
+
# c.rabbitmq_event_exchange_name = "exchange"
|
50
49
|
#
|
51
50
|
# time before trying again on softfail in milliseconds (temporary error)
|
52
51
|
# c.rabbitmq_deferred_time = 1800000 # 30min
|
@@ -58,15 +57,6 @@ GorgService.configure do |c|
|
|
58
57
|
# Central logging is disable if nil
|
59
58
|
# c.log_routing_key = nil
|
60
59
|
#
|
61
|
-
# Routing hash
|
62
|
-
# map routing_key of received message with MessageHandler
|
63
|
-
# exemple:
|
64
|
-
# c.message_handler_map={
|
65
|
-
# "some.routing.key" => MyMessageHandler,
|
66
|
-
# "Another.routing.key" => OtherMessageHandler,
|
67
|
-
# "third.routing.key" => MyMessageHandler,
|
68
|
-
# }
|
69
|
-
c.message_handler_map= {} #TODO : Set my routing hash
|
70
60
|
|
71
61
|
end
|
72
62
|
```
|
@@ -80,19 +70,19 @@ my_service.run
|
|
80
70
|
```
|
81
71
|
### Routing and MessageHandler
|
82
72
|
When running, GorgService act as a consumer on Gadz.org RabbitMQ network.
|
83
|
-
It bind its queue on the main exchange and subscribes to routing keys
|
73
|
+
It bind its queue on the main exchange and subscribes to routing keys with `listen_to`
|
84
74
|
|
85
75
|
Each received message will be routed to the corresponding `MessageHandler`. AMQP wildcards are supported.The first key to match the incoming routing key will be used.
|
86
76
|
|
87
|
-
A `MessageHandler` is a kind of controller. This is where you
|
77
|
+
A `MessageHandler` is a kind of controller. This is where you the message is processed.
|
88
78
|
A `MessageHandler` expect a `GorgService::Message` as param of its `initializer`method.
|
89
79
|
|
90
80
|
Here is an exemple `MessageHandler` :
|
91
81
|
```ruby
|
92
|
-
require 'json'
|
93
|
-
require 'json-schema' #Checkout https://github.com/ruby-json-schema/json-schema
|
94
82
|
|
95
|
-
class ExampleMessageHandler < GorgService::MessageHandler
|
83
|
+
class ExampleMessageHandler < GorgService::Consumer::MessageHandler::EventHandler
|
84
|
+
|
85
|
+
listen_to "event.user.updated"
|
96
86
|
|
97
87
|
EXPECTED_SCHEMA = {
|
98
88
|
"type" => "object",
|
@@ -102,22 +92,25 @@ class ExampleMessageHandler < GorgService::MessageHandler
|
|
102
92
|
}
|
103
93
|
}
|
104
94
|
|
105
|
-
def
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
#This message can't be processed, it will be discarded
|
111
|
-
raise_hardfail("Invalid message",e)
|
112
|
-
end
|
113
|
-
|
95
|
+
def validate
|
96
|
+
message.validate_with(EXPECTED_SCHEMA)
|
97
|
+
end
|
98
|
+
|
99
|
+
def process
|
114
100
|
begin
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
101
|
+
if example=Example.update(message.data[:user_id])
|
102
|
+
producer=GorgServiceProducer.new
|
103
|
+
event=GorgService::Message.new(
|
104
|
+
event: "event.example.updated",
|
105
|
+
data: {example_id: example.id}
|
106
|
+
)
|
107
|
+
producer.publish_message(event)
|
108
|
+
else
|
109
|
+
raise_hardfail("Unable to update example")
|
110
|
+
end
|
111
|
+
rescue ApiConnectionError => e
|
112
|
+
raise_softfail("Unable to connect to API",error: e)
|
113
|
+
end
|
121
114
|
end
|
122
115
|
end
|
123
116
|
```
|
@@ -156,7 +149,6 @@ Error log structure :
|
|
156
149
|
|
157
150
|
## To Do
|
158
151
|
|
159
|
-
- Internal logs using Logger
|
160
152
|
- Allow disable JSON Schema Validation on incomming messages
|
161
153
|
- Message and ErrorLog attributes validation
|
162
154
|
|
data/bin/console
CHANGED
@@ -11,7 +11,7 @@ require "gorg_service"
|
|
11
11
|
# Pry.start
|
12
12
|
|
13
13
|
#You can add a file bin/env.rb to set convenience environment such as configuration
|
14
|
-
env_path=File.
|
14
|
+
env_path=File.expand_path("./env.rb",File.dirname(__FILE__))
|
15
15
|
require env_path if File.file?(env_path)
|
16
16
|
|
17
17
|
require "irb"
|
data/bin/worker
CHANGED
@@ -11,7 +11,7 @@ require "gorg_service"
|
|
11
11
|
# Pry.start
|
12
12
|
|
13
13
|
#You should add a file bin/env.rb to set convenience environment such as configuration
|
14
|
-
env_path=File.
|
14
|
+
env_path=File.expand_path("./env.rb",File.dirname(__FILE__))
|
15
15
|
require env_path if File.file?(env_path)
|
16
16
|
|
17
17
|
GorgService.new.run
|
data/gorg_service.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency 'bunny', '~> 2.2', '>= 2.2.2'
|
30
30
|
spec.add_dependency 'json-schema', '~> 2.6'
|
31
|
-
spec.add_dependency 'gorg_message_sender', '~> 1'
|
31
|
+
spec.add_dependency 'gorg_message_sender', '~> 1.4.2'
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 1.11"
|
34
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -21,8 +21,7 @@ class GorgService
|
|
21
21
|
:application_id,
|
22
22
|
:rabbitmq_host,
|
23
23
|
:rabbitmq_port,
|
24
|
-
:
|
25
|
-
:rabbitmq_exchange_name,
|
24
|
+
:rabbitmq_event_exchange_name,
|
26
25
|
:rabbitmq_deferred_time,
|
27
26
|
:rabbitmq_max_attempts,
|
28
27
|
:rabbitmq_user,
|
@@ -41,9 +40,8 @@ class GorgService
|
|
41
40
|
@message_handler_map = {}
|
42
41
|
@rabbitmq_host = "localhost"
|
43
42
|
@rabbitmq_port = 5672
|
44
|
-
@rabbitmq_queue_name = @application_id
|
45
43
|
@rabbitmq_deferred_time = 1800000 #30 minutes
|
46
|
-
@
|
44
|
+
@rabbitmq_event_exchange_name = "exchange"
|
47
45
|
@rabbitmq_user = nil
|
48
46
|
@rabbitmq_password = nil
|
49
47
|
@rabbitmq_vhost = "/"
|
@@ -51,5 +49,20 @@ class GorgService
|
|
51
49
|
@log_routing_key = nil
|
52
50
|
@prefetch_count = 1
|
53
51
|
end
|
52
|
+
|
53
|
+
def rabbitmq_queue_name=(value)
|
54
|
+
warn "[DEPRECATION] GorgService::Configuration : `rabbitmq_queue_name=` is deprecated and will be removed soon."
|
55
|
+
end
|
56
|
+
|
57
|
+
# Deprecated: please use rabbitmq_event_exchange_name instead
|
58
|
+
def rabbitmq_exchange_name
|
59
|
+
@rabbitmq_event_exchange_name
|
60
|
+
end
|
61
|
+
|
62
|
+
# Deprecated: please use rabbitmq_event_exchange_name instead
|
63
|
+
def rabbitmq_exchange_name=(v)
|
64
|
+
warn "[DEPRECATION] GorgService::Configuration : `rabbitmq_exchange_name` is deprecated. Please use `rabbitmq_event_exchange_name` instead."
|
65
|
+
@rabbitmq_event_exchange_name=v
|
66
|
+
end
|
54
67
|
end
|
55
68
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class GorgService
|
5
|
+
class Consumer
|
6
|
+
#Common behavior of failling errors
|
7
|
+
class FailError < StandardError
|
8
|
+
attr_reader :error_raised
|
9
|
+
|
10
|
+
def initialize(message = nil, error_raised = nil)
|
11
|
+
@message = message
|
12
|
+
@error_raised = error_raised
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
@message
|
17
|
+
end
|
18
|
+
|
19
|
+
def type
|
20
|
+
""
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#Softfail error : This message should be processed again later
|
25
|
+
class SoftfailError < FailError
|
26
|
+
def type
|
27
|
+
"softerror"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#Hardfail error : This message is not processable and will never be
|
32
|
+
class HardfailError < FailError
|
33
|
+
def type
|
34
|
+
"harderror"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "bunny"
|
5
|
+
|
6
|
+
class GorgService
|
7
|
+
class Consumer
|
8
|
+
class Listener
|
9
|
+
|
10
|
+
attr_accessor :consumer
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(env: nil, max_attempts: 48, log_routing_key: nil)
|
15
|
+
@max_attempts=max_attempts.to_i
|
16
|
+
@log_routing_key=log_routing_key
|
17
|
+
|
18
|
+
@env=env
|
19
|
+
end
|
20
|
+
|
21
|
+
def listen
|
22
|
+
@consumer=@env.job_queue.subscribe(:manual_ack => true) do |delivery_info, properties, body|
|
23
|
+
#Log
|
24
|
+
routing_key=delivery_info[:routing_key]
|
25
|
+
GorgService.logger.info "Received message with routing key #{routing_key}"
|
26
|
+
GorgService.logger.debug "Message properties : #{properties.to_s}"
|
27
|
+
GorgService.logger.debug "Message payload : #{body.to_s[0...10000]}"
|
28
|
+
|
29
|
+
#Process
|
30
|
+
process_message(delivery_info, properties, body)
|
31
|
+
|
32
|
+
#Acknoledge
|
33
|
+
@env.ch.ack(delivery_info.delivery_tag)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def stop
|
38
|
+
@consumer.cancel
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def process_message(delivery_info, _properties, body)
|
44
|
+
message=nil
|
45
|
+
begin
|
46
|
+
#Parse message
|
47
|
+
message=Message.parse(delivery_info, _properties, body)
|
48
|
+
|
49
|
+
#Process message
|
50
|
+
incomming_message_error_count=message.errors.count
|
51
|
+
MessageRouter.new(message)
|
52
|
+
process_logging(message) if message.errors.count>incomming_message_error_count
|
53
|
+
|
54
|
+
rescue SoftfailError => e
|
55
|
+
process_softfail(e, message)
|
56
|
+
rescue HardfailError => e
|
57
|
+
process_hardfail(e, message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_softfail(e, message)
|
62
|
+
message.log_error(e)
|
63
|
+
GorgService.logger.error "SOFTFAIL ERROR : #{e.message}"
|
64
|
+
if message.errors.count.to_i >= @max_attempts
|
65
|
+
GorgService.logger.info " DISCARD MESSAGE : #{message.errors.count} errors in message log"
|
66
|
+
process_hardfail(HardfailError.new("Too Much SoftError : This message reached the limit of softerror (max: #{@max_attempts})"), message)
|
67
|
+
else
|
68
|
+
send_to_deferred_queue(message)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def process_hardfail(e, message)
|
73
|
+
GorgService.logger.error "HARDFAIL ERROR : #{e.message}, #{e.error_raised&&e.error_raised.inspect}"
|
74
|
+
GorgService.logger.info " DISCARD MESSAGE"
|
75
|
+
if message
|
76
|
+
message.log_error(e)
|
77
|
+
process_logging(message)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_logging(message)
|
82
|
+
message.routing_key=@log_routing_key
|
83
|
+
GorgService::Producer.new.publish_message(message)
|
84
|
+
#RabbitmqProducer.new.send_raw(message.to_json, @log_routing_key, verbose: true) if @log_routing_key
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_to_deferred_queue(msg)
|
88
|
+
if @env.delayed_queue_for msg.event
|
89
|
+
@env.delayed_in_exchange.publish(msg.to_json, :routing_key => msg.event)
|
90
|
+
GorgService.logger.info "DEFER MESSAGE : message sent to #{@env.delayed_in_exchange.name} with routing key #{msg.event}"
|
91
|
+
else
|
92
|
+
raise "DelayedQueueNotFound"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
class GorgService
|
4
|
+
class Consumer
|
5
|
+
module MessageHandler
|
6
|
+
class Base
|
7
|
+
|
8
|
+
def initialize(message)
|
9
|
+
@message=message
|
10
|
+
|
11
|
+
begin
|
12
|
+
validate
|
13
|
+
rescue GorgService::Message::DataValidationError => e
|
14
|
+
raise_hardfail("DataValidationError",error: e.errors)
|
15
|
+
end
|
16
|
+
process
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
GorgService.logger.warn "WARNING : No message schema validation in #{self.class.name}, implement it in #validate(message) "
|
21
|
+
end
|
22
|
+
|
23
|
+
def process
|
24
|
+
GorgService.logger.warn "WARNING : You must define your MessageHandler behavior in #process"
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
@message
|
29
|
+
end
|
30
|
+
alias_method :msg, :message
|
31
|
+
|
32
|
+
def raise_hardfail(message, error: nil)
|
33
|
+
raise HardfailError.new(message, error)
|
34
|
+
end
|
35
|
+
|
36
|
+
def raise_softfail(message, error: nil)
|
37
|
+
raise SoftfailError.new(message, error)
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
|
42
|
+
def listen_to(routing_key)
|
43
|
+
MessageRouter.register_route(routing_key, self)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class GorgService
|
5
|
+
class Consumer
|
6
|
+
module MessageHandler
|
7
|
+
class RequestHandler < Base
|
8
|
+
|
9
|
+
def reply_with(data)
|
10
|
+
if message.expect_reply?
|
11
|
+
|
12
|
+
reply=GorgService::Message.new(
|
13
|
+
event: message.reply_routing_key,
|
14
|
+
data: data,
|
15
|
+
correlation_id: message.id,
|
16
|
+
type: "reply"
|
17
|
+
)
|
18
|
+
|
19
|
+
replier=GorgService::Producer.new
|
20
|
+
replier.publish_message(reply,exchange: message.reply_to)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def raise_hardfail(message, error: nil, data: nil)
|
25
|
+
reply_with({
|
26
|
+
status: 'hardfail',
|
27
|
+
error_message: message,
|
28
|
+
debug_message: error&&error.inspect,
|
29
|
+
error_data: data
|
30
|
+
})
|
31
|
+
|
32
|
+
super(message, error: error)
|
33
|
+
end
|
34
|
+
|
35
|
+
def raise_softfail(message, error: nil, data: nil)
|
36
|
+
reply_with({
|
37
|
+
status: 'softfail',
|
38
|
+
error_message: message,
|
39
|
+
debug_message: error&&error.inspect,
|
40
|
+
error_data: data
|
41
|
+
})
|
42
|
+
super(message, error: error)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class GorgService
|
5
|
+
class Consumer
|
6
|
+
module MessageHandler
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require "gorg_service/consumer/message_handler/base"
|
13
|
+
require "gorg_service/consumer/message_handler/event_handler"
|
14
|
+
require "gorg_service/consumer/message_handler/request_handler"
|
15
|
+
require "gorg_service/consumer/message_handler/reply_handler"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class GorgService
|
5
|
+
class Consumer
|
6
|
+
class MessageRouter
|
7
|
+
|
8
|
+
def initialize(message)
|
9
|
+
message_handler=self.class.message_handler_for message.routing_key
|
10
|
+
raise HardfailError.new("Routing error : No message handler found for this routing key") unless message_handler
|
11
|
+
|
12
|
+
message_handler.new(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
def routes
|
18
|
+
@routes||={}
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_route(routing_key, message_handler)
|
22
|
+
routes[routing_key]=message_handler
|
23
|
+
end
|
24
|
+
|
25
|
+
def listened_keys
|
26
|
+
routes.keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def message_handler_for routing_key
|
30
|
+
@routes.each do |k, mh|
|
31
|
+
return mh if amqp_key_to_regex(k).match(routing_key)
|
32
|
+
end
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def amqp_key_to_regex(key)
|
39
|
+
regex_base=key.gsub('.', '\.')
|
40
|
+
.gsub('*', '([a-zA-Z0-9\-_:]+)')
|
41
|
+
.gsub(/(\\\.)?#(\\\.)?/, '((\.)?[a-zA-Z0-9\-_:]*(\.)?)*')
|
42
|
+
|
43
|
+
/^#{regex_base}$/
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "gorg_service/consumer/message_router"
|
5
|
+
require "gorg_service/consumer/errors"
|
6
|
+
require "gorg_service/consumer/listener"
|
7
|
+
require "gorg_service/consumer/message_handler"
|
8
|
+
|
9
|
+
class GorgService
|
10
|
+
class Consumer
|
11
|
+
|
12
|
+
attr_accessor :environment
|
13
|
+
|
14
|
+
def initialize(environment: GorgService.environment)
|
15
|
+
@environment=environment
|
16
|
+
end
|
17
|
+
|
18
|
+
def listener
|
19
|
+
@listener ||= Listener.new(
|
20
|
+
env: environment,
|
21
|
+
max_attempts: GorgService.configuration.rabbitmq_max_attempts.to_i,
|
22
|
+
log_routing_key: GorgService.configuration.log_routing_key
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
begin
|
28
|
+
self.start
|
29
|
+
puts " [*] Waiting for messages. To exit press CTRL+C"
|
30
|
+
loop do
|
31
|
+
sleep(1)
|
32
|
+
end
|
33
|
+
rescue SystemExit, Interrupt => _
|
34
|
+
self.stop
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start
|
39
|
+
listener.listen
|
40
|
+
end
|
41
|
+
|
42
|
+
def stop
|
43
|
+
listener.stop
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/gorg_service/message.rb
CHANGED
@@ -11,8 +11,27 @@ require "gorg_service/message/error_log"
|
|
11
11
|
class GorgService
|
12
12
|
class Message
|
13
13
|
|
14
|
-
|
14
|
+
class DataValidationError < StandardError
|
15
|
+
|
16
|
+
attr_reader :errors
|
17
|
+
|
18
|
+
def initialize(errors)
|
19
|
+
@errors=errors
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
attr_accessor :id
|
24
|
+
attr_accessor :reply_to
|
25
|
+
attr_accessor :correlation_id
|
26
|
+
attr_accessor :sender_id
|
27
|
+
attr_accessor :content_type
|
28
|
+
attr_accessor :content_encoding
|
29
|
+
attr_accessor :headers
|
30
|
+
attr_accessor :type
|
31
|
+
|
32
|
+
attr_accessor :event_id
|
33
|
+
attr_accessor :routing_key
|
34
|
+
attr_accessor :event
|
16
35
|
attr_accessor :data
|
17
36
|
attr_accessor :errors
|
18
37
|
attr_accessor :creation_time
|
@@ -23,28 +42,56 @@ class GorgService
|
|
23
42
|
end
|
24
43
|
|
25
44
|
|
26
|
-
def initialize(
|
27
|
-
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
45
|
+
def initialize(opts={})
|
46
|
+
##Message payload params
|
47
|
+
@event_id= opts.fetch(:event_id,generate_id)
|
48
|
+
@errors= opts.fetch(:errors,nil)
|
49
|
+
@creation_time= opts.fetch(:creation_time,DateTime.now.iso8601)
|
50
|
+
@sender= opts.fetch(:sender,application_id)
|
51
|
+
@event= opts.fetch(:event,nil)
|
52
|
+
@data= opts.fetch(:data,nil)
|
53
|
+
|
54
|
+
#Message Attributes params
|
55
|
+
@routing_key= opts.fetch(:routing_key,event)
|
56
|
+
@id= opts.fetch(:id,generate_id)
|
57
|
+
@reply_to= opts.fetch(:reply_to,nil)
|
58
|
+
@correlation_id= opts.fetch(:correlation_id,nil)
|
59
|
+
@sender_id= opts.fetch(:sender_id,application_id)
|
60
|
+
@content_type= opts.fetch(:content_type,"application/json")
|
61
|
+
@content_encoding= opts.fetch(:content_encoding,"deflate")
|
62
|
+
@headers= opts.fetch(:headers,{})
|
63
|
+
@type= opts.fetch(:type,"event")
|
33
64
|
end
|
34
65
|
|
35
|
-
def
|
36
|
-
|
37
|
-
event_uuid: @
|
66
|
+
def body
|
67
|
+
_body={
|
68
|
+
event_uuid: @event_id,
|
38
69
|
event_name: @event,
|
39
70
|
event_sender_id: @sender,
|
40
71
|
event_creation_time: @creation_time,
|
41
72
|
data: @data,
|
42
73
|
}
|
43
74
|
if errors.any?
|
44
|
-
|
45
|
-
|
75
|
+
_body[:errors_count]=@errors.count
|
76
|
+
_body[:errors]=@errors.map{|e| e.to_h}
|
46
77
|
end
|
47
|
-
|
78
|
+
_body
|
79
|
+
end
|
80
|
+
alias_method :to_h, :body
|
81
|
+
alias_method :payload, :body
|
82
|
+
|
83
|
+
def properties
|
84
|
+
{
|
85
|
+
routing_key: routing_key,
|
86
|
+
reply_to: reply_to,
|
87
|
+
correlation_id: correlation_id,
|
88
|
+
content_type: content_type,
|
89
|
+
content_encoding: content_encoding,
|
90
|
+
headers: headers,
|
91
|
+
app_id: sender_id,
|
92
|
+
type: type,
|
93
|
+
message_id: id,
|
94
|
+
}
|
48
95
|
end
|
49
96
|
|
50
97
|
# Generate RabbitMQ message body
|
@@ -62,32 +109,60 @@ class GorgService
|
|
62
109
|
errors<<e
|
63
110
|
end
|
64
111
|
|
112
|
+
def reply_exchange
|
113
|
+
reply_to
|
114
|
+
end
|
115
|
+
|
116
|
+
def expect_reply?
|
117
|
+
!!reply_to
|
118
|
+
end
|
119
|
+
|
120
|
+
def reply_routing_key
|
121
|
+
event.sub('request','reply')
|
122
|
+
end
|
123
|
+
|
124
|
+
def validate_data_with(schema)
|
125
|
+
errors=JSON::Validator.fully_validate(schema, self.data)
|
126
|
+
if errors.any?
|
127
|
+
raise DataValidationError.new(errors)
|
128
|
+
else
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
|
65
135
|
### Class methods
|
66
136
|
|
67
|
-
|
68
|
-
# @return Message
|
69
|
-
# parsed message
|
70
|
-
# Errors
|
71
|
-
# Hardfail if un-parsable JSON body
|
72
|
-
def self.parse_body(body)
|
137
|
+
def self.parse(delivery_info, properties, body)
|
73
138
|
begin
|
74
139
|
json_body=JSON.parse(body)
|
75
140
|
|
76
141
|
JSON::Validator.validate!(GorgService::Message::JSON_SCHEMA,json_body)
|
77
142
|
|
78
|
-
puts json_body["errors"].inspect
|
79
|
-
|
80
143
|
msg=self.new(
|
81
|
-
|
144
|
+
routing_key: delivery_info[:routing_key],
|
145
|
+
id: properties[:message_id],
|
146
|
+
reply_to: properties[:reply_to],
|
147
|
+
correlation_id: properties[:correlatio_to],
|
148
|
+
sender_id: properties[:app_id],
|
149
|
+
content_type: properties[:content_type],
|
150
|
+
content_encoding: properties[:content_encoding],
|
151
|
+
headers: properties[:header],
|
152
|
+
type: properties[:type],
|
153
|
+
|
154
|
+
event_id: json_body["event_uuid"],
|
82
155
|
event: json_body["event_name"],
|
83
156
|
data: convert_keys_to_sym(json_body["data"]),
|
84
157
|
creation_time: json_body["event_creation_time"] && DateTime.parse(json_body["event_creation_time"]),
|
85
158
|
sender: json_body["event_sender_id"],
|
86
159
|
errors: json_body["errors"]&&json_body["errors"].map{|e| GorgService::Message::ErrorLog.parse(e)},
|
87
|
-
|
160
|
+
)
|
88
161
|
msg
|
89
162
|
rescue JSON::ParserError => e
|
90
|
-
raise GorgService::HardfailError.new(
|
163
|
+
raise GorgService::Consumer::HardfailError.new("Unprocessable message : Unable to parse JSON message body", e)
|
164
|
+
rescue JSON::Schema::ValidationError => e
|
165
|
+
raise GorgService::Consumer::HardfailError.new("Invalid JSON : This message does not respect Gadz.org JSON Schema",e)
|
91
166
|
end
|
92
167
|
end
|
93
168
|
|
@@ -116,7 +191,7 @@ class GorgService
|
|
116
191
|
GorgService.configuration.application_id
|
117
192
|
end
|
118
193
|
|
119
|
-
|
194
|
+
|
120
195
|
|
121
196
|
end
|
122
197
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
|
5
|
+
class GorgService
|
6
|
+
class Producer
|
7
|
+
|
8
|
+
attr_accessor :default_exchange
|
9
|
+
attr_accessor :environment
|
10
|
+
|
11
|
+
def initialize(environment_: GorgService.environment ,default_exchange_: nil)
|
12
|
+
self.environment=environment_
|
13
|
+
self.default_exchange= default_exchange_ || environment.event_exchange
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def publish_message(message, exchange: default_exchange)
|
18
|
+
x=exchange.is_a?(Bunny::Exchange) ? exchange : environment.find_exchange_by_name(exchange)
|
19
|
+
GorgService.logger.info "Publish to #{x.name} - key : #{message.routing_key}"
|
20
|
+
GorgService.logger.debug "Message content : #{message.body.to_s[0...10000]}"
|
21
|
+
|
22
|
+
x.publish(message.to_json, message.properties)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class GorgService
|
2
2
|
class RabbitmqEnvBuilder
|
3
3
|
|
4
|
-
def initialize(conn:nil,
|
4
|
+
def initialize(conn:nil, event_exchange:"", app_id:"", deferred_time: 10000, listened_routing_keys: [], prefetch: 1)
|
5
5
|
@_conn=conn
|
6
6
|
@app_id=app_id
|
7
|
-
@
|
7
|
+
@event_exchange_name=event_exchange
|
8
8
|
@deferred_time=deferred_time
|
9
9
|
@delayed_queues={}
|
10
10
|
@listened_routing_keys=listened_routing_keys
|
@@ -15,6 +15,7 @@ class GorgService
|
|
15
15
|
@_conn.start unless @_conn.connected?
|
16
16
|
@_conn
|
17
17
|
end
|
18
|
+
alias_method :connection, :conn
|
18
19
|
|
19
20
|
def ch
|
20
21
|
unless (@_ch && @_ch.status == :open)
|
@@ -23,11 +24,21 @@ class GorgService
|
|
23
24
|
end
|
24
25
|
@_ch
|
25
26
|
end
|
27
|
+
alias_method :channel, :ch
|
26
28
|
|
27
|
-
def
|
28
|
-
ch.topic(@
|
29
|
+
def request_exchange
|
30
|
+
ch.topic("#{@app_id}.request", :durable => true)
|
29
31
|
end
|
30
32
|
|
33
|
+
def reply_exchange
|
34
|
+
ch.topic("#{@app_id}.reply", :durable => true)
|
35
|
+
end
|
36
|
+
|
37
|
+
def event_exchange
|
38
|
+
ch.topic(@event_exchange_name, :durable => true)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
31
42
|
def delayed_in_exchange
|
32
43
|
ch.topic("#{@app_id}_delayed_in_x", :durable => true)
|
33
44
|
end
|
@@ -37,12 +48,14 @@ class GorgService
|
|
37
48
|
end
|
38
49
|
|
39
50
|
def job_queue
|
40
|
-
GorgService.logger.debug @listened_routing_keys
|
51
|
+
GorgService.logger.debug "Listened keys :#{@listened_routing_keys}"
|
41
52
|
q=ch.queue("#{@app_id}_job_q", :durable => true)
|
42
53
|
q.bind delayed_out_exchange
|
43
54
|
@listened_routing_keys.each do |rk|
|
44
|
-
q.bind(
|
55
|
+
q.bind(event_exchange, :routing_key => rk)
|
45
56
|
end
|
57
|
+
q.bind(reply_exchange, :routing_key => "#")
|
58
|
+
q.bind(request_exchange, :routing_key => "#")
|
46
59
|
q
|
47
60
|
end
|
48
61
|
|
@@ -50,11 +63,29 @@ class GorgService
|
|
50
63
|
@delayed_queues[routing_key]||= create_delayed_queue_for(routing_key)
|
51
64
|
end
|
52
65
|
|
66
|
+
def find_exchange_by_name(name, type: 'topic', opts: {})
|
67
|
+
begin
|
68
|
+
ch.send(type,name,opts)
|
69
|
+
rescue Bunny::PreconditionFailed => e
|
70
|
+
regex=/PRECONDITION_FAILED - inequivalent arg '(?<arg>.*)' for exchange '(?<exchange>.*)' in vhost '(?<vhost>.*)': received '(?<our>.*)' but current is '(?<their>.*)'/
|
71
|
+
match=regex.match(e.message)
|
72
|
+
|
73
|
+
case match[:arg]
|
74
|
+
when "type"
|
75
|
+
find_exchange_by_name(name,type: match[:their],opts: opts)
|
76
|
+
else
|
77
|
+
find_exchange_by_name(name,type: type,opts: opts.merge({ match[:arg].to_sym => match[:their]}))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
53
82
|
private
|
54
83
|
|
55
84
|
def set_logger
|
56
85
|
x=ch.fanout("log", :durable => true)
|
57
|
-
x.bind(
|
86
|
+
x.bind(event_exchange, :routing_key => "#")
|
87
|
+
x.bind(reply_exchange, :routing_key => "#")
|
88
|
+
x.bind(request_exchange, :routing_key => "#")
|
58
89
|
x.bind(delayed_in_exchange, :routing_key => "#")
|
59
90
|
end
|
60
91
|
|
@@ -82,6 +113,4 @@ class GorgService
|
|
82
113
|
end
|
83
114
|
|
84
115
|
end
|
85
|
-
end
|
86
|
-
|
87
|
-
#ch.queue(test, durable: true, arguments: {'x-message-ttl' => 1000,'x-dead-letter-exchange' => "agoram_event_exchange",'x-dead-letter-routing-key' => "test",})
|
116
|
+
end
|
data/lib/gorg_service/version.rb
CHANGED
data/lib/gorg_service.rb
CHANGED
@@ -1,83 +1,43 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
require "gorg_service/configuration"
|
3
3
|
require "gorg_service/version"
|
4
|
-
require "gorg_service/errors"
|
5
4
|
require "gorg_service/rabbitmq_env_builder"
|
6
|
-
require "gorg_service/listener"
|
7
5
|
require "gorg_service/message"
|
8
|
-
require "gorg_service/message_handler"
|
9
6
|
|
10
|
-
|
11
|
-
|
7
|
+
require "gorg_service/consumer"
|
8
|
+
require "gorg_service/producer"
|
12
9
|
|
13
10
|
class GorgService
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
class << self
|
12
|
+
|
13
|
+
#Connection shared across Consumers and Producers (thread safe)
|
14
|
+
def connection
|
15
|
+
@bunny_session||=Bunny.new(
|
16
|
+
:hostname => GorgService.configuration.rabbitmq_host,
|
17
|
+
:port => GorgService.configuration.rabbitmq_port,
|
18
|
+
:user => GorgService.configuration.rabbitmq_user,
|
19
|
+
:pass => GorgService.configuration.rabbitmq_password,
|
20
|
+
:vhost => GorgService.configuration.rabbitmq_vhost
|
22
21
|
)
|
22
|
+
@bunny_session.start unless @bunny_session.connected?
|
23
|
+
@bunny_session
|
24
|
+
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@listener= listener || Listener.new(
|
34
|
-
bunny_session: @bunny_session,
|
35
|
-
message_handler_map:GorgService.configuration.message_handler_map,
|
36
|
-
env: @env,
|
37
|
-
max_attempts: GorgService.configuration.rabbitmq_max_attempts.to_i,
|
38
|
-
log_routing_key: GorgService.configuration.log_routing_key
|
26
|
+
#Environment buidler. Don't share across threads since channels are not thread safe
|
27
|
+
def environment
|
28
|
+
RabbitmqEnvBuilder.new(
|
29
|
+
conn: connection,
|
30
|
+
event_exchange: GorgService.configuration.rabbitmq_event_exchange_name,
|
31
|
+
app_id: GorgService.configuration.application_id,
|
32
|
+
deferred_time: GorgService.configuration.rabbitmq_deferred_time.to_i,
|
33
|
+
listened_routing_keys: Consumer::MessageRouter.listened_keys,
|
34
|
+
prefetch: GorgService.configuration.prefetch_count,
|
39
35
|
)
|
40
|
-
|
41
|
-
RabbitmqProducer.configure do |c|
|
42
|
-
# Id used to set the event_sender_id
|
43
|
-
c.application_id = GorgService.configuration.application_id
|
44
|
-
|
45
|
-
# RabbitMQ network and authentification
|
46
|
-
c.host = GorgService.configuration.rabbitmq_host
|
47
|
-
c.port = GorgService.configuration.rabbitmq_port
|
48
|
-
c.vhost = GorgService.configuration.rabbitmq_vhost
|
49
|
-
c.user = GorgService.configuration.rabbitmq_user
|
50
|
-
c.password = GorgService.configuration.rabbitmq_password
|
51
|
-
|
52
|
-
# Exchange configuration
|
53
|
-
c.exchange_name = GorgService.configuration.rabbitmq_exchange_name
|
54
|
-
c.durable_exchange= true
|
55
36
|
end
|
56
37
|
|
57
|
-
|
58
|
-
|
59
|
-
def run
|
60
|
-
begin
|
61
|
-
self.start
|
62
|
-
puts " [*] Waiting for messages. To exit press CTRL+C"
|
63
|
-
loop do
|
64
|
-
sleep(1)
|
65
|
-
end
|
66
|
-
rescue SystemExit, Interrupt => _
|
67
|
-
self.stop
|
38
|
+
def logger
|
39
|
+
GorgService.configuration.logger
|
68
40
|
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def start
|
72
|
-
@bunny_session.start
|
73
|
-
@listener.listen
|
74
|
-
end
|
75
|
-
|
76
|
-
def stop
|
77
|
-
@bunny_session.close
|
78
|
-
end
|
79
41
|
|
80
|
-
def self.logger
|
81
|
-
GorgService.configuration.logger
|
82
42
|
end
|
83
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorg_service
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandre Narbonne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -50,14 +50,14 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 1.4.2
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
60
|
+
version: 1.4.2
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: bundler
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -163,12 +163,19 @@ files:
|
|
163
163
|
- gorg_service.gemspec
|
164
164
|
- lib/gorg_service.rb
|
165
165
|
- lib/gorg_service/configuration.rb
|
166
|
-
- lib/gorg_service/
|
167
|
-
- lib/gorg_service/
|
166
|
+
- lib/gorg_service/consumer.rb
|
167
|
+
- lib/gorg_service/consumer/errors.rb
|
168
|
+
- lib/gorg_service/consumer/listener.rb
|
169
|
+
- lib/gorg_service/consumer/message_handler.rb
|
170
|
+
- lib/gorg_service/consumer/message_handler/base.rb
|
171
|
+
- lib/gorg_service/consumer/message_handler/event_handler.rb
|
172
|
+
- lib/gorg_service/consumer/message_handler/reply_handler.rb
|
173
|
+
- lib/gorg_service/consumer/message_handler/request_handler.rb
|
174
|
+
- lib/gorg_service/consumer/message_router.rb
|
168
175
|
- lib/gorg_service/message.rb
|
169
176
|
- lib/gorg_service/message/error_log.rb
|
170
177
|
- lib/gorg_service/message/json_schema.rb
|
171
|
-
- lib/gorg_service/
|
178
|
+
- lib/gorg_service/producer.rb
|
172
179
|
- lib/gorg_service/rabbitmq_env_builder.rb
|
173
180
|
- lib/gorg_service/version.rb
|
174
181
|
homepage: https://github.com/Zooip/gorg_service
|
data/lib/gorg_service/errors.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
class GorgService
|
5
|
-
|
6
|
-
#Common behavior of failling errors
|
7
|
-
class FailError < StandardError
|
8
|
-
attr_reader :error_raised
|
9
|
-
|
10
|
-
def initialize(message = nil, error_raised = nil)
|
11
|
-
@message = message
|
12
|
-
@error_raised = error_raised
|
13
|
-
end
|
14
|
-
|
15
|
-
def message
|
16
|
-
@message
|
17
|
-
end
|
18
|
-
|
19
|
-
def type
|
20
|
-
""
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
#Softfail error : This message should be processed again later
|
25
|
-
class SoftfailError < FailError
|
26
|
-
def type
|
27
|
-
"softerror"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
#Hardfail error : This message is not processable and will never be
|
32
|
-
class HardfailError < FailError
|
33
|
-
def type
|
34
|
-
"harderror"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
require "bunny"
|
5
|
-
|
6
|
-
class GorgService
|
7
|
-
class Listener
|
8
|
-
|
9
|
-
def initialize(bunny_session: nil, env: nil, message_handler_map: {default: DefaultMessageHandler}, max_attempts: 48,log_routing_key:nil)
|
10
|
-
@message_handler_map=message_handler_map
|
11
|
-
@max_attempts=max_attempts.to_i
|
12
|
-
@rmq_connection=bunny_session
|
13
|
-
@log_routing_key=log_routing_key
|
14
|
-
|
15
|
-
@env=env
|
16
|
-
end
|
17
|
-
|
18
|
-
def listen
|
19
|
-
@env.job_queue.subscribe(:manual_ack => true) do |delivery_info, _properties, body|
|
20
|
-
routing_key=delivery_info[:routing_key]
|
21
|
-
GorgService.logger.info "Received message with routing key #{routing_key} containing : #{body}"
|
22
|
-
process_message(body,routing_key)
|
23
|
-
@env.ch.ack(delivery_info.delivery_tag)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def rmq_connection
|
30
|
-
@rmq_connection.start unless @rmq_connection.connected?
|
31
|
-
@rmq_connection
|
32
|
-
end
|
33
|
-
|
34
|
-
def process_message(body,routing_key)
|
35
|
-
message=nil
|
36
|
-
incomming_message_error_count=0
|
37
|
-
begin
|
38
|
-
message_handler=message_handler_for routing_key
|
39
|
-
raise HardfailError.new("Routing error : No message handler finded for this routing key") unless message_handler
|
40
|
-
|
41
|
-
begin
|
42
|
-
message=Message.parse_body(body)
|
43
|
-
rescue JSON::ParserError => e
|
44
|
-
raise HardfailError.new("JSON Parse error : Can't parse incoming message",e)
|
45
|
-
rescue JSON::Schema::ValidationError => e
|
46
|
-
raise HardfailError.new("Invalid JSON : This message does not respect Gadz.org JSON Schema",e)
|
47
|
-
end
|
48
|
-
incomming_message_error_count=message.errors.count
|
49
|
-
message_handler.new(message)
|
50
|
-
process_logging(message) if message.errors.count>incomming_message_error_count
|
51
|
-
rescue SoftfailError => e
|
52
|
-
process_softfail(e,message)
|
53
|
-
rescue HardfailError => e
|
54
|
-
process_hardfail(e,message)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def process_softfail(e,message)
|
59
|
-
message.log_error(e)
|
60
|
-
GorgService.logger.error "SOFTFAIL ERROR : #{e.message}"
|
61
|
-
if message.errors.count.to_i >= @max_attempts
|
62
|
-
GorgService.logger.info " DISCARD MESSAGE : #{message.errors.count} errors in message log"
|
63
|
-
process_hardfail(HardfailError.new("Too Much SoftError : This message reached the limit of softerror (max: #{@max_attempts})"),message)
|
64
|
-
else
|
65
|
-
send_to_deferred_queue(message)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def process_hardfail(e,message)
|
70
|
-
GorgService.logger.error "HARDFAIL ERROR : #{e.message}"
|
71
|
-
GorgService.logger.info " DISCARD MESSAGE"
|
72
|
-
if message
|
73
|
-
message.log_error(e)
|
74
|
-
process_logging(message)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def process_logging(message)
|
79
|
-
RabbitmqProducer.new.send_raw(message.to_json,@log_routing_key, verbose: true) if @log_routing_key
|
80
|
-
end
|
81
|
-
|
82
|
-
def send_to_deferred_queue(msg)
|
83
|
-
if @env.delayed_queue_for msg.event
|
84
|
-
@env.delayed_in_exchange.publish(msg.to_json, :routing_key => msg.event)
|
85
|
-
GorgService.logger.info "DEFER MESSAGE : message sent to #{@env.delayed_in_exchange.name} with routing key #{msg.event}"
|
86
|
-
else
|
87
|
-
raise "DelayedQueueNotFound"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def message_handler_for routing_key
|
92
|
-
@message_handler_map.each do |k,mh|
|
93
|
-
return mh if self.class.amqp_key_to_regex(k).match(routing_key)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.amqp_key_to_regex(key)
|
98
|
-
regex_base=key.gsub('.','\.')
|
99
|
-
.gsub('*','([a-zA-Z0-9\-_:]+)')
|
100
|
-
.gsub(/(\\\.)?#(\\\.)?/,'((\.)?[a-zA-Z0-9\-_:]*(\.)?)*')
|
101
|
-
|
102
|
-
/^#{regex_base}$/
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
106
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
class GorgService
|
5
|
-
class MessageHandler
|
6
|
-
|
7
|
-
def initialize(message)
|
8
|
-
GorgService.logger.warning "WARNING : Defined your MessageHandler behavior in its 'initialize' method"
|
9
|
-
end
|
10
|
-
|
11
|
-
def raise_hardfail(message, error:nil)
|
12
|
-
raise HardfailError.new(message, error)
|
13
|
-
end
|
14
|
-
|
15
|
-
def raise_softfail(message, error:nil)
|
16
|
-
raise SoftfailError.new(message, error)
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|