# RabbitMQ, The Guide

**URL:** <https://community.shotgridsoftware.com/t/rabbitmq-the-guide/6816>\
**Category:** Pipeline Integrations\
**Tags:** python-api, tips\
**Created:** [January 31, 2020, 12:12am UTC](https://community.shotgridsoftware.com/t/rabbitmq-the-guide/6816 "2020-01-31T00:12:56Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![dougm](https://sea1.discourse-cdn.com/shotgunsoftware/user_avatar/community.shotgridsoftware.com/dougm/32/434_2.png) [@dougm](https://community.shotgridsoftware.com/u/dougm)\
**Post date:** [January 31, 2020, 12:12am UTC](https://community.shotgridsoftware.com/t/rabbitmq-the-guide/6816/1 "2020-01-31T00:12:56Z")

</div>

Tony Aiello (@taiello) once had a nice reply to Hasiel Alvarez (@hasielhassan) outlining his experience connecting **[RabbitMQ](https://www.rabbitmq.com/)** to Shotgun.

Since this valuable information was accidentally scrubbed from the [old ticket system](https://support.shotgunsoftware.com/hc/us/community/posts/209483538-MQ-based-Event-Log-Messages) I thought it might be helpful to post it back in here.

The rest of this post are mostly Tony’s words re-formatted and edited from 2018-05-24…

* * *

## For local-installs

Shotgun can publish `EventLogEntries` as MQ messages. I don’t think there is any significant documentation. The rest of my overly-long post here is still not going to be an attempt at thorough documentation either 🕶 But maybe others will help add to this over time and it’ll become a support Article.

## For hosted sites

Shotgun does not support this for cloud-hosted sites! Shotgun folks: yes / no?

## Get familiar with the terminology.

There was a [great presentation at PyCon 2018](https://www.youtube.com/watch?v=WXkhJ92-fsY) in Cleveland just a couple weeks ago, check it out. Go watch it, I’ll wait…

_(by the way, ALL [PyCon videos are on YouTube](https://www.youtube.com/channel/UCsX05-2sVSH7Nx3zuk3NYuQ). PyCon is awesome)_

## Shotgun would be considered the _producer_

- Shotgun already has built-in support to publish `EventLogEntries` as they happen out to an _exchange_

- Using a _message broker_, you create one or more _bindings_ between an _exchange_ and one or more _queues_.

- The _routing\_keys_ map from an _exchange_ to a _queue_.

- Each `EventLogEntry` gets packed as a json blob into a _message_. Then you can write _consumers_ to receive the _messages_ from the _queues_ and do stuff with them.

# Step #1: Installing a message broker

Here at Laika, our broker of choice is **RabbitMQ** ; we currently run version 3.6.12. You can install a broker directly on your shotgun server, or you can setup dedicated hardware for it. Dedicated hardware is what we do, because we use _RabbitMQ_ for MUCH more than just Shotgun.

Our _RabbitMQ_ setup consists of an 8-node cluster: two servers that are physically redundant for each of our four buildings.

A **proxy server** provides the physically-closest pair through a common URL. Rabbit only ships data over the building WANs if necessary, otherwise all the traffic is kept within the specific network segment of the cluster. The hosts are eight-core i7 machines with 12G RAM, running FreeBSD 10.3p7 without any tweaks.

We’ve measured the cluster wrangling over 18 thousand simple “hello world” messages per second with a cluster policy of +2 replication. In addition to Rabbit, the hosts also serve DHCP/DNS/LDAP, and even so load is consistently below 1.

# Step #2: Enable message production

This Shotgun _producer_-bit might be the easiest part of this process! If you’re locally-hosted, `ssh` into your Shotgun server then

```
cd /var/rails/<your_site>/shared/config

```

You should find a “bunny.example” file there; take a look inside:

```
# This file contains contains information Shotgun uses to 
# connect to RabbitMQ.
#
# The host and port variables should match the information needed 
# to connect to the rabbit host. 
# If the host is different then localhost you may need to open
# firewall ports between the Shotgun server and the RabbitMQ server.
#
# DEFINITIONS
# :host: The fqdn or ip address of the host running rabbitmq
# :port: The port at which rabbitmq is listening on the :host:
# :id: This is the exchange prefix. 
# Typically, this is set to your shotgun site's fqdn. 
# The exchange is the rabbit process which delivers the message
# to the appropriate queue(s).
# :no_crud: This is an internal variable which should be set to true
#
# EXAMPLE
---
:bunny:
  :host: localhost
  :port: 5672
  :id: shotgun.yourstudio.com
  :no_crud: true

```

You should now:

- make a copy of the `bunny.example` file, named `bunny.yml` – that specific name is _required_ for this to work

- edit that copy to set the values for `host` and `port` to point at your broker install

- edit that copy to set the `id` value

- save the file and exit your editor, then

- restart your site.

To clarify the `:id:` key in this basic config – whatever value you specify for `:id:` will get concatenated with `_events` to form the exchange name.

So if you specify `:id: fubar.studio.com` then the exchange into which the Shotgun `EventLogEntries` will go will be named `fubar.studio.com_events`.

### Exchange Type

Shotgun uses the `fanout` exchange type by default.

Assuming you were paying attention to the video, this means that all messages from Shotgun going into the exchange defined by your `bunny.yml` will be routed to every queue you bind to that exchange.

So if you’re paying close attention, you might notice there is no way to specify a `routing_key` in that `bunny.yml`.

Fanout is like an ethernet hub – any one thing coming-in gets copied back out every exit, so the responsibility is on the consumer(s) of the queue(s) to decide whether the message (copy) is interesting or not.

As opposed to `topic` and `direct` exchange types, which are analogous to an ethernet switch, where there’s efficiency gained by smart routing thus lightening the workload for your consumer(s).

### Routing Keys

Regardless, Shotgun creates the `routing_keys` for event messages for you. The first part of the string is either the `event['project']['code']` field value or `global` if the event doesn’t have a `['project']` value.

Then it appends a converted form of the `event['event_type']`  
e.g. `Shotgun_Asset_Change` by stripping off the leading `Shotgun_` then converting underscores to periods then lowercasing it all: `Shotgun_Asset_Change` becomes `asset.change`.

So for an _Asset change event_ in a project whose `['code']` is `abc`, the `routing_key` for the MQ message created would be `abc.asset.change` and for a non-project event like creating a new `HumanUser` entity, the `routing_key` would be `global.humanuser.new`.

### One other bit

If you’re running anything older than Shotgun v6.3, which uses Ruby 1.9 and thus bunny v0.8.0, you will need to upgrade the bunny gem in your installation. cd /opt/ruby/bin then sudo ./gem install bunny should bring you up to the right version. Again, local-hosted only…

If you’re cloud-hosted, you would have to ask Shotgun Support to do all this for you… and again, I don’t know if Shotgun even officially supports this at all for cloud-hosted sites.

If you do get to this point, new `EventLogEntry` messages should start hitting your broker.

### SPOILER WARNING…

Speaking just for Laika, we don’t recommend nor use that basic `bunny.yml` file. Since you watched the video, you know that one might want to completely control the exchange name and probably set an `exchange_type`.

The exchange name of `{id}_events` and `fanout` exchange type might not be ideal for your studio. And if your IT dept cares about security like ours, they probably will want to SSL-secure the broker site and create virtual hosts within it with `username:password` creds.

I do know of at least two studios that are successfully using Shotgun’s basic `bunny.yml` file and hardcoded defaults, but it was not enough for our environment.

So a few years ago, we worked with Shotgun on custom code to add support for those additional (in our case, essential) properties. Now our `bunny.yml` file looks like this:

```
:bunny:
  :uri: amqp://user:pword@rabbitmq.studio.com/%2Fa_virtual%2Fhost%2Fname
  :exchange_type: topic
  :exchange: an_exchange_name
  :id: routing_key_prefix
  :no_crud: true

```

Notice that the custom code adds three new keys:

- `:uri:`
- `:exchange:`
- `:exchange_type:`

Also notice that by using a specific `:uri:` , it’s not necessary to specify a `:host:` or `:port:` .

You or your IT dept would configure the virtual host(s) in RabbitMQ, in the form of something like maybe /your\_studio/your\_department/maybe\_another\_noun , and notice that the slashes have to be converted to hex %2F when entered in the :uri: key in the bunny.yml file. You’d define the :exchange: name and :exchange\_type: in RabbitMQ, then enter those values in the bunny.yml file. (If you leave the exchange\_type as fanout in RabbitMQ, you can skip entering an :exchange\_type: key/value in the .yml.) You’d define routing keys in RabbitMQ as described in the video – if you use a “topic” type, you’ll probably use at least one # wildcard in your key definition – and bind the exchange(s) to queue(s) with those keys.

## More on routing keys

If you use a `topic :exchange_type`, the `:id:` key in the YML becomes a prefix value for Shotgun’s `routing_keys` instead of the exchange name.

So for example with an `:id:` value of `foo`, the `routing_keys` for messages will start with `foo` and end with the rest of the key that Shotgun constructed, for example `foo.abc.asset.change` or `foo.global.humanuser.new` using the example above.

Again, using the `#`-wildcard functionality in _RabbitMQ_, you might bind an exchange to an _asset-changes-only-queue_ for _all_ projects with a key like `#.asset.change`… and maybe another queue for _all_ events in the `abc` project with a key like `#.abc.#`… and maybe if you’re running multiple Shotgun sites (for example production and staging) and thus have multiple `bunny.yml` files, you might use that `:id:` field to specify the site after all and thus have a queue per site. All sorts of combinations are possible!

If you want these additional RabbitMQ properties, regardless of whether you’re cloud- or local-hosted, you need to **talk to Shotgun about the custom code.** I don’t know if it has become part of the standard shotgun app code in the years since, or if it needs to be specially-activated, or if it has to be a totally different app software cut for you.

## Disclaimer

[Tony] I hope I’m not making trouble for the Shotgun folks by talking about this!

Certainly not my intent my friends, and feel free to delete my post, or just this part of it, with my apologies in advance.

[Dougm] …ditto! Likely Shotgun Software would appreciate me noting that this is posting might never be discussed by anyone other than clients that have local installs and enough engineers to dabble in this.

# After installing

You’ve got EventLogEntries from Shotgun hitting your message broker! Great! Route them from the exchange(s) to some queues via some key bindings, and start consuming the messages from those queues.

### Step 3: Create one or more queues

…in your broker app

### Step 4: Create bindings

…from the exchange(s) to those queues in your broker app using some form of key specification probably involving the # wildcard

### Step 5: Write some consumer apps

…to pull the messages off those queues… see the video for examples in python

### Step 6: Profit!

# Consumer Apps

Notice that your consumer apps will probably have to open a shotgun python API connection (or hit Shotgun v7.9 REST API endpoint woohoo!) to retrieve more data about the EventLogEntries, similar to what you usually have to do with the shotgun event daemon or your own apps.

Finally, `EventLogEntries` that _fail_ to get into an exchange get logged into

```
/var/rails/<your_site>shared/log/rabbitmq_failed_events.log

```

# Sample

Here’s a sample RabbitMQ message from Kubo and the Two Strings production. Note the Exchange and Routing Key are custom-code spoiler-style:

```
  Exchange: shotgun
  Routing Key: production.kbo.version.change
  content_type: application/json
  Payload: 756 bytes
  Encoding: string
  {"user":{"name":"GDPR Redacted","type":"HumanUser","id":<redacted>,"login":"gdpr_redacted"},"id":39871767,"event_type":"Shotgun_Version_Change","description":"GDPR Redacted changed \"Status\" from \"aprdir\" to \"aprtk\" on Version 1500.0490.test.001","meta":{"type":"attribute_change","attribute_name":"sg_status_list","entity_type":"Version","entity_id":407523,"field_data_type":"status_list","old_value":"aprdir","new_value":"aprtk"},"entity_id":407523,"entity_type":"Version","created_at":"2015-11-10 00:46:14.314755","image_id":0,"attribute_name":"sg_status_list","project":{"name":"Kubo","type":"Project","id":125},"notification_trial":1,"session_uuid":"6bd44946-8744-11e5-a82e-1adeadbeef01","sg_processed":"f","filmstrip_image_id":0,"cached_display_name":null}
```

---

<div class="post-metadata">

**Author:** ![andrew.lawrence](https://sea1.discourse-cdn.com/shotgunsoftware/user_avatar/community.shotgridsoftware.com/andrew.lawrence/32/3938_2.png) [@andrew.lawrence](https://community.shotgridsoftware.com/u/andrew.lawrence)\
**Post date:** [January 31, 2020, 3:32am UTC](https://community.shotgridsoftware.com/t/rabbitmq-the-guide/6816/2 "2020-01-31T03:32:12Z")

</div>

Hi Doug,

This is absolutely fantastic, thank you so much for re-sharing this.

Cheers,  
Andrew
