Simple RabbitMQ Wrapper for PowerShell

I was working on a project where I needed a pool of PowerShell workers that were distributed across several nodes and should be served by a central dispatcher. So why not give AMQP with RabbitMQ a try?
The setup was very easy thanks to the documentation. And besides plenty of .NET library wrappers around “Rabbit.MQ.Client.dll” there were even two PowerShell modules on the Developer tools list. However, as it turned out they had their limitations (like working via the HTTP interface instead of AMQP directly, or just providing too much functionality where I only needed a quick dispatcher/worker queue scenario).

So my first thought was to just “Add-Type” the “Rabbit.MQ.Client.dll” client and implement Send/Receive quickly by following the tutorials. But as it turned out this was not really working as the “Rabbit.MQ.Client.dll” assembly is not CLS compliant and therefore the “ConnectionFactory” could not be instantiated. A quick search revealed I was not the only one having this issue. So I decided to write a quick wrapper around it with a few convenience methods to be used in PowerShell. The idea was not to create a full replica of the original client or to create 667 PowerShell cmdlets but to expose as much from the original client as possible while being able to send and receive a message with as few command as possible. So here we are (code complies with VS2013):

In case your browser does not render the Gist correctly you can view the code directly at: https://gist.github.com/dfch/6da3d17414c913595302.

Usage is simple, you just add the assembly to your session (note: the assembly must be in the same path as the original RabbitMQ client dll) and then you specify server, username, password and your queue.

Add-Type -Path ".\RabbitMQHelper.dll"
$mq = New-Object RabbitMQHelper.Client;
$mq.Server = "amqp.example.com";
$mq.Username = "Edgar.Schnittenfittich";
$mq.Password = "P@ssL0rd";
$mq.QueueName = "MyQueueName";
$mq.Send("tralala");
$mq.Disconnect();

Receiving a message is nearly the same; call “Rceive()” and optionally specify the wait timeout:

Add-Type -Path ".\RabbitMQHelper.dll"
$mq = New-Object RabbitMQHelper.Client;
$mq.Server = "amqp.example.com";
$mq.Username = "Edgar.Schnittenfittich";
$mq.Password = "P@ssL0rd";
$mq.QueueName = "MyQueueName";
# receive message but return immediately if none
$Message = $mq.Receive(0);
# receive message but wait only 5 seconds
$Message = $mq.Receive(5000);
# receive message and wait indefinitely
$Message = $mq.Receive();
$mq.Disconnect();

There was a strange behavior I observed after connecting a consumer and receving from a queue. On the first fetch/dequeue operation with a WaitTimeout of 0 no message was returned. On a subsequent fetch the message was received. When using an infinite WaitTimeout (-1) the message was received on the first call.

In case you get an error while trying to connect to your AMQP server have a look at the ErrorRecord, especially the second InnerException of the Exception:

PS > $mq.Send("tralala");
Exception calling "Send" with "1" argument(s): "None of the specified endpoints were reachable"
At line:1 char:1
+ $mq.Send("tralala");
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : BrokerUnreachableException

PS > $error[0].Exception
Exception calling "Send" with "1" argument(s): "None of the specified endpoints were reachable"
PS > $error[0].Exception.InnerException
None of the specified endpoints were reachable
PS > $error[0].Exception.InnerException.InnerException
ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.

The broker log file (which you will find below the %APPDATA% folder) will sometimes give you more details, such as telling you about lacking permissions or wrong credentials:

=INFO REPORT==== 24-Aug-2014::01:12:59 ===
accepting AMQP connection <0.281.0> (amqpclient.example.com:54082 -> amqp.example.com:5672)

=ERROR REPORT==== 24-Aug-2014::01:13:02 ===
closing AMQP connection <0.281.0> (amqpclient.example.com:54082 -> amqp.example.com:5672):
{handshake_error,starting,0,
 {amqp_error,access_refused,
 "PLAIN login refused: user 'guest' can only connect via localhost",
 'connection.start_ok'}}

=INFO REPORT==== 24-Aug-2014::01:13:21 ===
accepting AMQP connection <0.286.0> (amqpclient.example.com:54083 -> amqp.example.com:5672)

=ERROR REPORT==== 24-Aug-2014::01:13:24 ===
closing AMQP connection <0.286.0> (amqpclient.example.com:54083 -> amqp.example.com:5672):
{handshake_error,starting,0,
 {amqp_error,access_refused,
 "PLAIN login refused: user 'Edgar.Schnittenfittich' - invalid credentials",
 'connection.start_ok'}}

Note: if you have a fresh installation of RabbitMQ you might want to add a user so you can connect remotely to your server:

Create new user so we can connect from remote
C:\> rabbitmqctl.bat add_user Administrator P@ssL0rd
Make this user read/write admin
C:\> rabbitmqctl.bat set_user_tags Administrator administrator
C:\> rabbitmqctl.bat set_permissions -p / Administrator .* .* .*

C:\> rabbitmqctl.bat list_users
Listing users ...
Administrator   [administrator]
guest   [administrator]
...done.

C:\> rabbitmqctl.bat list_permissions
Listing permissions in vhost &quot;/&quot; ...
Administrator   .*   .*   .*
guest   .*   .*   .*
...done.

And it is always a good idea to watch your connections, consumers, queues and the like:

C:\> rabbitmqctl.bat list_connections
Listing connections ...
Administrator   192.168.174.148 53825   running
Administrator   192.168.174.148 53828   running
...done.

C:\> rabbitmqctl.bat list_queues
Listing queues ...
default 0
q2      0
q3      1786889
task_queue      0
...done.

C:\> rabbitmqctl.bat list_consumers
Listing consumers ...
q3      <rabbit@amqpserver.1.1609.0>     amq.ctag-K1U5l7foM-7RDhxfcgOwTw true
1       []
q3      <rabbit@amqpserver.1.1665.0>     amq.ctag-fB8dS9ZeJt9jNDP4EbCuBw true
1       []
...done.

Comments

  1. Reblogged this on Dinesh Ram Kali..

Trackbacks

  1. […] couple of days ago I wrote about how to use RabbitMQ with AMQP from PowerShell (via a small assembly that utilized the original C# .NET Client). After I implemented it in the use case for our customer […]

  2. […] module contains Cmdlets to work with RabbitMQ queues, send and receive messages. It uses a wrapper I earlier presented that solved the problem of loading the official RabbitMQ .NET client into a PowerShell session. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: