Gem for happn
Happn connects a RabbitMQ exchange and listens for CREPE events (possibly generated using flu-rails) sequentially.
Happn helps developers to create "Projectors" that define how to match and consume events.
This gem connects a single RabbitMQ queue and bind it automatically to its exchange. These bindings are defined by developers through "matchers" when loading projectors.
Requirements
- Ruby 2.2
- Tested with RabbitMQ 3.5.8
happn-rubyworks with or without Rails (tested with Rails 4 and 5).
Installation
Add the gem to your project's Gemfile:
```ruby
gem "happn", git: "https://github.com/crepesourcing/happn-ruby.git"
```
Then, configure Happn. If you use Rails, you can create an initializer into your Rails app (config/initializers/happn.rb). This code can be called anywhere before starting Happn.init.
Happn.configure do |config|
config.logger = Rails.logger
config.rabbitmq_host = ENV["RABBITMQ_HOST"]
config.rabbitmq_port = ENV["RABBITMQ_PORT"]
config.rabbitmq_management_port = ENV["RABBITMQ_MANAGEMENT_PORT"]
config.rabbitmq_user = ENV["RABBITMQ_USER"]
config.rabbitmq_password = ENV["RABBITMQ_PASSWORD"]
config.rabbitmq_queue_name = ENV["CONSUMER_QUEUE_NAME"]
config.rabbitmq_exchange_name = ENV["RABBITMQ_EXCHANGE_NAME"]
config.rabbitmq_exchange_durable = ENV["RABBITMQ_EXCHANGE_DURABLE"] == "true"
config.projector_classes = [LoggerProjector]
config. = {}
end
Each configuration is detailed below.
About queues
Happnconsumes a single queue through the RabbitMQ's Topic Exchange Model.- If the queue does not exist when
Happnstarts, it is created automatically. - When connecting a queue, please be careful that each connection parameter must match the existing queue's parameters. For instance, the value of
x-queue-modemust match to avoid aPRECONDITION FAILEDerror. - All bindings between queues and their exchange are reset when starting
Happn. Based on all the projectors that have been registered (optionprojector_classes),Happndetects which events must be consumed and binds its queue to the exchange depending on these event matchers.
About projectors
A projector:
- defines one or multiple matchers to detect which events must be consumed. Matchers can be declared using 4 event properties
emitter: e.g.FacebookorMyInternalApi(:allor no value mean "all emitters")kind: e.g.entity_changeorkind(:allor no value mean "all kinds")name: e.g.create countryorrequest to destroy bunnies(:allor no value mean "all names")status: e.g.:newor:replayed(:allor no value mean "all statuses")
- defines how to consume these events.
When a projector raises an Error, Happn stops.
Usage
Start Up
Starting Happn consumes events sequentially. For instance, it can be started from a Rake tasks:
```ruby
namespace :events do
desc "Listen all events and consume them."
task consume: :environment do
Happn.init
Happn.start
end
end
```
Define a Projector
A projector is a class that defines how to consume one or multiple types of events. This class must:
- extend
Happn::Projector. - use its
onmethod to declare which events to match and how to consume them. This must be done in adefine_handlersmethod.
class LoggerProjector < Happn::Projector
def define_handlers
on emitter: "MyApplication", name: "create country" do |event|
Rails.logger("A country has been created and generated an event with id #{event.id}")
end
on kind: "request", status: :new do |event|
Rails.logger("This is a new request to the controller: #{event.data["controller_name"]}")
end
end
end
Registering all projectors automatically
If all subclass of Happn::Projector should be registered seamlessly, the configuration can declare the following code (using Rails):
Happn.configure do |config|
Rails.application.eager_load!
config.projector_classes = Happn::Projector.descendants
end
When Zeitwerk is defined and when Rails.application.eager_load! returns false, you should call Zeitwerk::Loader.eager_load_all.
Overall configuration options
All options have a default value. However, all of them can be changed in your Happn.configure block.
| Option | Default Value | Type | Required? | Description | Example | ||
|---|---|---|---|---|---|---|---|
logger |
Logger.new(STDOUT) |
Logger | Optional | The logger used by happn |
Rails.logger |
||
rabbitmq_host |
"localhost" |
String | Required | RabbitMQ exchange's host. | "192.168.42.42" |
||
rabbitmq_port |
5672 |
Integer | Required | RabbitMQ exchange's port. | 1234 |
||
rabbitmq_user |
"" |
String | Required | RabbitMQ exchange's username. | "root" |
||
rabbitmq_password |
"" |
String | Required | RabbitMQ exchange's password. | "pouet" |
||
rabbitmq_exchange_name |
"events" |
String | Required | RabbitMQ exchange's name. | "myproject" |
||
rabbitmq_management_scheme |
"http" |
String | Required | RabbitMQ exchange's management scheme. This scheme is used when happn must access metadata information about queues, messages, etc. This port is used to create/delete bindings between the queue and its exchange. |
"https" |
||
rabbitmq_management_port |
15672 |
Integer | Required | RabbitMQ exchange's management port. This port is used when happn must access metadata information about queues, messages, etc. This port is used to create/delete bindings between the queue and its exchange. |
15671 |
||
rabbitmq_queue_name |
"happn-queue" |
String | Required | The RabbitMQ queue to create, bind and consume. If the queue does not exist, it will be created at startup. | "my-queue" |
||
rabbitmq_exchange_durable |
true |
Boolean | Optional | Make the RabbitMQ's exchange durable or not. From RabbitMQ's documentation: "Durable exchanges survive broker restart whereas transient exchanges do not (they have to be redeclared when broker comes back online)." | false |
||
rabbitmq_queue_mode |
nil |
String | Optional | When creating the queue, this option can be passed to set x-queue-mode. For instance, a queue can be made "lazy" by passing "lazy" as a value. See RabbitMQ's documentation for more details. |
lazy |
||
rabbitmq_prefetch_size |
10 |
Integer | Optional | Also known as RabbitMQ's QOS. From the RabbitMQ's documentation: "AMQP specifies the basic.qos method to allow you to limit the number of unacknowledged messages on a channel (or connection) when consuming (aka "prefetch count")." | 1000 |
||
projector_classes |
[] |
Array of constants | Required | All Projector classes to register. This value can be generated by reading all descendant classes from Happn::Projector. |
[MyProjector] |
||
bunny_options |
{} |
Hash of symbols | Optional | Additional options to add when connecting the RabbitMQ broker. This overrides the existing options with the same name. | { verify_peer: true } |
||
management_options |
{} |
Hash of symbols | Optional | Additional options to add when accessing the RabbitMQ Managmement. This overrides the existing options with the same name. The options are defined at https://github.com/ruby-amqp/rabbitmq_http_api_client | { verify: false } |
||
on_error |
nil |
block with an argument exception |
false | When the consumption of an event raises an Error, the consumption exits. However, this block can be called before exiting the consumption execution. | `lambda { | exception | Raven.capture_exception(exception) }` (see Sentry's documentation |