rabbitmq-tutorials/python/tutorial-one.mdx
2010-10-08 12:01:32 +01:00

382 lines
12 KiB
Plaintext

Learning RabbitMQ, part 1 ("Hello world!")
==========================================
{% dot -Gsize="10,1.3" -Grankdir=LR %}
digraph G {
P1 [label="P", {{ dotstyle.producer }}];
Q1 [label="{<s>||||<e>}", {{ dotstyle.queue }}];
C1 [label="C", {{ dotstyle.consumer }}];
P1 -> Q1 -> C1;
}
{% enddot %}
Throughout this tutorial, we'll teach you the basic concepts required for
creating RabbitMQ applications. The tutorial will be illustrated with
code snippets written in [Python](http://www.python.org/) and executed on Linux.
But don't worry if you don't know this language - the core ideas are the same
for other languages.
This tutorial consists of three parts:
* First we're going to write the simplest possible "Hello World" example.
* Next we'll try to use Rabbit as [a simple "Work queue" server]({{ python_two_url }}).
* Finally, we'll discuss how to [broadcast a message]({{ python_three_url }}).
You need to have RabbitMQ server installed to go through this tutorial.
If you haven't installed it yet you can follow the
[installation instructions](http://www.rabbitmq.com/install.html).
You can tell RabbitMQ is installed by running:
$ sudo rabbitmqctl status
If you have installed RabbitMQ you should see something like:
Status of node rabbit@example ...
[{running_applications,[{os_mon,"CPO CXC 138 46","2.2.5"},
{sasl,"SASL CXC 138 11","2.1.9"},
{mnesia,"MNESIA CXC 138 12","4.4.13"},
{stdlib,"ERTS CXC 138 10","1.16.5"},
{kernel,"ERTS CXC 138 10","2.13.5"}]},
{nodes,[{disc,[rabbit@example]}]},
{running_nodes,[rabbit@example]}]
...done.
> #### Where to get help
>
> If you're having trouble going through this tutorial you can post a message to
> [rabbitmq-discuss mailing list](https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss)
> or join the [#rabbitmq](irc://irc.freenode.net/rabbitmq) irc channel.
Introduction
------------
RabbitMQ is a message broker. The principle idea is pretty simple: it accepts
and forwards messages. You can think about it as a post office: when you send
mail to the post box you're pretty sure that Mr. Postman will eventually
deliver the mail to your recipient. Using this metaphor RabbitMQ is a post box,
a post office and a postman.
The major difference between RabbitMQ and the post office is the fact that it
doesn't deal with the paper, instead it accepts, stores and forwards binary
blobs of data - _messages_.
RabbitMQ uses a specific jargon. For example:
* _Producing_ means nothing more than sending. A program that sends messages
is a _producer_. We'll draw it like that, with "P":
{% dot -Gsize="10,0.3" -Grankdir=LR%}
digraph G {
P1 [label="P", {{ dotstyle.producer }}];
}
{% enddot %}
* _A queue_ is the name for a mailbox. It lives inside
RabbitMQ. Although messages flow through RabbitMQ and your
applications, they can be stored only inside a _queue_. A _queue_
is not bound by any limits, it can store how many messages you
like - it's essentially an infinite buffer. Many _producers_ can send
messages that go to the one queue, many _consumers_ can try to
receive data from one _queue_. A queue will be drawn as like that, with
its name above it:
{% dot -Gsize="10,0.9" -Grankdir=LR%}
digraph G {
subgraph cluster_Q1 {
label="queue_name";
color=transparent;
Q1 [label="{<s>||||<e>}", {{ dotstyle.queue }}];
};
}
{% enddot %}
* _Consuming_ has a similar meaning to receiving. A _consumer_ is a program
that mostly waits to receive messages. On our drawings it's shown with "C":
{% dot -Gsize="10,0.3" -Grankdir=LR %}
digraph G {
C1 [label="C", {{ dotstyle.consumer }}];
}
{% enddot %}
Hello World!
------------
Our "Hello world" won't be too complex - let's send a message, receive
it and print it on the screen. To do so we need two programs: one that
sends a message and one that receives and prints it.
Our overall design will look like:
{% dot -Gsize="10,1.3" -Grankdir=LR %}
digraph G {
P1 [label="P", {{ dotstyle.producer }}];
subgraph cluster_Q1 {
label="test";
color=transparent;
Q1 [label="{<s>||||<e>}", {{ dotstyle.queue }}];
};
C1 [label="C", {{ dotstyle.consumer }}];
P1 -> Q1 -> C1;
}
{% enddot %}
Producer sends messages to the "test" queue. The consumer receives
messages from that queue.
> #### RabbitMQ libraries
>
> RabbitMQ speaks a protocol called AMQP. To use Rabbit you'll need a library
> that understands the same protocol as Rabbit. There is a choice of libraries
> for almost every programming language. For python it's not different and there
> is a bunch of libraries to choose from:
>
> * [py-amqplib](http://barryp.org/software/py-amqplib/)
> * [txAMQP](https://launchpad.net/txamqp)
> * [pika](http://github.com/tonyg/pika)
>
> In this tutorial we're going to use `pika`. To install it you can use
> [`pip`](http://pip.openplans.org/) package management tool:
>
> $ sudo pip install -e git+http://github.com/tonyg/pika.git#egg=pika
>
> If you don't have `pip`, you may need to install it.
>
> * On Ubuntu:
>
> $ sudo apt-get install python-pip
>
> * On Debian:
>
> $ sudo apt-get install python-setuptools
> $ sudo easy_install pip
### Sending
{% dot -Gsize="10,1.3" -Grankdir=LR %}
digraph G {
P1 [label="P", {{ dotstyle.producer }}];
subgraph cluster_Q1 {
label="test";
color=transparent;
Q1 [label="{<s>||||<e>}", {{ dotstyle.queue }}];
};
P1 -> Q1;
}
{% enddot %}
Our first program `send.py` will send a single message to the queue.
The first thing we need to do is to establish a connection with
RabbitMQ server.
{% highlight python %}
#!/usr/bin/env python
import pika
connection = pika.AsyncoreConnection(pika.ConnectionParameters(
'127.0.0.1',
credentials = pika.PlainCredentials('guest', 'guest'))
channel = connection.channel()
{% endhighlight %}
We're connected now. Next, before sending we need to make sure the
recipient queue exists. If we send a message to non-existing location,
RabbitMQ will just trash the message. Let's create a queue to which
the message will be delivered, let's name it _test_:
{% highlight python %}
channel.queue_declare(queue='test')
{% endhighlight %}
At that point we're ready to send a message. Our first message will
just contain a string _Hello World!_ and we want to send it to our
_test_ queue.
In RabbitMQ a message can never be send directly to the queue, it always
needs to go through an _exchange_. But let's not get dragged by the
details - you can read more about _exchanges_ in [the third part of this
tutorial]({{ python_three_url }}). All we need to know now is how to use a default exchange
identified by an empty string. This exchange is special - it
allows us to specify exactly to which queue the message should go.
The queue name needs to be specified in the `routing_key` parameter:
{% highlight python %}
channel.basic_publish(exchange='',
routing_key='test',
body='Hello World!')
print " [x] Sent 'Hello World!'"
{% endhighlight %}
### Receiving
{% dot -Gsize="10,1.3" -Grankdir=LR %}
digraph G {
rankdir=LR;
subgraph cluster_Q1 {
label="test";
color=transparent;
Q1 [label="{<s>||||<e>}", {{ dotstyle.queue }}];
};
C1 [label="C", {{ dotstyle.consumer }}];
Q1 -> C1;
}
{% enddot %}
Our second program `receive.py` will receive messages from the queue and print
them on the screen.
Again, first we need to connect to RabbitMQ server. The code
responsible for connecting to Rabbit is the same as previously.
The next step, just like before, is to make sure that the
queue exists. Creating a queue using `queue_declare` is idempotent - we can
run the command as many times you like, and only one will be created.
{% highlight python %}
channel.queue_declare(queue='test')
{% endhighlight %}
You may ask why to declare the queue again - we have already declared it
in our previous code. We could have avoided that if we were sure
that the queue already exists. For example if `send.py` program was
run before. But we're not yet sure which
program to run first. In such cases it's a good practice to repeat
declaring the queue in both programs.
> #### Listing queues
>
> You may want to see what queues does RabbitMQ store and how many
> messages are in them. You can do it using the `rabbitmqctl` tool:
>
> $ sudo rabbitmqctl list_queues
> Listing queues ...
> test 0
> ...done.
Receiving messages from the queue is more complex. It works by subscribing
a `callback` function to a queue. Whenever we receive
a message, this `callback` function is called by the Pika library.
In our case this function will print on the screen the contents of
the message.
{% highlight python %}
def callback(ch, method, header, body):
print " [x] Received %.20r" % (body,)
{% endhighlight %}
Next, we need to tell RabbitMQ that this particular callback function should
receive messages from our _test_ queue:
{% highlight python %}
channel.basic_consume(callback,
queue='test',
no_ack=True)
{% endhighlight %}
For that command to succeed we must be sure that a queue which we want
to subscribe to exists. Fortunately we're confident about that - we've
created a queue above - using `queue_declare`.
The `no_ack` parameter will be described [later on]({{ python_two_url }}).
And finally, we enter a never-ending loop that waits for data and runs callbacks
whenever necessary.
{% highlight python %}
print ' [*] Waiting for messages. To exit press CTRL+C'
pika.asyncore_loop()
{% endhighlight %}
### Putting it all together
Full code for `send.py`:
{% highlight python linenos=true %}
#!/usr/bin/env python
import pika
connection = pika.AsyncoreConnection(pika.ConnectionParameters(
host='127.0.0.1',
credentials=pika.PlainCredentials('guest', 'guest')))
channel = connection.channel()
channel.queue_declare(queue='test')
channel.basic_publish(exchange='',
routing_key='test',
body='Hello World!')
print " [x] Sent 'Hello World!'"
{% endhighlight %}
[(send.py source)]({{ examples_url }}/python/send.py)
Full `receive.py` code:
{% highlight python linenos=true %}
#!/usr/bin/env python
import pika
connection = pika.AsyncoreConnection(pika.ConnectionParameters(
host='127.0.0.1',
credentials=pika.PlainCredentials('guest', 'guest')))
channel = connection.channel()
channel.queue_declare(queue='test')
print ' [*] Waiting for messages. To exit press CTRL+C'
def callback(ch, method, header, body):
print " [x] Received %.20r" % (body,)
channel.basic_consume(callback,
queue='test',
no_ack=True)
pika.asyncore_loop()
{% endhighlight %}
[(receive.py source)]({{ examples_url }}/python/receive.py)
Now we can try out our programs. First, let's send a message using our
`send.py` program:
$ python send.py
[x] Sent 'Hello World!'
Let's receive it:
$ python receive.py
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'Hello World!'
Hurray! We were able to send our first message through RabbitMQ. As you might
have noticed, the `receive.py` program doesn't exit. It will stay ready to
receive further messages. Try to run `send.py` again in a new terminal!
We've learned how to send and receive a message from a named
queue. It's time to move on to [part 2]({{ python_two_url }})
and build a simple _task queue_.