banter 0.5.0 → 0.7.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/.travis.yml +6 -0
- data/Gemfile +9 -0
- data/README.md +37 -3
- data/lib/banter.rb +4 -0
- data/lib/banter/cli.rb +13 -3
- data/lib/banter/configuration.rb +10 -0
- data/lib/banter/middleware.rb +9 -1
- data/lib/banter/publisher.rb +45 -4
- data/lib/banter/rabbit_logger.rb +22 -4
- data/lib/banter/railtie.rb +6 -1
- data/lib/banter/server/client_queue_listener.rb +31 -7
- data/lib/banter/server/rabbit_mq_subscriber.rb +2 -1
- data/lib/banter/server/subscriber_server.rb +55 -23
- data/lib/banter/subscriber.rb +0 -1
- data/lib/banter/version.rb +1 -1
- data/lib/banter/web.rb +28 -0
- data/lib/banter/web/application.rb +36 -0
- data/lib/banter/web/helpers.rb +9 -0
- data/lib/banter/web/models/banter_message.rb +82 -0
- data/lib/banter/web/models/banter_worker.rb +68 -0
- data/spec/banter/cli_spec.rb +28 -6
- data/spec/banter/publisher_spec.rb +78 -0
- data/spec/banter/rabbit_logger_spec.rb +27 -2
- data/spec/banter/server/client_queue_listener_spec.rb +86 -22
- data/spec/banter/server/subscriber_server_spec.rb +87 -0
- data/spec/banter/web/application_spec.rb +10 -0
- data/spec/factories/banter_workers.rb +13 -0
- data/spec/mongoid.yml +7 -0
- data/spec/spec_helper.rb +28 -9
- data/web/assets/stylesheets/banter.css +66 -0
- data/web/views/index.haml +32 -0
- data/web/views/layout.haml +45 -0
- data/web/views/message.haml +52 -0
- data/web/views/messages.haml +20 -0
- data/web/views/subscribers.haml +17 -0
- data/web/views/worker.haml +51 -0
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aaa823c8ce67b181474196407a8f92bab5c396ab
|
4
|
+
data.tar.gz: 0a3a05c26ce018f3b4c19a9c849d2cb353c26991
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78049fb9a4a98e41e8fe0e8f09bf40ec192529ea0fa79d76a84d9664ac9c529fc1f30e281cb7b464f732ce084f39d399b4c1eb1a2d9586a0a44878ef73d98724
|
7
|
+
data.tar.gz: 961168e99870c9cde24af76a2cea4182eacff90235d990ba046d61cbd561da4df8205a80558aec08f85f3110601cfaecfa8617dc465f4fb3509493cdda0def15
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -2,3 +2,12 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in banter.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'rack-test'
|
8
|
+
gem 'sinatra'
|
9
|
+
gem 'haml'
|
10
|
+
gem 'mongoid', github: 'mongoid/mongoid'
|
11
|
+
gem 'timecop'
|
12
|
+
gem "factory_girl", "~> 4.0"
|
13
|
+
end
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Banter
|
1
|
+
# Banter
|
2
2
|
|
3
3
|
Simple Publishers and subscribers for Ruby using RabbitMQ
|
4
4
|
|
@@ -33,7 +33,7 @@ There are two sides to this gem, publishers and subscribers
|
|
33
33
|
|
34
34
|
Publishing to a message is super simple
|
35
35
|
|
36
|
-
Banter.publish('user.created',
|
36
|
+
Banter.publish('user.created', id: 3, name: 'Foo Bar', email: 'foobar@email.com')
|
37
37
|
|
38
38
|
Additionally you can setup a context that is passed down to the subscribers. This context can be set up in a `before_filter`
|
39
39
|
|
@@ -81,11 +81,12 @@ Usage: bundle exec start_subscribers [options]
|
|
81
81
|
-P, --pidfile PATH path to pidfile
|
82
82
|
-o, --only [SUBSCRIBERS] comma separated name of subsriber classes that should be run
|
83
83
|
-r, --require [PATH|DIR] Location of Rails application with workers or file to require
|
84
|
+
-n, --name [PROCESS_NAME] Name of the process
|
84
85
|
-v, --version Print version and exit
|
85
86
|
|
86
87
|
```
|
87
88
|
|
88
|
-
#### Validations
|
89
|
+
#### Payload Validations
|
89
90
|
|
90
91
|
```ruby
|
91
92
|
class UserWelcomeSubscriber < Banter::Subscriber
|
@@ -107,6 +108,39 @@ class UserWelcomeSubscriber < Banter::Subscriber
|
|
107
108
|
end
|
108
109
|
```
|
109
110
|
|
111
|
+
### Dashboards!!!
|
112
|
+
|
113
|
+
Banter provides dashboards that can be easily enabled in any Rack application.
|
114
|
+
|
115
|
+
For a Rails application
|
116
|
+
|
117
|
+
```
|
118
|
+
# config/application.rb
|
119
|
+
config.banter.web_enabled = true
|
120
|
+
```
|
121
|
+
|
122
|
+
```
|
123
|
+
#config/routes.rb
|
124
|
+
mount Banter::Web::Application => '/banter'
|
125
|
+
```
|
126
|
+
|
127
|
+
or
|
128
|
+
|
129
|
+
```
|
130
|
+
#config.routes.rb
|
131
|
+
authenticate :admin do
|
132
|
+
mount Banter::Web::Application => '/banter'
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
Dashboards currently use Mongo DB (Mongoid gem) to store data, so the following gems should be added to your gemfile
|
137
|
+
```
|
138
|
+
gem 'mongoid'
|
139
|
+
gem 'haml'
|
140
|
+
gem 'sinatra'
|
141
|
+
```
|
142
|
+
|
143
|
+
|
110
144
|
## Contributing
|
111
145
|
|
112
146
|
1. Fork it
|
data/lib/banter.rb
CHANGED
@@ -31,6 +31,10 @@ module Banter
|
|
31
31
|
Publisher.instance.publish(Banter::Context.instance, routing_key, payload)
|
32
32
|
end
|
33
33
|
|
34
|
+
def self.delay_messages
|
35
|
+
Publisher.instance.delay_messages{ yield }
|
36
|
+
end
|
37
|
+
|
34
38
|
# @return [Logger] Logger used for logging through the Banter gem
|
35
39
|
def self.logger
|
36
40
|
return Banter::Configuration.logger if Banter::Configuration.logger
|
data/lib/banter/cli.rb
CHANGED
@@ -10,7 +10,7 @@ module Banter
|
|
10
10
|
class CLI
|
11
11
|
include Singleton
|
12
12
|
|
13
|
-
attr_accessor :pidfile, :subscribers
|
13
|
+
attr_accessor :pidfile, :subscribers, :process_name
|
14
14
|
|
15
15
|
# Method to support parsing of arguments passed through the command line
|
16
16
|
def parse(args = ARGV)
|
@@ -28,7 +28,11 @@ module Banter
|
|
28
28
|
@require_path = arg
|
29
29
|
end
|
30
30
|
|
31
|
-
opts.on '-
|
31
|
+
opts.on '-n', '--name [PROCESS_NAME]', "Name of the process" do |arg|
|
32
|
+
@process_name = arg
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on '-v', '--version', "Print version and exit" do
|
32
36
|
puts "Banter #{Banter::VERSION}"
|
33
37
|
abort
|
34
38
|
end
|
@@ -38,6 +42,7 @@ module Banter
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def run
|
45
|
+
change_process_name
|
41
46
|
load_environment
|
42
47
|
write_pidfile
|
43
48
|
load_subscribers
|
@@ -87,7 +92,12 @@ module Banter
|
|
87
92
|
end
|
88
93
|
|
89
94
|
def load_subscribers
|
90
|
-
Banter::Server::SubscriberServer.
|
95
|
+
Banter::Server::SubscriberServer.instance.set_workers(subscriber_classes)
|
96
|
+
Banter::Server::SubscriberServer.instance.start
|
97
|
+
end
|
98
|
+
|
99
|
+
def change_process_name
|
100
|
+
$PROGRAM_NAME = @process_name || "banter_subscriber"
|
91
101
|
end
|
92
102
|
end
|
93
103
|
end
|
data/lib/banter/configuration.rb
CHANGED
@@ -30,10 +30,20 @@ module Banter
|
|
30
30
|
mattr_accessor :push_enabled
|
31
31
|
@@push_enabled = true
|
32
32
|
|
33
|
+
# RabbitMQ batches messages and delays until the end of a request
|
34
|
+
# @default false
|
35
|
+
mattr_accessor :batch_messages
|
36
|
+
@@batch_messages = false
|
37
|
+
|
33
38
|
# Dead letter queue name
|
34
39
|
# @default nil
|
35
40
|
mattr_accessor :dead_letter_queue
|
36
41
|
|
42
|
+
# Enable web monitoring
|
43
|
+
# @default false
|
44
|
+
mattr_accessor :web_enabled
|
45
|
+
@@web_enabled = false
|
46
|
+
|
37
47
|
def self.configure_with(environment_name, yaml_file = nil)
|
38
48
|
@@yaml_file = yaml_file.nil? ? "config/banter.yml" : yaml_file
|
39
49
|
@@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
|
data/lib/banter/middleware.rb
CHANGED
@@ -2,13 +2,21 @@
|
|
2
2
|
|
3
3
|
module Banter
|
4
4
|
class Middleware
|
5
|
+
@@cached = nil
|
5
6
|
def initialize(app)
|
6
7
|
@app = app
|
7
8
|
end
|
8
9
|
|
9
10
|
def call(env)
|
10
11
|
Banter::Context.clear!
|
11
|
-
|
12
|
+
if @@cached.nil?
|
13
|
+
@@cached = Banter::Configuration.batch_messages
|
14
|
+
end
|
15
|
+
if @@cached
|
16
|
+
Banter.dedupe_messages { @app.call(env) }
|
17
|
+
else
|
18
|
+
@app.call(env)
|
19
|
+
end
|
12
20
|
end
|
13
21
|
end
|
14
22
|
end
|
data/lib/banter/publisher.rb
CHANGED
@@ -15,6 +15,7 @@ module Banter
|
|
15
15
|
def initialize(exchange = nil)
|
16
16
|
@exchange = exchange || Banter::Configuration.exchange_name
|
17
17
|
@disabled = false
|
18
|
+
@batch_messages = false
|
18
19
|
end
|
19
20
|
|
20
21
|
def enable(value)
|
@@ -22,6 +23,13 @@ module Banter
|
|
22
23
|
@disabled
|
23
24
|
end
|
24
25
|
|
26
|
+
def delay_messages
|
27
|
+
delay_start
|
28
|
+
yield
|
29
|
+
ensure
|
30
|
+
delay_execute
|
31
|
+
end
|
32
|
+
|
25
33
|
def start
|
26
34
|
unless Configuration.push_enabled
|
27
35
|
@disabled = true
|
@@ -41,7 +49,6 @@ module Banter
|
|
41
49
|
return
|
42
50
|
end
|
43
51
|
|
44
|
-
|
45
52
|
@publisher.on_return do |return_info, properties, content|
|
46
53
|
# contents are already transformed into message that we want to send
|
47
54
|
Banter::RabbitLogger.failed_publish(return_info[:routing_key], properties, content)
|
@@ -49,29 +56,45 @@ module Banter
|
|
49
56
|
|
50
57
|
end
|
51
58
|
|
52
|
-
def publish(context, key, payload
|
59
|
+
def publish(context, key, payload)
|
53
60
|
routing_key = "#{@exchange}.#{key}"
|
54
61
|
envelope = ::Banter::Message.new.serialize(context, key, payload)
|
55
62
|
|
63
|
+
if @batch_messages
|
64
|
+
add_message(routing_key, envelope)
|
65
|
+
else
|
66
|
+
execute_publish(routing_key, envelope)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def execute_publish(routing_key, envelope)
|
72
|
+
|
56
73
|
if @publisher.nil?
|
57
74
|
start
|
58
75
|
end
|
59
76
|
|
60
|
-
if @disabled || @publisher.nil?
|
77
|
+
if @disabled || @publisher.nil?
|
61
78
|
Banter::RabbitLogger.failed_publish(routing_key, {}, envelope)
|
62
79
|
else
|
63
80
|
tries = 2
|
64
81
|
begin
|
65
82
|
@publisher.publish(envelope.to_json, :persistent => true, :mandatory => true, :timestamp => envelope[:ts], :content_type => "application/json", :routing_key => routing_key)
|
66
83
|
Banter::RabbitLogger.log_publish(routing_key, envelope)
|
84
|
+
# FIX!!! -thl
|
85
|
+
# What kind of errors could be fired from a failure to publish?
|
86
|
+
# Should we be more specific?
|
87
|
+
# Docs only have errors while connecting, and really not during some sort of long running socket. For now
|
88
|
+
# We'll log until we get more info.
|
67
89
|
rescue => e
|
90
|
+
Banter::RabbitLogger.log(Logger::WARN, "Error occured on publish: #{e.message}: #{e.inspect}, #{routing_key}: #{envelope.inspect}")
|
68
91
|
tries -= 1
|
69
92
|
teardown
|
70
93
|
start
|
71
94
|
if tries > 0 && @publisher
|
72
95
|
retry
|
73
96
|
else
|
74
|
-
Banter::RabbitLogger.failed_publish(routing_key, {}, envelope)
|
97
|
+
Banter::RabbitLogger.failed_publish(routing_key, { error: e.message }, envelope)
|
75
98
|
end
|
76
99
|
end
|
77
100
|
|
@@ -80,9 +103,27 @@ module Banter
|
|
80
103
|
end
|
81
104
|
|
82
105
|
def teardown
|
106
|
+
# FIX!!! -thl
|
107
|
+
# How can I check to see if the connection is valid quickly without trying a close on the connection?
|
83
108
|
@connection.close
|
84
109
|
@publisher = nil
|
85
110
|
end
|
86
111
|
|
112
|
+
def delay_start
|
113
|
+
@batch_messages = true
|
114
|
+
@messages = []
|
115
|
+
end
|
116
|
+
|
117
|
+
def delay_execute
|
118
|
+
@messages.each do |key, envelope|
|
119
|
+
execute_publish(key, envelope)
|
120
|
+
end
|
121
|
+
@messages.clear
|
122
|
+
@batch_messages = false
|
123
|
+
end
|
124
|
+
|
125
|
+
def add_message(key, envelope)
|
126
|
+
@messages << [key, envelope]
|
127
|
+
end
|
87
128
|
end
|
88
129
|
end
|
data/lib/banter/rabbit_logger.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Internal log class that is used to log messages before sending, after receiving, and failure to send
|
2
2
|
module Banter
|
3
|
+
|
3
4
|
class RabbitLogger
|
4
5
|
def self.enabled?
|
5
6
|
Banter::Configuration.logging_enabled
|
@@ -8,25 +9,42 @@ module Banter
|
|
8
9
|
def self.log_publish(routing_key, message)
|
9
10
|
return unless enabled?
|
10
11
|
tags = ["BANTER PUBLISH", message[:ts], message[:pub], message[:v], routing_key]
|
11
|
-
|
12
|
+
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
12
13
|
end
|
13
14
|
|
14
15
|
def self.failed_publish(routing_key, properties, message)
|
15
16
|
return unless enabled?
|
16
17
|
tags = ["BANTER FAILED_SEND", message[:ts], message[:pub], message[:v], routing_key]
|
17
|
-
|
18
|
+
logger.tagged(tags) { logger.error( { properties: properties, payload:message[:payload] }.as_json ) }
|
18
19
|
end
|
19
20
|
|
20
21
|
def self.log_receive(routing_key, message)
|
21
22
|
return unless enabled?
|
22
23
|
tags = ["BANTER RECEIVED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
|
23
|
-
|
24
|
+
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.log_complete(routing_key, message)
|
28
|
+
return unless enabled?
|
29
|
+
tags = ["BANTER COMPLETED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
|
30
|
+
logger.tagged(tags) { logger.warn message[:payload].as_json }
|
24
31
|
end
|
25
32
|
|
26
33
|
def self.log_service(service_name, message)
|
27
34
|
return unless enabled?
|
28
35
|
tags = ["BANTER SERVICE", service_name, Process::pid]
|
29
|
-
|
36
|
+
logger.tagged(tags) { logger.info message.as_json}
|
30
37
|
end
|
38
|
+
|
39
|
+
@@log_map = [:debug, :info, :warn, :error, :fatal]
|
40
|
+
def self.log(log_level, message)
|
41
|
+
tags = ["BANTER LOG_LEVEL:#{@@log_map[log_level].capitalize.to_s}", Process::pid]
|
42
|
+
logger.tagged(tags) { logger.send(@@log_map[log_level], message.as_json ) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.logger
|
46
|
+
Banter.logger
|
47
|
+
end
|
48
|
+
|
31
49
|
end
|
32
50
|
end
|
data/lib/banter/railtie.rb
CHANGED
@@ -11,7 +11,12 @@ module Banter
|
|
11
11
|
end
|
12
12
|
|
13
13
|
config.after_initialize do
|
14
|
-
Banter.logger =
|
14
|
+
Banter.logger = config.banter.logger if config.banter.logger
|
15
|
+
|
16
|
+
if Banter::Configuration.web_enabled
|
17
|
+
require 'banter/web'
|
18
|
+
end
|
19
|
+
|
15
20
|
end
|
16
21
|
|
17
22
|
console do
|
@@ -28,17 +28,27 @@ module Banter
|
|
28
28
|
|
29
29
|
def shutdown
|
30
30
|
@subscriber.teardown if @subscriber.present?
|
31
|
-
# TODO -thl
|
32
|
-
# This the kosher thing to do in ruby?
|
33
|
-
teardown
|
34
31
|
end
|
35
32
|
|
36
33
|
def message_received(delivery_info, properties, envelope)
|
37
|
-
Banter.
|
34
|
+
Banter::RabbitLogger.log(::Logger::DEBUG, "Message received by listener on #{worker_class.name} with payload: #{envelope[:payload]}")
|
38
35
|
worker = worker_class.new(delivery_info, properties, envelope[:context])
|
39
|
-
|
36
|
+
payload = envelope[:payload]
|
37
|
+
track_message_received(worker, payload)
|
38
|
+
worker.perform!(payload)
|
39
|
+
track_message_successful
|
40
|
+
rescue ::Banter::PayloadValidationError => banter_error
|
41
|
+
track_message_failed(banter_error.message, false)
|
42
|
+
Banter::RabbitLogger.log(::Logger::ERROR, { worker: worker_class.name, message: banter_error.message, envelope: envelope })
|
43
|
+
Airbrake.notify("Validation error for #{worker_class.name}",
|
44
|
+
params: {
|
45
|
+
info: delivery_info,
|
46
|
+
properties: properties,
|
47
|
+
context: envelope[:context],
|
48
|
+
payload: envelope[:payload] })
|
40
49
|
rescue => e
|
41
|
-
|
50
|
+
track_message_failed(e.message, true)
|
51
|
+
Banter::RabbitLogger.log(::Logger::DEBUG, "Failed message for #{worker_class.name} #{e.message}")
|
42
52
|
Airbrake.notify("Failed perform for #{worker_class.name}",
|
43
53
|
params: {
|
44
54
|
info: delivery_info,
|
@@ -49,7 +59,21 @@ module Banter
|
|
49
59
|
end
|
50
60
|
|
51
61
|
protected
|
52
|
-
|
62
|
+
|
63
|
+
def track_message_successful
|
64
|
+
return unless Banter::Configuration.web_enabled
|
65
|
+
BanterMessage.message_successful
|
66
|
+
end
|
67
|
+
|
68
|
+
def track_message_failed(error_message, error = true)
|
69
|
+
return unless Banter::Configuration.web_enabled
|
70
|
+
status = error ? BanterMessage::STATUS_ERROR : BanterMessage::STATUS_VALIDATION_FAILED
|
71
|
+
BanterMessage.message_failed(error_message, status)
|
72
|
+
end
|
73
|
+
|
74
|
+
def track_message_received(worker, payload)
|
75
|
+
return unless Banter::Configuration.web_enabled
|
76
|
+
BanterMessage.message_received(worker_class, worker, payload)
|
53
77
|
end
|
54
78
|
end
|
55
79
|
end
|
@@ -54,13 +54,14 @@ module Banter
|
|
54
54
|
@consumer = @listener.subscribe(consumer_tag: name, block: blocking)
|
55
55
|
|
56
56
|
@consumer.on_delivery do |delivery_info, properties, contents|
|
57
|
-
Banter.
|
57
|
+
Banter::RabbitLogger.log(Logger::DEBUG, "Message delivery with contents: #{contents}")
|
58
58
|
if delivery_info[:redelivered]
|
59
59
|
Airbrake.notify("PubSub Message redelivery", params: { info: delivery_info, props: properties, contents: contents }, environment_name: ENV['RAILS_ENV'])
|
60
60
|
end
|
61
61
|
message = ::Banter::Message.new.parse(contents)
|
62
62
|
Banter::RabbitLogger.log_receive(delivery_info[:routing_key], message)
|
63
63
|
yield delivery_info, properties, message
|
64
|
+
Banter::RabbitLogger.log_complete(delivery_info[:routing_key], message)
|
64
65
|
true
|
65
66
|
end
|
66
67
|
end
|