Tackle
Tackles the problem of processing asynchronous jobs in reliable manner by relying on RabbitMQ.
You should also take a look at Elixir Tackle.
Why should I use tackle?
- It is ideal for fast microservice prototyping
- It uses sane defaults for queue and exchange creation
- It retries messages that fail to be processed
- It stores unprocessed messages into a dead queue for later inspection
Installation
Add this line to your application's Gemfile:
gem "rt-tackle", :require => "tackle"
Publishing messages
With tackle, you can publish a message to an AMQP exchange. For example, to
publish "Hello World!"
do the following:
= {
:url => "amqp://localhost",
:exchange => "test-exchange",
:routing_key => "test-messages",
}
Tackle.publish("Hello World!", )
Optionally, you can pass a dedicated logger to the publish method. This comes handy if you want to log the status of your publish action to a file.
= {
:url => "amqp://localhost",
:exchange => "test-exchange",
:routing_key => "test-messages",
:logger => Logger.new("publish.log")
}
Tackle.publish("Hello World!", )
Consume messages
Tackle enables you to connect to an AMQP exchange and consume messages from it.
require "tackle"
= {
:url => "amqp://localhost",
:exchange => "users",
:routing_key => "signed-up",
:service => "user-mailer",
:exception_handler => lambda { |ex, consumer| puts ex. }
}
Tackle.consume() do ||
puts
end
The above code snippet creates the following AMQP resources:
- A dedicated exchange for your service, in this example
user-mailer.signed-up
- Connects your dedicated
user-mailer.signed-up
exchange to the remote exchange from which you want to consume messages, in this exampleusers
exchange - Creates an AMQP queue
user-mailer.signed-up
and connects it to your local exchange - Creates a delay queue
user-mailer.signed-up.delay
. If your service raises an exception while processing an incoming message, tackle will put it in this this queue, wait for a while, and then republish to theuser-mailer.signed-up
exchange. - Creates a dead queue
user-mailer.signed-up.dead
. After several retries where your service can't consume the message, tackle will store them in a dedicated dead queue. You can consume this messages manually.
You can pass additional configuration to tackle in order to control the number of retries, and the delay between each retry.
require "tackle"
options = {
:url => "amqp://localhost",
:exchange => "users",
:routing_key => "signed-up"
:service => "user-mailer",
:retry_limit => 8,
:retry_delay => 30,
:exception_handler => lambda { |ex, consumer| puts ex.message }
}
Tackle.consume(options) do |message|
puts message
end
By default, tackle logs helpful information to the STDOUT
. To redirect these
messages to a file, pass a dedicated logger to tackle.
require "tackle"
options = {
:url => "amqp://localhost",
:exchange => "users",
:routing_key => "signed-up"
:service => "user-mailer",
:retry_limit => 8,
:retry_delay => 30,
:logger => Logger.new("consumer.log"),
:exception_handler => lambda { |ex, consumer| puts ex.message }
}
Tackle.consume(options) do |message|
puts message
end
Manually ack-ing messages
By default, Tackle assumes that a message was successfully processed if no exceptions were raised in the consume block. This behaviour is well suited for handling unknown exceptions.
Sometimes however, we want to send a message to the retry queue without raising an exception (and polluting our exception tracker with false positives).
For this purpose, tackle can be configured to consume messages in a "manual ack"
fashion. Pass :manual_ack => true
to the consumer to activate the manual_ack
mode.
require "tackle"
options = {
:url => "amqp://localhost",
:exchange => "users",
:routing_key => "signed-up",
:service => "user-mailer"
:manual_ack => true
}
Tackle.consume(options) do |message|
puts message
Tackle::ACK
end
When Tackle consumes messages in the manual_ack mode, the return value of the
consumer block must be either Tackle::ACK
or Tackle::NACK
. In case the
response is Tackle::NACK
the message is put on the retry queue.
require "tackle"
= {
:url => "amqp://localhost",
:exchange => "numbers",
:routing_key => "positive-numbers",
:service => "number-processor",
:manual_ack => true
}
Tackle.consume() do ||
# accept only positive numbers
if ["value"].even?
Tackle::ACK
else
Tackle::NACK
end
end
If neither Tackle::ACK nor Tackle::NACK are returned, tackle assumes that the response is negative.
Test
To better performance in unit test put a instance mock of Bunny class in a connection key at options, for example, you could use a gem bunny-mock.
options = {
:url => "amqp://localhost",
...
:connection => BunnyMock.new
...
}
Development
After checking out the repo, run bin/setup
to install dependencies. Then,
run rake rspec
to run the tests. You can also run bin/console
for an
interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
.
To release a new version, update the version number in version.rb
, and
then run bundle exec rake release
, which will create a git tag for the
version, push git commits and tags, and push the .gem
file
to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/renderedtext/tackle.