https://github.com/cloud8421/rbt
Opinionated, high level RabbitMQ client wrapper
https://github.com/cloud8421/rbt
amqp background-processing elixir elixir-lang job-processor rabbitmq rabbitmq-client rabbitmq-consumer
Last synced: 10 months ago
JSON representation
Opinionated, high level RabbitMQ client wrapper
- Host: GitHub
- URL: https://github.com/cloud8421/rbt
- Owner: cloud8421
- License: mit
- Created: 2018-07-01T14:02:56.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-10-05T08:58:16.000Z (over 7 years ago)
- Last Synced: 2025-03-02T13:26:25.534Z (about 1 year ago)
- Topics: amqp, background-processing, elixir, elixir-lang, job-processor, rabbitmq, rabbitmq-client, rabbitmq-consumer
- Language: Elixir
- Size: 167 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Rbt
[](https://travis-ci.org/cloud8421/rbt)
[](https://coveralls.io/github/cloud8421/rbt?branch=master)
ALPHA stage, usable with some extra care.
## Guidelines
- Opt-in, self-configuring topology
- Small, explicit and composable building blocks
- Configuration only for compile-time variables (e.g. which JSON decoder to use)
- Always pass down configuration from the top, e.g. application -> supervisor -> single worker
- Always pass options explicitly at start
- Instrumentable
- Smallest possible dependency surface (make as many as possible optional)
- Support multiple mimetypes
- Don't hide APIs, rather provide ways to compose them
- Introspection to see running components at any given time
## Features
- [x] Consumers
- [x] auto retries with backoff
- [x] forward failures to separate exchange for capture
- [x] parallelized, bounded message handling
- [x] instrumentation hooks
- [x] manual consume/cancel control
- [x] multiple content types (erlang, json)
- [x] Producers
- [x] internal buffering in case of disconnection
- [x] auto-fingerprint of published messages with generated uuid
- [x] instrumentation hooks
- [x] multiple content types (erlang, json)
- [x] test helpers
- [x] RPC server
- [x] RPC client
- [x] Runtime topology information
- [ ] Complete docs
- [x] Installation and configuration
- [x] Basic supervision tree setup
- [ ] Content types
- [ ] Working with consumers
- [ ] Working with producers
- [ ] Permanent failure handling
- [ ] Manual consume/cancel
- [x] Testing a consumer
- [ ] Testing a producer
- [ ] Instrumentation
- [ ] Correlation IDs
- [ ] Custom topologies
- [ ] Manage bindings
- [ ] RPC servers
- [ ] RPC clients
## Installation
The package can be installed by adding `rbt` to your list of dependencies in `mix.exs` with the custom github url:
```elixir
def deps do
[
{:rbt, github: "cloud8421/rbt", tag: "v0.3.0"},
{:jason, "~> 1.1"}, # optional, but needed to support json-encoded messages
]
end
```
## Global configuration
As one of the goals of the library is remove hidden configuration as much as possible, it only supports a limited set of options that
influence compilation. For example:
```elixir
config :rbt,
json_adapter: Jason # default, can be omitted
```
Options:
- `json_adapter` (defaults to `Jason`): which adapter to use to encode and decode json data. See the docs for `Rbt.Data` for more details,
but in general the adapter has to expose `decode/1` and `encode/1` functions. In both instances, the expected return values are either `{:ok, result}` or `{:error, reason}`.
## Usage
RBT components can be composed via supervision trees. Here's a fairly extended example that starts two connections (one for consumers, one for producers), a producer and a consumer.
```elixir
defmodule ExampleSupervisor do
use Supervisor
def start_link(vhost_url) do
Supervisor.start_link(__MODULE__, vhost_url)
end
def init(opts) do
vhost_url = Keyword.fetch!(opts, :vhost_url)
children = [
{Rbt.Conn, uri: vhost_url, name: :prod_conn},
{Rbt.Conn, uri: vhost_url, name: :cons_conn},
{Rbt.Producer,
conn_ref: :prod_conn,
definitions: %{exchange_name: "test-exchange"},
create_infrastructure: true},
{Rbt.Consumer,
conn_ref: :cons_conn,
handler: MyHandler,
definitions: %{
exchange_name: "test-exchange",
queue_name: "test-queue",
routing_keys: ["test.topic"]
},
create_infrastructure: true,
max_retries: 3}
]
Supervisor.init(children, strategy: :one_for_one)
end
end
```
The supervisor itself can be mounted inside the main application tree by adding `{ExampleSupervisor, vhost_url: "amqp://"}`.
The consumer worker references a `MyHandler` module which needs to implement the `Rbt.Consumer.Handler` behaviour:
```elixir
defmodule MyHandler do
use Rbt.Consumer.Handler
def handle_event(event, meta) do
IO.inspect(event)
IO.inspect(meta)
:ok
end
end
```
To publish a message, it's possible to call:
```elixir
Rbt.Producer.publish("test-exchange", "test.topic", %{some: "data"}, message_id: "my-client-id")
```
## Testable components
Here's some strategies to write testable components based on Rbt.
### Consumer handlers
As a handler needs to implement a `handle_event/2` function, it's possible to make it easier to test by implementing a `handle_event/3` function (not a callback) which provides a third argument with defaults used for dependency injection.
For example:
```elixir
defmodule MyHandlerWhichRepublishes do
use Rbt.Consumer.Handler
@default_context %{
producer: Rbt.Producer
}
def handle_event(event, meta, context \\ @default_context) do
# do some work
context.producer.publish("new-exchange", "new-topic", %{some: "new data"})
end
end
```
In the snippet above, we provide a context map with a `producer` key, which is the module we want to use to produce new events. In our implementation
code, this module will use `Rbt.Producer`.
When we test this function, we can override it with `Rbt.Producer.Sandbox`:
```elixir
test_context = %{producer: Rbt.Producer.Sandbox}
assert :ok == MyHandlerWhichRepublishes.handle_event(%{some: "data"}, %{}, test_context)
assert 1 == Rbt.Producer.Sandbox.count_by_exchange("new-exchange")
```