bunny 0.9.0.pre3 → 0.9.0.pre4
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.
- 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
|