bunny 0.9.0.pre3 → 0.9.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/ChangeLog.md +56 -1
- data/README.md +96 -30
- data/bunny.gemspec +1 -1
- data/examples/connection/heartbeat.rb +3 -3
- data/examples/connection/unknown_host.rb +2 -2
- data/examples/guides/getting_started/hello_world.rb +1 -2
- data/examples/guides/queues/redeliveries.rb +79 -0
- data/lib/bunny/authentication/credentials_encoder.rb +37 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +26 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +17 -0
- data/lib/bunny/channel.rb +44 -22
- data/lib/bunny/consumer.rb +5 -1
- data/lib/bunny/delivery_info.rb +3 -2
- data/lib/bunny/exceptions.rb +7 -1
- data/lib/bunny/exchange.rb +6 -3
- data/lib/bunny/heartbeat_sender.rb +7 -3
- data/lib/bunny/queue.rb +16 -3
- data/lib/bunny/session.rb +28 -14
- data/lib/bunny/transport.rb +8 -1
- data/lib/bunny/version.rb +1 -1
- data/spec/higher_level_api/integration/basic_cancel_spec.rb +43 -0
- data/spec/higher_level_api/integration/connection_spec.rb +2 -7
- data/spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb +1 -1
- data/spec/higher_level_api/integration/exchange_declare_spec.rb +21 -6
- data/spec/higher_level_api/integration/exchange_delete_spec.rb +47 -2
- data/spec/higher_level_api/integration/message_properties_access_spec.rb +95 -0
- data/spec/higher_level_api/integration/publisher_confirms_spec.rb +4 -3
- data/spec/issues/issue78_spec.rb +73 -0
- data/spec/lower_level_api/integration/basic_cancel_spec.rb +6 -1
- metadata +177 -160
data/.gitignore
CHANGED
data/ChangeLog.md
CHANGED
@@ -1,6 +1,61 @@
|
|
1
1
|
## Changes between Bunny 0.9.0.pre3 and 0.9.0.pre4
|
2
2
|
|
3
|
-
|
3
|
+
### Heartbeats Support Fixes
|
4
|
+
|
5
|
+
Heartbeats are now correctly sent at safe intervals (half of the configured
|
6
|
+
interval). In addition, setting `:heartbeat => 0` (or `nil`) will disable
|
7
|
+
heartbeats, just like in Bunny 0.8 and [amqp gem](http://rubyamqp.info).
|
8
|
+
|
9
|
+
Default `:heartbeat` value is now `600` (seconds), the same as RabbitMQ 3.0
|
10
|
+
default.
|
11
|
+
|
12
|
+
|
13
|
+
### Eliminate Race Conditions When Registering Consumers
|
14
|
+
|
15
|
+
Fixes a potential race condition between `basic.consume-ok` handler and
|
16
|
+
delivery handler when a consumer is registered for a queue that has
|
17
|
+
messages in it.
|
18
|
+
|
19
|
+
GH issue: #78.
|
20
|
+
|
21
|
+
### Support for Alternative Authentication Mechanisms
|
22
|
+
|
23
|
+
Bunny now supports two authentication mechanisms and can be extended
|
24
|
+
to support more. The supported methods are `"PLAIN"` (username
|
25
|
+
and password) and `"EXTERNAL"` (typically uses TLS, UNIX sockets or
|
26
|
+
another mechanism that does not rely on username/challenge pairs).
|
27
|
+
|
28
|
+
To use the `"EXTERNAL"` method, pass `:auth_mechanism => "EXTERNAL"` to
|
29
|
+
`Bunny.new`:
|
30
|
+
|
31
|
+
``` ruby
|
32
|
+
# uses the EXTERNAL authentication mechanism
|
33
|
+
conn = Bunny.new(:auth_method => "EXTERNAL")
|
34
|
+
conn.start
|
35
|
+
```
|
36
|
+
|
37
|
+
### Bunny::Consumer#cancel
|
38
|
+
|
39
|
+
A new high-level API method: `Bunny::Consumer#cancel`, can be used to
|
40
|
+
cancel a consumer. `Bunny::Queue#subscribe` will now return consumer
|
41
|
+
instances when the `:block` option is passed in as `false`.
|
42
|
+
|
43
|
+
|
44
|
+
### Bunny::Exchange#delete Behavior Change
|
45
|
+
|
46
|
+
`Bunny::Exchange#delete` will no longer delete pre-declared exchanges
|
47
|
+
that cannot be declared by Bunny (`amq.*` and the default exchange).
|
48
|
+
|
49
|
+
|
50
|
+
### Bunny::DeliveryInfo#redelivered?
|
51
|
+
|
52
|
+
`Bunny::DeliveryInfo#redelivered?` is a new method that is an alias
|
53
|
+
to `Bunny::DeliveryInfo#redelivered` but follows the Ruby community convention
|
54
|
+
about predicate method names.
|
55
|
+
|
56
|
+
### Corrected Bunny::DeliveryInfo#delivery_tag Name
|
57
|
+
|
58
|
+
`Bunny::DeliveryInfo#delivery_tag` had a typo which is now fixed.
|
4
59
|
|
5
60
|
|
6
61
|
## Changes between Bunny 0.9.0.pre2 and 0.9.0.pre3
|
data/README.md
CHANGED
@@ -1,79 +1,145 @@
|
|
1
1
|
# About Bunny
|
2
2
|
|
3
|
-
Bunny is a synchronous RabbitMQ client that focuses on ease of use.
|
3
|
+
Bunny is a synchronous RabbitMQ client that focuses on ease of use. With the next
|
4
|
+
0.9 release (currently in master), it is feature complete, supports all RabbitMQ 3.0
|
5
|
+
features and is free of many limitations of earlier versions.
|
4
6
|
|
5
7
|
|
6
8
|
## Supported Ruby Versions
|
7
9
|
|
8
|
-
|
10
|
+
Bunny 0.9 and more recent versions support Ruby 1.9.3, 1.9.2, JRuby 1.7, Rubinius 2.0 and 1.8.7.
|
9
11
|
|
10
12
|
|
11
13
|
## Supported RabbitMQ Versions
|
12
14
|
|
13
|
-
Bunny
|
14
|
-
supports RabbitMQ 2.x.
|
15
|
+
Bunny `0.8.x` and later versions only support RabbitMQ 2.x and 3.x.
|
15
16
|
|
17
|
+
Bunny versions `0.7.x` and earlier support RabbitMQ 1.x and 2.x.
|
16
18
|
|
17
|
-
|
19
|
+
|
20
|
+
## Changes in Bunny 0.9
|
18
21
|
|
19
22
|
Bunny is a very old library with **a lot** of missing functionality. It also implements an older version of the spec
|
20
|
-
and may or may not work with future RabbitMQ versions. As such, Bunny
|
21
|
-
We
|
23
|
+
and may or may not work with future RabbitMQ versions. As such, Bunny needed serious internal changes.
|
24
|
+
We (the maintainers) make our best to keep the new version as backwards compatible as possible but within reason.
|
22
25
|
|
23
26
|
See [this announcement](https://groups.google.com/forum/?fromgroups#!topic/ruby-amqp/crNVGEuHm68) to learn more.
|
24
27
|
|
25
|
-
In the meantime, consider using [Hot Bunnies](http://github.com/ruby-amqp/hot_bunnies) (JRuby-only) or [amqp gem](http://rubyamqp.info) instead.
|
26
28
|
|
29
|
+
## Installation & Bundler Dependency
|
30
|
+
|
31
|
+
To install Bunny 0.9.x with RubyGems:
|
32
|
+
|
33
|
+
```
|
34
|
+
gem install bunny --pre
|
35
|
+
```
|
27
36
|
|
28
|
-
|
37
|
+
To use Bunny 0.9.x in a project managed with Bundler:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
gem "bunny", ">= 0.9.0.pre3" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
|
41
|
+
```
|
42
|
+
|
43
|
+
|
44
|
+
## Quick Start for Bunny 0.9.x
|
45
|
+
|
46
|
+
Below is a small snippet that demonstrates how to publish
|
47
|
+
and synchronously consume ("pull API") messages with Bunny 0.9.
|
48
|
+
|
49
|
+
For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html).
|
29
50
|
|
30
51
|
``` ruby
|
31
52
|
require "bunny"
|
32
53
|
|
33
|
-
|
54
|
+
# Start a communication session with RabbitMQ
|
55
|
+
conn = Bunny.new
|
56
|
+
conn.start
|
34
57
|
|
35
|
-
#
|
36
|
-
|
58
|
+
# open a channel
|
59
|
+
ch = conn.create_channel
|
37
60
|
|
38
61
|
# declare a queue
|
39
|
-
q
|
62
|
+
q = ch.queue("test1")
|
40
63
|
|
41
64
|
# declare default direct exchange which is bound to all queues
|
42
|
-
e
|
65
|
+
e = ch.default_exchange
|
43
66
|
|
44
67
|
# publish a message to the exchange which then gets routed to the queue
|
45
|
-
e.publish("Hello, everybody!", :
|
68
|
+
e.publish("Hello, everybody!", :routing_key => 'test1')
|
46
69
|
|
47
|
-
#
|
48
|
-
|
70
|
+
# fetch a message from the queue
|
71
|
+
delivery_info, metadata, payload = q.pop
|
49
72
|
|
50
|
-
puts "This is the message: "
|
73
|
+
puts "This is the message: #{payload}"
|
51
74
|
|
52
75
|
# close the connection
|
53
|
-
|
76
|
+
conn.stop
|
54
77
|
```
|
55
78
|
|
56
|
-
... or just:
|
57
79
|
|
58
|
-
|
59
|
-
require "bunny"
|
80
|
+
## Community & Getting Help
|
60
81
|
|
61
|
-
|
62
|
-
Bunny.run { |c| c.queue("my_testq") }
|
63
|
-
```
|
82
|
+
### Getting Started
|
64
83
|
|
65
|
-
|
84
|
+
For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html).
|
85
|
+
|
86
|
+
### Guides
|
87
|
+
|
88
|
+
Other documentation guides are available at [rubybunny.info](http://rubybunny.info).
|
89
|
+
|
90
|
+
### API Reference
|
91
|
+
|
92
|
+
[Bunny API Reference](http://rubydoc.info/github/ruby-amqp/bunny/master/frames) is available at rubydoc.info.
|
93
|
+
|
94
|
+
## Getting Help
|
95
|
+
|
96
|
+
### Mailing List
|
97
|
+
|
98
|
+
[Bunny a mailing list](groups.google.com/group/ruby-amqp). We encourage you
|
99
|
+
to also join the [rabbitmq-discuss](https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss) mailing list. Feel free to ask any questions that you may have.
|
100
|
+
|
101
|
+
|
102
|
+
### IRC
|
103
|
+
|
104
|
+
For more immediate help, please join `#rabbitmq` on `irc.freenode.net`.
|
105
|
+
|
106
|
+
|
107
|
+
### News & Announcements on Twitter
|
108
|
+
|
109
|
+
To subscribe for announcements of releases, important changes and so on, please follow [@rubyamqp](https://twitter.com/#!/rubyamqp) on Twitter.
|
110
|
+
|
111
|
+
|
112
|
+
### Reporting Issues
|
113
|
+
|
114
|
+
If you find a bug, poor default, missing feature or find any part of the API inconvenient, please [file an issue](http://github.com/ruby-amqp/bunny/issues) on GitHub.
|
115
|
+
When filing an issue, please specify which Bunny and RabbitMQ versions you are using, provide recent RabbitMQ log file contents if possible,
|
116
|
+
and try to explain what behavior you expected and why. Bonus points for contributing failing test cases.
|
117
|
+
|
118
|
+
|
119
|
+
## Other Ruby RabbitMQ Clients
|
120
|
+
|
121
|
+
Other widely used Ruby RabbitMQ clients are [Hot Bunnies](http://github.com/ruby-amqp/hot_bunnies) (JRuby-only) and [amqp gem](http://rubyamqp.info).
|
122
|
+
Both are mature libraries and require RabbitMQ 2.x or 3.x.
|
123
|
+
|
124
|
+
|
125
|
+
## Contributing
|
126
|
+
|
127
|
+
First, clone the repository and run
|
128
|
+
|
129
|
+
bundle install --binstubs
|
130
|
+
|
131
|
+
and then run tests with
|
66
132
|
|
67
|
-
|
68
|
-
have.
|
133
|
+
./bin/rspec -cfs spec
|
69
134
|
|
70
|
-
|
135
|
+
After that create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
|
136
|
+
on GitHub.
|
71
137
|
|
72
138
|
|
73
139
|
|
74
140
|
## Other Resources
|
75
141
|
|
76
|
-
* [AMQP 0.9.1 model explained](): introductory explanation of the AMQP v0.9.1 specification with particular reference to RabbitMQ.
|
142
|
+
* [AMQP 0.9.1 model explained](http://www.rabbitmq.com/tutorials/amqp-concepts.html): introductory explanation of the AMQP v0.9.1 specification with particular reference to RabbitMQ.
|
77
143
|
|
78
144
|
|
79
145
|
## Links
|
data/bunny.gemspec
CHANGED
@@ -9,9 +9,9 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
|
|
9
9
|
require 'bunny'
|
10
10
|
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
conn = Bunny.new(:heartbeat_interval => 2)
|
13
|
+
conn.start
|
14
14
|
|
15
|
-
c =
|
15
|
+
c = conn.create_channel
|
16
16
|
|
17
17
|
sleep 10
|
@@ -9,13 +9,12 @@ conn.start
|
|
9
9
|
|
10
10
|
ch = conn.create_channel
|
11
11
|
q = ch.queue("bunny.examples.hello_world", :auto_delete => true)
|
12
|
-
x = ch.default_exchange
|
13
12
|
|
14
13
|
q.subscribe do |delivery_info, properties, payload|
|
15
14
|
puts "Received #{payload}"
|
16
15
|
end
|
17
16
|
|
18
|
-
|
17
|
+
q.publish("Hello!", :routing_key => q.name)
|
19
18
|
|
20
19
|
sleep 1.0
|
21
20
|
conn.close
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
|
7
|
+
puts "=> Subscribing for messages using explicit acknowledgements model"
|
8
|
+
puts
|
9
|
+
|
10
|
+
connection1 = Bunny.new
|
11
|
+
connection1.start
|
12
|
+
|
13
|
+
connection2 = Bunny.new
|
14
|
+
connection2.start
|
15
|
+
|
16
|
+
connection3 = Bunny.new
|
17
|
+
connection3.start
|
18
|
+
|
19
|
+
ch1 = connection1.create_channel
|
20
|
+
ch1.prefetch(1)
|
21
|
+
|
22
|
+
ch2 = connection2.create_channel
|
23
|
+
ch2.prefetch(1)
|
24
|
+
|
25
|
+
ch3 = connection3.create_channel
|
26
|
+
ch3.prefetch(1)
|
27
|
+
|
28
|
+
x = ch3.direct("amq.direct")
|
29
|
+
q1 = ch1.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
|
30
|
+
q1.purge
|
31
|
+
|
32
|
+
q1.bind(x).subscribe(:ack => true, :block => false) do |delivery_info, properties, payload|
|
33
|
+
# do some work
|
34
|
+
sleep(0.2)
|
35
|
+
|
36
|
+
# acknowledge some messages, they will be removed from the queue
|
37
|
+
if rand > 0.5
|
38
|
+
# FYI: there is a shortcut, Bunny::Channel.ack
|
39
|
+
ch1.acknowledge(delivery_info.delivery_tag, false)
|
40
|
+
puts "[consumer1] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed"
|
41
|
+
else
|
42
|
+
# some messages are not ack-ed and will remain in the queue for redelivery
|
43
|
+
# when app #1 connection is closed (either properly or due to a crash)
|
44
|
+
puts "[consumer1] Got message ##{properties.headers['i']}, SKIPPPED"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
q2 = ch2.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
|
49
|
+
q2.bind(x).subscribe(:ack => true, :block => false) do |delivery_info, properties, payload|
|
50
|
+
# do some work
|
51
|
+
sleep(0.2)
|
52
|
+
|
53
|
+
ch2.acknowledge(delivery_info.delivery_tag, false)
|
54
|
+
puts "[consumer2] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed"
|
55
|
+
end
|
56
|
+
|
57
|
+
t1 = Thread.new do
|
58
|
+
i = 0
|
59
|
+
loop do
|
60
|
+
sleep 0.5
|
61
|
+
|
62
|
+
x.publish("Message ##{i}", :headers => { :i => i })
|
63
|
+
i += 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
t1.abort_on_exception = true
|
67
|
+
|
68
|
+
t2 = Thread.new do
|
69
|
+
sleep 4.0
|
70
|
+
|
71
|
+
connection1.close
|
72
|
+
puts "----- Connection 1 is now closed (we pretend that it has crashed) -----"
|
73
|
+
end
|
74
|
+
t2.abort_on_exception = true
|
75
|
+
|
76
|
+
|
77
|
+
sleep 7.0
|
78
|
+
connection2.close
|
79
|
+
connection3.close
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Bunny
|
2
|
+
module Authentication
|
3
|
+
class CredentialsEncoder
|
4
|
+
|
5
|
+
#
|
6
|
+
# API
|
7
|
+
#
|
8
|
+
|
9
|
+
attr_reader :session
|
10
|
+
|
11
|
+
def self.for_session(session)
|
12
|
+
registry[session.mechanism].new(session)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.registry
|
16
|
+
@@registry ||= Hash.new { raise NotImplementedError }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.auth_mechanism(*mechanisms)
|
20
|
+
mechanisms.each do |m|
|
21
|
+
registry[m] = self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def encode_credentials(username, challenge)
|
26
|
+
raise NotImplementedError.new("Subclasses must override this method")
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def initialize(session)
|
32
|
+
@session = session
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "bunny/authentication/credentials_encoder"
|
2
|
+
|
3
|
+
module Bunny
|
4
|
+
module Authentication
|
5
|
+
class ExternalMechanismEncoder < CredentialsEncoder
|
6
|
+
|
7
|
+
auth_mechanism "EXTERNAL", "external"
|
8
|
+
|
9
|
+
# Encodes a username and password for the EXTERNAL mechanism. Since
|
10
|
+
# authentication is handled by an encapsulating protocol like SSL or
|
11
|
+
# UNIX domain sockets, EXTERNAL doesn't pass along any username or
|
12
|
+
# password information at all and this method always returns the
|
13
|
+
# empty string.
|
14
|
+
#
|
15
|
+
# @param [String] username The username to encode. This parameter is
|
16
|
+
# ignored.
|
17
|
+
# @param [String] password The password to encode. This parameter is
|
18
|
+
# ignored.
|
19
|
+
# @return [String] The username and password, encoded for the
|
20
|
+
# EXTERNAL mechanism. This is always the empty string.
|
21
|
+
def encode_credentials(username, password)
|
22
|
+
""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bunny/authentication/credentials_encoder"
|
2
|
+
|
3
|
+
module Bunny
|
4
|
+
module Authentication
|
5
|
+
class PlainMechanismEncoder < CredentialsEncoder
|
6
|
+
|
7
|
+
auth_mechanism "PLAIN", "plain"
|
8
|
+
|
9
|
+
# @api public
|
10
|
+
# @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
|
11
|
+
def encode_credentials(username, password)
|
12
|
+
"\0#{username}\0#{password}"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|