-
Notifications
You must be signed in to change notification settings - Fork 30
Notification architecture
Notifications are delivered via the AMQP 0-9-1 protocol to a compatible message broker. Quite what happens to notifications after that point is not the concern of the ACC application itself, so this isn't going to be covering a development perspective of how they work. This document is mostly from an operational perspective.
We're using RabbitMQ as the message broker, and to account for network issues/server restarts/etc, we're using a pair of brokers. One broker hosted on WMCS receives the messages from the local instances of the application. The other broker is hosted on the same infrastructure as Helpmebot. We use a shovel to move messages between brokers.
To quickly summarise how AMQP message brokers work, I'll introduce some basic concepts. We connect to the broker (via TCP), and then "publish" our message to an "exchange". We can also read messages from a "queue". Over-simplifying, exchanges are write-only, and queues are read-only. A message is published with a "routing key" - this can be any string value. Queues and exchanges are connected together with "bindings", which can be configured to send specific messages to different queues based on the routing key of the message.
For ACC, the application writes to a single exchange (acc.notification
) on the WMCS broker with a configured routing key. This exchange has a single binding to a queue (acc.notification.queue
) which receives all messages published to the exchange (this exchange is a fanout
exchange).
Once messages are on the queue, they are picked up by a shovel - basically a thing which reads messages off one queue and writes them to an exchange somewhere else. This shovel is configured to read messages from acc.notification.queue
and write them to an exchange on Helpmebot's message broker (helpmebot.prod.x.notification
).
On Helpmebot's side, the exchange helpmebot.prod.x.notification
is configured as a direct
exchange, so it uses the routing key on the message to match the routing key filters on the bindings to route to the "correct" queue. In practice, there's only a single queue, and it's used as a filter to only route messages to the queue which are for a known set of routing keys.
Helpmebot then reads the messages from the queue and delivers the content directly to IRC, using the message routing key as a channel name.
Note - the configuration described below is not secure.
- Install RabbitMQ on the same server ACC runs on (Debian:
sudo apt-get install rabbitmq-server
; Docker: use image rabbitmq:3-management mapping ports 5672 and 15672). - Enable the management UI (
sudo rabbitmq-plugins enable rabbitmq_management
, docker users can skip) - Log into the management UI on port 15672 (
guest
/guest
) - Create a queue with any name you want, eg
potato
from the RabbitMQ management UI - Ensure ACC's configuration setting
$amqpConfiguration
is set to the default (unless you've started changing more things in the RabbitMQ management UI; at that point you're on your own) - Edit the domain setting for notification target to the queue name on RabbitMQ (eg,
potato
). Also set$ircBotNotificationRoutingKey = 'potato;
in configuration. - Ensure
$ircBotNotificationsEnabled = 1;
- You should be able to use the RabbitMQ management UI to view and acknowledge (thus dequeue) items on the queue as they're generated.
The technical stuff: this config should work because the default exchange ""
acts as a direct exchange with implicit bindings on every queue, so the routing key specifies the target queue without further configuration. We don't do this in prod, but it should work fine for a local dev instance.
-
Install RabbitMQ on WMCS. Enable the management and shovel plugins:
sudo rabbitmq-plugins enable rabbitmq_management rabbitmq_shovel_management
-
Add a new user for yourself:
sudo rabbitmqctl add_user myusername myp@ssw0rd
sudo rabbitmqctl set_permissions -p / myusername '.*' '.*' '.*'
sudo rabbitmqctl set_user_tags myusername administrator
- Set up an SSH tunnel mapping port 15672 to your local machine.
- Browse to http://localhost:15672 (or equivalent), and log in.
- Make sure the guest user is deleted.
-
Create exchange
acc.notification
, typefanout
, durable, autodelete=no. -
Create exchange
acc.deadletter
, typefanout
, durable, autodelete=no. -
Create queue
acc.notification.queue
, durable -
Create queue
acc.deadletter.queue
, durable, lazy mode -
Bind
acc.deadletter
toacc.deadletter.queue
with blank routing key -
Bind
acc.notification
toacc.notification.queue
with blank routing key -
Create policy to set the dead letter target and set some time/length limits on the queue.
- pattern:
^acc\.notification\.queue$
- apply to queues
dead-letter-exchange: acc.deadletter
max-length: 20
message-ttl: 900000
- pattern:
-
Create user
ExampleUsername
. THIS MUST MATCH the username on Helpmebot's broker, though the password need not match.- Grant access to vhost
/
with configure^$
, read^$
, write^acc\.notification$
. This permits this user only to write to one exchange.
- Grant access to vhost
-
Create another user for the shovel - I used
shovel
.- Grant access to vhost
/
with configure^$
, read^acc\.notification\.queue$
, write^$
. This permits this user only to read from one queue.
- Grant access to vhost
-
Configure ACC to connect to the broker with username
ExampleUsername
, writing to exchangeacc.notification
on vhost/
-
Configure a shovel:
- Name: whatever you want
- Source:
- AMQP 0.9.1
amqp://shovel@
- Queue:
acc.notification.queue
- Prefetch count left blank
- Auto-delete: never
- Destination:
- AMQP 0.9.1
-
amqps://ExampleUsername:[redacted]@mq.srv.stwalkerster.net
(ensure password is URL-encoded) - Exchange:
helpmebot.prod.x.notification
- Forwarding headers: Yes
- Routing key left blank
- Reconnect delay left blank
- Ack mode: on publish
- Create a user for Helpmebot, with configure/write/read to
^helpmebot\.prod\.[xq]\..*$
- Configure Helpmebot to point to the broker with a prefix of
helpmebot.prod
, and start it. It'll create most of the queues/exchanges it needs itself. - Create user
ExampleUsername
. THIS MUST MATCH the username on Helpmebot's broker, though the password need not match.- Grant access to vhost
/
with configure^$
, read^$
, write^helpmebot\.prod\.x\.notification$
. This permits this user only to write to one exchange.
- Grant access to vhost
- Ensure the necessary bindings are set up between
helpmebot.prod.x.notification
andhelpmebot.prod.q.notification
. This can be managed with bot commands.
- Helpmebot only accepts messages with a user_id set.
- The shovel only works for messages with user_id == ExampleUsername.
- It's not possible to set user_id to a value different from you current username.