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