rabbit_feed 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/Brewfile +4 -0
- data/DEVELOPING.md +140 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +121 -0
- data/LICENSE.txt +9 -0
- data/README.md +304 -0
- data/Rakefile +30 -0
- data/bin/bundle +3 -0
- data/bin/rabbit_feed +11 -0
- data/example/non_rails_app/.rspec +1 -0
- data/example/non_rails_app/Gemfile +7 -0
- data/example/non_rails_app/Gemfile.lock +56 -0
- data/example/non_rails_app/Rakefile +5 -0
- data/example/non_rails_app/bin/benchmark +63 -0
- data/example/non_rails_app/bin/bundle +3 -0
- data/example/non_rails_app/config/rabbit_feed.yml +8 -0
- data/example/non_rails_app/lib/non_rails_app.rb +32 -0
- data/example/non_rails_app/lib/non_rails_app/event_handler.rb +10 -0
- data/example/non_rails_app/log/.keep +0 -0
- data/example/non_rails_app/spec/lib/non_rails_app/event_handler_spec.rb +14 -0
- data/example/non_rails_app/spec/lib/non_rails_app/event_routing_spec.rb +14 -0
- data/example/non_rails_app/spec/spec_helper.rb +31 -0
- data/example/non_rails_app/tmp/pids/.keep +0 -0
- data/example/rails_app/.gitignore +17 -0
- data/example/rails_app/.node-version +1 -0
- data/example/rails_app/.rspec +1 -0
- data/example/rails_app/Gemfile +36 -0
- data/example/rails_app/Gemfile.lock +173 -0
- data/example/rails_app/README.rdoc +28 -0
- data/example/rails_app/Rakefile +6 -0
- data/example/rails_app/app/assets/images/.keep +0 -0
- data/example/rails_app/app/assets/javascripts/application.js +16 -0
- data/example/rails_app/app/assets/javascripts/beavers.js.coffee +3 -0
- data/example/rails_app/app/assets/stylesheets/application.css +15 -0
- data/example/rails_app/app/assets/stylesheets/beavers.css.scss +3 -0
- data/example/rails_app/app/assets/stylesheets/scaffolds.css.scss +69 -0
- data/example/rails_app/app/controllers/application_controller.rb +5 -0
- data/example/rails_app/app/controllers/beavers_controller.rb +81 -0
- data/example/rails_app/app/controllers/concerns/.keep +0 -0
- data/example/rails_app/app/helpers/application_helper.rb +2 -0
- data/example/rails_app/app/helpers/beavers_helper.rb +2 -0
- data/example/rails_app/app/mailers/.keep +0 -0
- data/example/rails_app/app/models/.keep +0 -0
- data/example/rails_app/app/models/beaver.rb +2 -0
- data/example/rails_app/app/models/concerns/.keep +0 -0
- data/example/rails_app/app/views/beavers/_form.html.erb +21 -0
- data/example/rails_app/app/views/beavers/edit.html.erb +6 -0
- data/example/rails_app/app/views/beavers/index.html.erb +25 -0
- data/example/rails_app/app/views/beavers/index.json.jbuilder +4 -0
- data/example/rails_app/app/views/beavers/new.html.erb +5 -0
- data/example/rails_app/app/views/beavers/show.html.erb +9 -0
- data/example/rails_app/app/views/beavers/show.json.jbuilder +1 -0
- data/example/rails_app/app/views/layouts/application.html.erb +14 -0
- data/example/rails_app/bin/bundle +3 -0
- data/example/rails_app/bin/rails +4 -0
- data/example/rails_app/bin/rake +4 -0
- data/example/rails_app/config.ru +4 -0
- data/example/rails_app/config/application.rb +25 -0
- data/example/rails_app/config/boot.rb +4 -0
- data/example/rails_app/config/database.yml +22 -0
- data/example/rails_app/config/environment.rb +5 -0
- data/example/rails_app/config/environments/development.rb +83 -0
- data/example/rails_app/config/environments/test.rb +39 -0
- data/example/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/example/rails_app/config/initializers/cookies_serializer.rb +3 -0
- data/example/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/example/rails_app/config/initializers/inflections.rb +16 -0
- data/example/rails_app/config/initializers/mime_types.rb +4 -0
- data/example/rails_app/config/initializers/rabbit_feed.rb +43 -0
- data/example/rails_app/config/initializers/session_store.rb +3 -0
- data/example/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/example/rails_app/config/locales/en.yml +23 -0
- data/example/rails_app/config/rabbit_feed.yml +8 -0
- data/example/rails_app/config/routes.rb +58 -0
- data/example/rails_app/config/secrets.yml +18 -0
- data/example/rails_app/config/unicorn.rb +4 -0
- data/example/rails_app/db/migrate/20140424102400_create_beavers.rb +9 -0
- data/example/rails_app/db/schema.rb +22 -0
- data/example/rails_app/db/seeds.rb +7 -0
- data/example/rails_app/lib/assets/.keep +0 -0
- data/example/rails_app/lib/event_handler.rb +7 -0
- data/example/rails_app/lib/tasks/.keep +0 -0
- data/example/rails_app/log/.keep +0 -0
- data/example/rails_app/public/404.html +67 -0
- data/example/rails_app/public/422.html +67 -0
- data/example/rails_app/public/500.html +66 -0
- data/example/rails_app/public/favicon.ico +0 -0
- data/example/rails_app/public/robots.txt +5 -0
- data/example/rails_app/spec/controllers/beavers_controller_spec.rb +32 -0
- data/example/rails_app/spec/event_routing_spec.rb +15 -0
- data/example/rails_app/spec/spec_helper.rb +51 -0
- data/example/rails_app/test/controllers/.keep +0 -0
- data/example/rails_app/test/controllers/beavers_controller_test.rb +49 -0
- data/example/rails_app/test/fixtures/.keep +0 -0
- data/example/rails_app/test/fixtures/beavers.yml +7 -0
- data/example/rails_app/test/helpers/.keep +0 -0
- data/example/rails_app/test/helpers/beavers_helper_test.rb +4 -0
- data/example/rails_app/test/integration/.keep +0 -0
- data/example/rails_app/test/mailers/.keep +0 -0
- data/example/rails_app/test/models/.keep +0 -0
- data/example/rails_app/test/models/beaver_test.rb +7 -0
- data/example/rails_app/test/test_helper.rb +13 -0
- data/example/rails_app/tmp/pids/.keep +0 -0
- data/example/rails_app/vendor/assets/javascripts/.keep +0 -0
- data/example/rails_app/vendor/assets/stylesheets/.keep +0 -0
- data/lib/dsl.rb +9 -0
- data/lib/rabbit_feed.rb +41 -0
- data/lib/rabbit_feed/client.rb +181 -0
- data/lib/rabbit_feed/configuration.rb +50 -0
- data/lib/rabbit_feed/connection_concern.rb +95 -0
- data/lib/rabbit_feed/consumer.rb +14 -0
- data/lib/rabbit_feed/consumer_connection.rb +108 -0
- data/lib/rabbit_feed/event.rb +43 -0
- data/lib/rabbit_feed/event_definitions.rb +98 -0
- data/lib/rabbit_feed/event_routing.rb +90 -0
- data/lib/rabbit_feed/producer.rb +47 -0
- data/lib/rabbit_feed/producer_connection.rb +65 -0
- data/lib/rabbit_feed/testing_support/rspec_matchers/publish_event.rb +90 -0
- data/lib/rabbit_feed/testing_support/testing_helpers.rb +16 -0
- data/lib/rabbit_feed/version.rb +3 -0
- data/logo.png +0 -0
- data/rabbit_feed.gemspec +35 -0
- data/run_benchmark +35 -0
- data/run_example +62 -0
- data/run_recovery_test +26 -0
- data/spec/features/connectivity.feature +13 -0
- data/spec/features/step_definitions/connectivity_steps.rb +96 -0
- data/spec/fixtures/configuration.yml +14 -0
- data/spec/lib/rabbit_feed/client_spec.rb +116 -0
- data/spec/lib/rabbit_feed/configuration_spec.rb +121 -0
- data/spec/lib/rabbit_feed/connection_concern_spec.rb +116 -0
- data/spec/lib/rabbit_feed/consumer_connection_spec.rb +85 -0
- data/spec/lib/rabbit_feed/event_definitions_spec.rb +139 -0
- data/spec/lib/rabbit_feed/event_routing_spec.rb +121 -0
- data/spec/lib/rabbit_feed/event_spec.rb +33 -0
- data/spec/lib/rabbit_feed/producer_connection_spec.rb +72 -0
- data/spec/lib/rabbit_feed/producer_spec.rb +57 -0
- data/spec/lib/rabbit_feed/testing_support/rspec_matchers/publish_event_spec.rb +60 -0
- data/spec/lib/rabbit_feed/testing_support/testing_helper_spec.rb +34 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/support/shared_examples_for_connections.rb +40 -0
- metadata +305 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b89c207774f809ccb829ba29d9b11299dc2872b0
|
4
|
+
data.tar.gz: 9ff5943d611beed002969fcad6b62e9d75b4f1c2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2e612b191caae6071e6e5df07e0fc45f081353a68eae365be1ed5548a41d7b23ac82395436ca7e494d3f53d94724e4941041edd9986ed706d48b0e3e26e949ca
|
7
|
+
data.tar.gz: d76e833bf491449b3b641dd2346aa1f7b76de28acf7e1ff02d3f63416af311ec1d464a7a8f84f8fff3509251b0edc1c4150118b1318256a45e642a71b598c0f5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Brewfile
ADDED
data/DEVELOPING.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Developing RabbitFeed
|
2
|
+
|
3
|
+
## Prerequisites
|
4
|
+
|
5
|
+
### Ruby
|
6
|
+
|
7
|
+
RabbitFeed has been tested on Ruby v2.0.
|
8
|
+
|
9
|
+
Install the required gems with
|
10
|
+
|
11
|
+
gem install bundler
|
12
|
+
bundle install
|
13
|
+
|
14
|
+
### RabbitMQ running locally
|
15
|
+
|
16
|
+
brew project
|
17
|
+
rabbitmq-server
|
18
|
+
|
19
|
+
The management interface can be found [here](http://localhost:15672/). The default login is `guest/guest`. You can view exchanges [here](http://localhost:15672/#/exchanges) and queues [here](http://localhost:15672/#/queues).
|
20
|
+
|
21
|
+
|
22
|
+
## Running Tests
|
23
|
+
|
24
|
+
This will run the specs and the features:
|
25
|
+
|
26
|
+
bundle exec rake
|
27
|
+
|
28
|
+
## Running the example project
|
29
|
+
|
30
|
+
After doing any dev work, it is good practice to verify you haven't broken the examples. Run the examples like this:
|
31
|
+
|
32
|
+
./run_example
|
33
|
+
|
34
|
+
You should see something similar to the following output:
|
35
|
+
|
36
|
+
Starting non rails application consumer...
|
37
|
+
/opt/boxen/rbenv/versions/2.0.0-p451/bin/ruby -S rspec ./spec/lib/non_rails_app/event_handler_spec.rb
|
38
|
+
NonRailsApp::EventHandler - Consumed event: user_updates_beaver with payload: {"beaver_name"=>"beaver"}
|
39
|
+
.
|
40
|
+
|
41
|
+
Finished in 0.00349 seconds
|
42
|
+
1 example, 0 failures
|
43
|
+
|
44
|
+
Randomized with seed 59391
|
45
|
+
|
46
|
+
Non rails application consumer started
|
47
|
+
Starting rails application consumer...
|
48
|
+
/opt/boxen/rbenv/versions/2.0.0-p451/bin/ruby -S rspec ./spec/controllers/beavers_controller_spec.rb
|
49
|
+
...
|
50
|
+
|
51
|
+
Finished in 0.04397 seconds
|
52
|
+
3 examples, 0 failures
|
53
|
+
|
54
|
+
Randomized with seed 18883
|
55
|
+
|
56
|
+
Rails application consumer started
|
57
|
+
Starting rails application...
|
58
|
+
Rails application started
|
59
|
+
Triggering event...
|
60
|
+
NonRailsApp::EventHandler - Consumed event: user_creates_beaver with payload: {"application"=>"rails_app", "host"=>"macjfleck.home", "environment"=>"development", "version"=>"1.0.0", "name"=>"user_creates_beaver", "created_at_utc"=>"2014-05-05T14:16:44.045395Z", "beaver_name"=>"05/05/14 15:16:43"}
|
61
|
+
Event triggered
|
62
|
+
RailsApp::EventHandler - Consumed event: application_acknowledges_event with payload: {"application"=>"non_rails_app", "host"=>"macjfleck.home", "environment"=>"development", "version"=>"1.0.0", "name"=>"application_acknowledges_event", "created_at_utc"=>"2014-05-05T14:16:44.057279Z", "beaver_name"=>"05/05/14 15:16:43", "event_name"=>"user_creates_beaver"}
|
63
|
+
Stopping non rails application consumer...
|
64
|
+
Non rails application consumer stopped
|
65
|
+
Stopping rails application consumer...
|
66
|
+
Rails application consumer stopped
|
67
|
+
Stopping rails application...
|
68
|
+
Rails application stopped
|
69
|
+
|
70
|
+
## Performance Benchmarking
|
71
|
+
|
72
|
+
There is a script that can be run to benchmark the tool. It benchmarks three areas:
|
73
|
+
|
74
|
+
1. Producing events during HTTP requests within a rails application (using [siege](http://www.joedog.org/siege-home/))
|
75
|
+
2. Producing events directly
|
76
|
+
3. Consuming events
|
77
|
+
|
78
|
+
To run the benchmarking script
|
79
|
+
|
80
|
+
brew project
|
81
|
+
./run_benchmark
|
82
|
+
|
83
|
+
As of 20140925, running the benchmark on this hardware:
|
84
|
+
|
85
|
+
MacBook Pro Retina, Mid 2012
|
86
|
+
Processor 2.6 GHz Intel Core i7
|
87
|
+
Memory 8 GB 1600 MHz DDR3
|
88
|
+
Software OS X 10.8.5 (12F45)
|
89
|
+
|
90
|
+
Results in this output:
|
91
|
+
|
92
|
+
Starting test of rails application...
|
93
|
+
Starting rails application...
|
94
|
+
-- create_table("beavers", {:force=>true})
|
95
|
+
-> 0.0140s
|
96
|
+
-- initialize_schema_migrations_table()
|
97
|
+
-> 0.0196s
|
98
|
+
Rails application started
|
99
|
+
done.
|
100
|
+
|
101
|
+
Transactions: 200 hits
|
102
|
+
Availability: 100.00 %
|
103
|
+
Elapsed time: 1.33 secs
|
104
|
+
Data transferred: 0.16 MB
|
105
|
+
Response time: 0.06 secs
|
106
|
+
Transaction rate: 150.38 trans/sec
|
107
|
+
Throughput: 0.12 MB/sec
|
108
|
+
Concurrency: 8.76
|
109
|
+
Successful transactions: 100
|
110
|
+
Failed transactions: 0
|
111
|
+
Longest transaction: 0.36
|
112
|
+
Shortest transaction: 0.00
|
113
|
+
|
114
|
+
Stopping rails application...
|
115
|
+
Rails application stopped
|
116
|
+
Rails application test complete
|
117
|
+
|
118
|
+
|
119
|
+
Starting standalone publishing and consuming benchmark...
|
120
|
+
Publishing 5000 events...
|
121
|
+
user system total real
|
122
|
+
2.460000 0.310000 2.770000 ( 2.779719)
|
123
|
+
Consuming 5000 events...
|
124
|
+
user system total real
|
125
|
+
2.380000 0.590000 2.970000 ( 3.500515)
|
126
|
+
Benchmark complete
|
127
|
+
|
128
|
+
## Connection Recovery Testing
|
129
|
+
|
130
|
+
A critical piece of RabbitFeed is the ability to recover from network connectivity problems. This means...
|
131
|
+
|
132
|
+
* When publishing, all events that are published make it to the queue
|
133
|
+
* When consuming, the consumer re-establishes its connection to the queue automatically
|
134
|
+
|
135
|
+
To simulate network connectivity problems, there is a recovery test script that can be run like this:
|
136
|
+
|
137
|
+
brew project
|
138
|
+
./run_recovery_test
|
139
|
+
|
140
|
+
The script will publish and then consume 5000 mesages with the network dropping out every half-second.
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in rabbit_feed.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'pry-plus'
|
8
|
+
gem 'rake'
|
9
|
+
end
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem 'codeclimate-test-reporter'
|
13
|
+
gem 'rutabaga'
|
14
|
+
gem 'timecop'
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rabbit_feed (0.3.1)
|
5
|
+
activemodel
|
6
|
+
activesupport
|
7
|
+
avro
|
8
|
+
bunny
|
9
|
+
connection_pool
|
10
|
+
pidfile
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activemodel (4.1.6)
|
16
|
+
activesupport (= 4.1.6)
|
17
|
+
builder (~> 3.1)
|
18
|
+
activesupport (4.1.6)
|
19
|
+
i18n (~> 0.6, >= 0.6.9)
|
20
|
+
json (~> 1.7, >= 1.7.7)
|
21
|
+
minitest (~> 5.1)
|
22
|
+
thread_safe (~> 0.1)
|
23
|
+
tzinfo (~> 1.1)
|
24
|
+
amq-protocol (1.9.2)
|
25
|
+
avro (1.7.7)
|
26
|
+
multi_json
|
27
|
+
binding_of_caller (0.7.2)
|
28
|
+
debug_inspector (>= 0.0.1)
|
29
|
+
bond (0.4.3)
|
30
|
+
builder (3.2.2)
|
31
|
+
bunny (1.6.3)
|
32
|
+
amq-protocol (>= 1.9.2)
|
33
|
+
codeclimate-test-reporter (0.2.0)
|
34
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
35
|
+
coderay (1.1.0)
|
36
|
+
columnize (0.3.6)
|
37
|
+
connection_pool (2.0.0)
|
38
|
+
debug_inspector (0.0.2)
|
39
|
+
debugger (1.6.6)
|
40
|
+
columnize (>= 0.3.1)
|
41
|
+
debugger-linecache (~> 1.2.0)
|
42
|
+
debugger-ruby_core_source (~> 1.3.2)
|
43
|
+
debugger-linecache (1.2.0)
|
44
|
+
debugger-ruby_core_source (1.3.2)
|
45
|
+
diff-lcs (1.2.5)
|
46
|
+
docile (1.1.2)
|
47
|
+
gherkin (2.12.2)
|
48
|
+
multi_json (~> 1.3)
|
49
|
+
i18n (0.6.11)
|
50
|
+
interception (0.3)
|
51
|
+
jist (1.5.1)
|
52
|
+
json
|
53
|
+
json (1.8.1)
|
54
|
+
method_source (0.8.2)
|
55
|
+
minitest (5.4.2)
|
56
|
+
multi_json (1.9.2)
|
57
|
+
pidfile (0.3.0)
|
58
|
+
pry (0.9.12.4)
|
59
|
+
coderay (~> 1.0)
|
60
|
+
method_source (~> 0.8)
|
61
|
+
slop (~> 3.4)
|
62
|
+
pry-debugger (0.2.2)
|
63
|
+
debugger (~> 1.3)
|
64
|
+
pry (~> 0.9.10)
|
65
|
+
pry-doc (0.4.6)
|
66
|
+
pry (>= 0.9)
|
67
|
+
yard (>= 0.8)
|
68
|
+
pry-docmore (0.1.1)
|
69
|
+
pry
|
70
|
+
pry-doc
|
71
|
+
pry-plus (1.0.0)
|
72
|
+
bond
|
73
|
+
jist
|
74
|
+
pry-debugger
|
75
|
+
pry-doc
|
76
|
+
pry-docmore
|
77
|
+
pry-rescue
|
78
|
+
pry-stack_explorer
|
79
|
+
pry-rescue (1.2.0)
|
80
|
+
interception (>= 0.3)
|
81
|
+
pry
|
82
|
+
pry-stack_explorer (0.4.9.1)
|
83
|
+
binding_of_caller (>= 0.7)
|
84
|
+
pry (>= 0.9.11)
|
85
|
+
rake (10.2.2)
|
86
|
+
rspec (2.14.1)
|
87
|
+
rspec-core (~> 2.14.0)
|
88
|
+
rspec-expectations (~> 2.14.0)
|
89
|
+
rspec-mocks (~> 2.14.0)
|
90
|
+
rspec-core (2.14.8)
|
91
|
+
rspec-expectations (2.14.5)
|
92
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
93
|
+
rspec-mocks (2.14.6)
|
94
|
+
rutabaga (0.0.4)
|
95
|
+
turnip (>= 1.1.0)
|
96
|
+
simplecov (0.8.2)
|
97
|
+
docile (~> 1.1.0)
|
98
|
+
multi_json
|
99
|
+
simplecov-html (~> 0.8.0)
|
100
|
+
simplecov-html (0.8.0)
|
101
|
+
slop (3.4.7)
|
102
|
+
thread_safe (0.3.4)
|
103
|
+
timecop (0.4.4)
|
104
|
+
turnip (1.2.1)
|
105
|
+
gherkin (>= 2.5)
|
106
|
+
rspec (>= 2.0, < 4.0)
|
107
|
+
tzinfo (1.2.2)
|
108
|
+
thread_safe (~> 0.1)
|
109
|
+
yard (0.8.7.3)
|
110
|
+
|
111
|
+
PLATFORMS
|
112
|
+
ruby
|
113
|
+
|
114
|
+
DEPENDENCIES
|
115
|
+
codeclimate-test-reporter
|
116
|
+
pry-plus
|
117
|
+
rabbit_feed!
|
118
|
+
rake
|
119
|
+
rspec
|
120
|
+
rutabaga
|
121
|
+
timecop
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2013-2014 Simply Business
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
# RabbitFeed
|
2
|
+
|
3
|
+
![RabbitFeed logo](https://cloud.githubusercontent.com/assets/768254/3286432/b4e65e7e-f548-11e3-9c91-f7d04f489cf3.png)
|
4
|
+
|
5
|
+
A gem providing asynchronous event publish and subscribe capabilities with RabbitMQ.
|
6
|
+
|
7
|
+
## Core concepts
|
8
|
+
|
9
|
+
* Fire and forget: Application can publish an event and it has no knowledge/care of how that event is consumed.
|
10
|
+
* Persistent events: Once an event has been published, it will persist until it has been processed successfully.
|
11
|
+
* Self-describing events: The event not only contains a payload, but also a schema that describes that payload.
|
12
|
+
* Multiple subscribers: Multiple applications can subscribe to the same events.
|
13
|
+
* Event versioning: Consumers can customize event handling based on the event version.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
gem 'rabbit_feed', git: 'git@github.com:simplybusiness/rabbit_feed.git'
|
20
|
+
|
21
|
+
### Configuration
|
22
|
+
|
23
|
+
Create a `config/rabbit_feed.yml` file. The following options can be specified:
|
24
|
+
|
25
|
+
environment:
|
26
|
+
host: RabbitMQ host
|
27
|
+
user: RabbitMQ user name
|
28
|
+
password: RabbitMQ password
|
29
|
+
application: Application name
|
30
|
+
|
31
|
+
Sample:
|
32
|
+
|
33
|
+
development:
|
34
|
+
host: localhost
|
35
|
+
user: guest
|
36
|
+
password: guest
|
37
|
+
application: beavers
|
38
|
+
|
39
|
+
### Initialisation
|
40
|
+
|
41
|
+
If installing in a rails application, the following should be defined in `config/initializers/rabbit_feed.rb`:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
RabbitFeed.instance_eval do
|
45
|
+
self.log = Logger.new (Rails.root.join 'log', 'rabbit_feed.log')
|
46
|
+
self.environment = Rails.env
|
47
|
+
self.configuration_file_path = Rails.root.join 'config', 'rabbit_feed.yml'
|
48
|
+
end
|
49
|
+
# Define the events (if producing)
|
50
|
+
EventDefinitions do
|
51
|
+
define_event('user_creates_beaver', version: '1.0.0') do
|
52
|
+
defined_as do
|
53
|
+
'A beaver has been created'
|
54
|
+
end
|
55
|
+
payload_contains do
|
56
|
+
field('beaver_name', type: 'string', definition: 'The name of the beaver')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# Define the event routing (if consuming)
|
61
|
+
EventRouting do
|
62
|
+
accept_from('beavers') do
|
63
|
+
event('foo') do |event|
|
64
|
+
# Do something...
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
## Producing events
|
71
|
+
|
72
|
+
The producer defines the events and their payloads using the [Event Definitions DSL](https://github.com/simplybusiness/rabbit_feed#event-definitions-dsl). In a rails app, this can be defined in the [initialiser](https://github.com/simplybusiness/rabbit_feed#initialisation).
|
73
|
+
|
74
|
+
To produce an event:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
require 'rabbit_feed'
|
78
|
+
RabbitFeed::Producer.publish_event 'Event name', { 'payload_field' => 'payload field value' }
|
79
|
+
```
|
80
|
+
|
81
|
+
**Event name:** This tells you what the event is.
|
82
|
+
|
83
|
+
**Event payload:** This is the data about the event. This should be a hash.
|
84
|
+
|
85
|
+
The event will be published to the configured exchange on RabbitMQ (`amq.topic` by default) with a routing key having the pattern of: `[environment].[producer application name].[event name]`.
|
86
|
+
|
87
|
+
### Returned Events
|
88
|
+
|
89
|
+
In the case that there are no consumers configured to subscribe to an event, the event will be returned to the producer. The returned event will be logged, and if your project uses [Airbrake](https://airbrake.io), an error will be reported there.
|
90
|
+
|
91
|
+
### Testing the Producer
|
92
|
+
|
93
|
+
To prevent RabbitFeed from firing events during tests, add the following to `spec_helper.rb`:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
config.before :each do
|
97
|
+
RabbitFeed::Producer.stub!
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
#### RSpec
|
102
|
+
|
103
|
+
To verify that your application publishes an event, use the custom RSpec matcher provided with this application.
|
104
|
+
|
105
|
+
Add the following RSpec configuration to `spec_helper.rb`:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
RSpec.configure do |config|
|
109
|
+
config.include(RabbitFeed::TestingSupport::RSpecMatchers)
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
The expectation looks like this:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
require 'spec_helper'
|
117
|
+
|
118
|
+
describe BeaversController do
|
119
|
+
|
120
|
+
describe 'POST create' do
|
121
|
+
it 'publishes a create event' do
|
122
|
+
expect{
|
123
|
+
post :create, beaver: { name: 'beaver' }
|
124
|
+
}.to publish_event('user_creates_beaver', { 'beaver_name' => 'beaver' })
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
## Consuming events
|
131
|
+
|
132
|
+
The consumer defines to which events it will subscribe as well as how it handles events using the [Event Routing DSL](https://github.com/simplybusiness/rabbit_feed#event-routing-dsl). In a rails app, this can be defined in the [initialiser](https://github.com/simplybusiness/rabbit_feed#initialisation).
|
133
|
+
|
134
|
+
An `Event` contains the following information:
|
135
|
+
|
136
|
+
`environment` The environment in which the event was created (e.g. development, test, production)
|
137
|
+
`application` The name of the application that generated the event (as specified in rabbit_feed.yml)
|
138
|
+
`version` The version of the event
|
139
|
+
`name` The name of the event
|
140
|
+
`host` The hostname of the server on which the event was generated
|
141
|
+
`created_at_utc` The time (in UTC) that the event was created
|
142
|
+
`payload` The payload of the event
|
143
|
+
|
144
|
+
### Running the consumer
|
145
|
+
|
146
|
+
bundle exec rabbit_feed consume --environment development
|
147
|
+
|
148
|
+
More information about the consumer command line options can be found [here](https://github.com/simplybusiness/rabbit_feed#consumer).
|
149
|
+
|
150
|
+
### Event Processing Errors
|
151
|
+
|
152
|
+
In the case that your consumer raises an error whilst processing an event, the error will be logged. If your project uses [Airbrake](https://airbrake.io), the error will also be reported there. The event that was being processed will remain on the RabbitMQ queue, and will be redelivered to the consumer until it is processed without error.
|
153
|
+
|
154
|
+
### Testing the Consumer
|
155
|
+
|
156
|
+
If you want to test that your routes are behaving as expected without actually using RabbitMQ, you can include the module `TestHelpers` in your tests and then invoke `rabbit_feed_consumer.consume_event(event)`. Following is an example:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
describe 'consuming events' do
|
160
|
+
|
161
|
+
include RabbitFeed::TestingSupport::TestingHelpers
|
162
|
+
|
163
|
+
accumulator = []
|
164
|
+
|
165
|
+
let(:define_route) do
|
166
|
+
EventRouting do
|
167
|
+
accept_from('app') do
|
168
|
+
event('ev') do |event|
|
169
|
+
accumulator << event
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
let(:event) { {'application' => 'app', 'name' => 'ev', 'stuff' => 'some_stuff'} }
|
176
|
+
|
177
|
+
before { define_route }
|
178
|
+
|
179
|
+
it 'route to the correct service' do
|
180
|
+
rabbit_feed_consumer.consume_event(event)
|
181
|
+
expect(accumulator.size).to eq(1)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
## Command Line Tools
|
187
|
+
|
188
|
+
### Event Publish
|
189
|
+
|
190
|
+
bundle exec bin/rabbit_feed produce --payload 'Event payload' --name 'Event name' --environment test --config spec/fixtures/configuration.yml --logfile test.log --require rabbit_feed.rb --verbose
|
191
|
+
|
192
|
+
Publishes an event. Note: until you've specified the [event definitions](https://github.com/simplybusiness/rabbit_feed#event-definitions-dsl), this will not publish any events. Options are as follows:
|
193
|
+
|
194
|
+
--payload The payload of the event
|
195
|
+
--name The name of the event
|
196
|
+
--environment The environment to run in
|
197
|
+
--config The location of the rabbit_feed configuration file
|
198
|
+
--logfile The location of the log file
|
199
|
+
--require The project file containing the dependancies
|
200
|
+
--verbose Turns on DEBUG logging
|
201
|
+
--help Print the available options
|
202
|
+
|
203
|
+
### Consumer
|
204
|
+
|
205
|
+
bundle exec bin/rabbit_feed consume --environment test --config spec/fixtures/configuration.yml --logfile test.log --require rabbit_feed.rb --pidfile rabbit_feed.pid --verbose --daemon
|
206
|
+
|
207
|
+
Starts a consumer. Note: until you've specified the [event routing](https://github.com/simplybusiness/rabbit_feed#event-routing-dsl), this will not receive any events. Options are as follows:
|
208
|
+
|
209
|
+
--environment The environment to run in
|
210
|
+
--config The location of the rabbit_feed configuration file
|
211
|
+
--logfile The location of the log file
|
212
|
+
--require The project file containing the dependancies (only necessary if running with non-rails application)
|
213
|
+
--pidfile The location at which to write a pid file
|
214
|
+
--verbose Turns on DEBUG logging
|
215
|
+
--daemon Run the consumer as a daemon
|
216
|
+
--help Print the available options
|
217
|
+
|
218
|
+
## Event Definitions DSL
|
219
|
+
|
220
|
+
Provides a means to define all events that are published by an application. Defines the event names and the payload associated with each event. The DSL is converted into a schema that is serialized along with the event payload, meaning the events are self-describing. This is accomplished using Apache [Avro](http://avro.apache.org/docs/current/). This also validates the event payload against its schema before it is published.
|
221
|
+
|
222
|
+
Event definitions are cumulative, meaning you can load multiple `EventDefinitions` blocks.
|
223
|
+
|
224
|
+
Here is an example DSL:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
EventDefinitions do
|
228
|
+
define_event('user_creates_beaver', version: '1.0.0') do
|
229
|
+
defined_as do
|
230
|
+
'A beaver has been created'
|
231
|
+
end
|
232
|
+
payload_contains do
|
233
|
+
field('beaver_name', type: 'string', definition: 'The name of the beaver')
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
define_event('user_updates_beaver', version: '1.0.0') do
|
238
|
+
defined_as do
|
239
|
+
'A beaver has been updated'
|
240
|
+
end
|
241
|
+
payload_contains do
|
242
|
+
field('beaver_name', type: 'string', definition: 'The name of the beaver')
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
This defines two events:
|
249
|
+
|
250
|
+
1. `user_creates_beaver`
|
251
|
+
2. `user_updates_beaver`
|
252
|
+
|
253
|
+
Each event has a mandatory string field in its payload, called `beaver_name`.
|
254
|
+
|
255
|
+
The available field types are described [here](http://avro.apache.org/docs/current/spec.html#schema_primitive).
|
256
|
+
|
257
|
+
Publishing a `user_creates_beaver` event would look like this:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
RabbitFeed::Producer.publish_event 'user_creates_beaver', { 'beaver_name' => @beaver.name }
|
261
|
+
```
|
262
|
+
|
263
|
+
## Event Routing DSL
|
264
|
+
|
265
|
+
Provides a means for consumers to specify to which events it will subscribe as well as how it handles events. This is accomplished using a custom DSL backed by a RabbitMQ [topic](http://www.rabbitmq.com/tutorials/tutorial-five-ruby.html) exchange.
|
266
|
+
|
267
|
+
Event routing definitions are cumulative, meaning you can load multiple `EventRouting` blocks.
|
268
|
+
|
269
|
+
Here is an example DSL:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
EventRouting do
|
273
|
+
accept_from('beavers') do
|
274
|
+
event('user_created_beaver') do |event|
|
275
|
+
puts event.payload
|
276
|
+
end
|
277
|
+
event('user_updated_beaver') do |event|
|
278
|
+
puts event.payload
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
```
|
283
|
+
|
284
|
+
This will subscribe to specified events originating from the `beavers` application. We have specified that we would like to subcribe to `user_created_beaver` and `user_updated_beaver` events. If either event type is received, we have specified that its payload will be printed to the screen.
|
285
|
+
|
286
|
+
When the consumer is started, it will create its queue named using this pattern: `[environment].[consumer application name]`. It will bind the queue to the `amq.topic` exchange on the routing keys as defined in the event routing. In this example, it will bind on:
|
287
|
+
|
288
|
+
environment.beavers.user_created_beaver
|
289
|
+
environment.beavers.user_updated_beaver
|
290
|
+
|
291
|
+
_Note: The consumer queues will automatically expire (delete) after 7 days without any consumer connections. This is to prevent unused queues from hanging around once their associated consumer has been terminated._
|
292
|
+
|
293
|
+
## Delivery Semantics
|
294
|
+
|
295
|
+
RabbitFeed provides 'at least once' delivery semantics. There are two use-cases where an event may be delivered more than once:
|
296
|
+
|
297
|
+
1. If the subscriber raises an exception whilst processing an event, RabbitFeed will re-deliver the event to the subscriber until the event is processed without error.
|
298
|
+
1. If an event is pushed to the subscriber, and the subscriber loses connectivity with RabbitMQ before it can send an acknowledgement back to RabbitMQ, RabbitMQ will push the event again once connectivity has been restored.
|
299
|
+
|
300
|
+
It is advisable to run RabbitFeed in a [clustered](https://www.rabbitmq.com/clustering.html) RabbitMQ environment to prevent the loss of messages in the case that a RabbitMQ node is lost. By default, RabbitFeed will declare queues to be mirrored across all nodes of the cluster.
|
301
|
+
|
302
|
+
## Developing
|
303
|
+
|
304
|
+
_See [./DEVELOPING.md](./DEVELOPING.md) for instructions on how to develop RabbitFeed_
|