Updated README.md to reflect changes.
This commit is contained in:
parent
b558b8cac6
commit
f7e8cf5414
177
README.md
177
README.md
|
|
@ -5,33 +5,33 @@ AMQP-CPP is a C++ library for communicating with a RabbitMQ message broker. The
|
|||
library can be used to parse incoming data from a RabbitMQ server, and to
|
||||
generate frames that can be sent to a RabbitMQ server.
|
||||
|
||||
This library has a layered architecture, and allows you - if you like - to
|
||||
completely take care of the network layer. If you want to set up and manage the
|
||||
This library has a layered architecture, and allows you - if you like - to
|
||||
completely take care of the network layer. If you want to set up and manage the
|
||||
network connections yourself, the AMQP-CPP library will not make a connection to
|
||||
RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As
|
||||
a user of this library, you create the socket connection and implement a certain
|
||||
interface that you pass to the AMQP-CPP library and that the library will use
|
||||
RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As
|
||||
a user of this library, you create the socket connection and implement a certain
|
||||
interface that you pass to the AMQP-CPP library and that the library will use
|
||||
for IO operations.
|
||||
|
||||
Intercepting this network layer is however optional, the AMQP-CPP library also
|
||||
comes with a predefined Tcp module that can be used if you trust the AMQP library
|
||||
Intercepting this network layer is however optional, the AMQP-CPP library also
|
||||
comes with a predefined Tcp module that can be used if you trust the AMQP library
|
||||
to take care of the network handling. In that case, the AMQP-CPP library does
|
||||
all the system calls to set up network connections and send and receive the data.
|
||||
|
||||
This layered architecture makes the library extremely flexible and portable: it
|
||||
does not necessarily rely on operating system specific IO calls, and can be
|
||||
This layered architecture makes the library extremely flexible and portable: it
|
||||
does not necessarily rely on operating system specific IO calls, and can be
|
||||
easily integrated into any kind of event loop. If you want to implement the AMQP
|
||||
protocol on top of some [unusual other communication layer](https://tools.ietf.org/html/rfc1149),
|
||||
this library can be used for that - but if you want to use it with regular TCP
|
||||
connections, setting it up is just as easy.
|
||||
protocol on top of some [unusual other communication layer](https://tools.ietf.org/html/rfc1149),
|
||||
this library can be used for that - but if you want to use it with regular TCP
|
||||
connections, setting it up is just as easy.
|
||||
|
||||
AMQP-CPP is fully asynchronous and does not do any blocking (system) calls, so
|
||||
AMQP-CPP is fully asynchronous and does not do any blocking (system) calls, so
|
||||
it can be used in high performance applications without the need for threads.
|
||||
|
||||
The AMQP-CPP library uses C++11 features, so if you intend use it, please make
|
||||
The AMQP-CPP library uses C++11 features, so if you intend use it, please make
|
||||
sure that your compiler is up-to-date and supports C++11.
|
||||
|
||||
**Note for the reader:** This readme file has a peculiar structure. We start
|
||||
**Note for the reader:** This readme file has a peculiar structure. We start
|
||||
explaining the pure and hard core low level interface in which you have to
|
||||
take care of opening socket connections yourself. In reality, you probably want
|
||||
to use the simpler TCP interface that is being described later on.
|
||||
|
|
@ -41,13 +41,13 @@ ABOUT
|
|||
=====
|
||||
|
||||
This library is created and maintained by Copernica (www.copernica.com), and is
|
||||
used inside the MailerQ (www.mailerq.com), Yothalot (www.yothalot.com) and
|
||||
AMQPipe (www.amqpipe.com) applications. MailerQ is a tool for sending large
|
||||
used inside the MailerQ (www.mailerq.com), Yothalot (www.yothalot.com) and
|
||||
AMQPipe (www.amqpipe.com) applications. MailerQ is a tool for sending large
|
||||
volumes of email, using AMQP message queues, Yothalot is a big data processing
|
||||
map/reduce framework and AMQPipe is a tool for high-speed processing messages
|
||||
map/reduce framework and AMQPipe is a tool for high-speed processing messages
|
||||
between AMQP pipes
|
||||
|
||||
Do you appreciate our work and are you looking for high quality email solutions?
|
||||
Do you appreciate our work and are you looking for high quality email solutions?
|
||||
Then check out our other commercial and open source solutions:
|
||||
|
||||
* Copernica Marketing Suite (www.copernica.com)
|
||||
|
|
@ -62,24 +62,38 @@ Then check out our other commercial and open source solutions:
|
|||
|
||||
INSTALLING
|
||||
==========
|
||||
There are two methods to compile AMQP-CPP, CMake and Make. CMake is platform portable, but the Makefile only works on Linux.
|
||||
## CMake
|
||||
The CMake file supports both building and installing. You can choose not to use the install functionality, and instead to manually use the build output at bin/. Keep in mind that the TCP module is only supported for Linux. An example install method would be:
|
||||
``` bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. [-DBUILD_SHARED] [-DLINUX_TCP]
|
||||
cmake --build .. --target install
|
||||
```
|
||||
|
||||
If you are on some kind of Linux environment, installing the library is as easy
|
||||
### BUILD_SHARED
|
||||
Default OFF
|
||||
ON: Build shared library
|
||||
OFF: Build static library
|
||||
|
||||
### LINUX_TCP
|
||||
Default OFF
|
||||
ON: Build linux-only TCP module
|
||||
OFF: Do not build the module.
|
||||
|
||||
## Make
|
||||
Installing the library is as easy
|
||||
as running `make` and `make install`. This will install the full version of
|
||||
the AMQP-CPP, including the system specific TCP module. If you do not need the
|
||||
additional TCP module (because you take care of handling the network stuff
|
||||
yourself), you can also compile a pure form of the library. Use `make pure`
|
||||
additional TCP module (because you take care of handling the network stuff
|
||||
yourself), you can also compile a pure form of the library. Use `make pure`
|
||||
and `make install` for that.
|
||||
|
||||
For users on a non-Linux environment: this library is known to work on your
|
||||
environment too (after all, it does not do any operating specific system calls),
|
||||
but it might take some extra effort to get your compiler to compile it. Please
|
||||
send in your pull requests once you have it running, so that others can benefit
|
||||
from your experiences.
|
||||
|
||||
When you compile an application that uses the AMQP-CPP library, do not
|
||||
forget to link with the library. For gcc and clang the linker flag is -lamqpcpp.
|
||||
If you use the fullblown version of AMQP-CPP (with the TCP module), you also
|
||||
need to pass a -lpthread linker flag, because the TCP module uses a thread
|
||||
If you use the fullblown version of AMQP-CPP (with the TCP module), you also
|
||||
need to pass a -lpthread linker flag, because the TCP module uses a thread
|
||||
for running an asynchronous and non-blocking DNS hostname lookup.
|
||||
|
||||
|
||||
|
|
@ -87,11 +101,11 @@ HOW TO USE AMQP-CPP
|
|||
===================
|
||||
|
||||
As we mentioned above, the library can be used in a network-agnostic fashion.
|
||||
It then does not do any IO by itself, and you need to pass an object to the
|
||||
It then does not do any IO by itself, and you need to pass an object to the
|
||||
library that the library can use for IO. So, before you start using the
|
||||
library, you first you need to create a class that extends from the
|
||||
ConnectionHandler base class. This is a class with a number of methods that are
|
||||
called by the library every time it wants to send out data, or when it needs to
|
||||
library, you first you need to create a class that extends from the
|
||||
ConnectionHandler base class. This is a class with a number of methods that are
|
||||
called by the library every time it wants to send out data, or when it needs to
|
||||
inform you that an error occured.
|
||||
|
||||
````c++
|
||||
|
|
@ -204,16 +218,16 @@ example call the "send()" or "write()" system call to send out the data to
|
|||
the RabbitMQ server. But what about data in the other direction? How does the
|
||||
library receive data back from RabbitMQ?
|
||||
|
||||
In this raw setup, the AMQP-CPP library does not do any IO by itself and it is
|
||||
therefore also not possible for the library to receive data from a
|
||||
socket. It is again up to you to do this. If, for example, you notice in your
|
||||
event loop that the socket that is connected with the RabbitMQ server becomes
|
||||
readable, you should read out that socket (for example by using the recv() system
|
||||
call), and pass the received bytes to the AMQP-CPP library. This is done by
|
||||
In this raw setup, the AMQP-CPP library does not do any IO by itself and it is
|
||||
therefore also not possible for the library to receive data from a
|
||||
socket. It is again up to you to do this. If, for example, you notice in your
|
||||
event loop that the socket that is connected with the RabbitMQ server becomes
|
||||
readable, you should read out that socket (for example by using the recv() system
|
||||
call), and pass the received bytes to the AMQP-CPP library. This is done by
|
||||
calling the parse() method in the Connection object.
|
||||
|
||||
The Connection::parse() method gets two parameters, a pointer to a buffer of
|
||||
data that you just read from the socket, and a parameter that holds the size of
|
||||
data that you just read from the socket, and a parameter that holds the size of
|
||||
this buffer. The code snippet below comes from the Connection.h C++ header file.
|
||||
|
||||
````c++
|
||||
|
|
@ -249,9 +263,9 @@ both the old data, and the new data.
|
|||
To optimize your calls to the parse() method, you _could_ use the Connection::expected()
|
||||
and Connection::maxFrame() methods. The expected() method returns the number of bytes
|
||||
that the library prefers to receive next. It is pointless to call the parse() method
|
||||
with a smaller buffer, and it is best to call the method with a buffer of exactly this
|
||||
size. The maxFrame() returns the max frame size for AMQP messages. If you read your
|
||||
messages into a reusable buffer, you could allocate this buffer up to this size, so that
|
||||
with a smaller buffer, and it is best to call the method with a buffer of exactly this
|
||||
size. The maxFrame() returns the max frame size for AMQP messages. If you read your
|
||||
messages into a reusable buffer, you could allocate this buffer up to this size, so that
|
||||
you never will have to reallocate.
|
||||
|
||||
|
||||
|
|
@ -259,17 +273,17 @@ TCP CONNECTIONS
|
|||
===============
|
||||
|
||||
Although the AMQP-CPP library gives you extreme flexibility by letting you setup
|
||||
your own network connections, the reality is that most if not all AMQP connections
|
||||
use the TCP protocol. To help you out, the library therefore also comes with a
|
||||
your own network connections, the reality is that most if not all AMQP connections
|
||||
use the TCP protocol. To help you out, the library therefore also comes with a
|
||||
TCP module that takes care of setting up the network connections, and sending
|
||||
and receiving the data.
|
||||
and receiving the data.
|
||||
|
||||
If you want to use this TCP module, you should not use the AMQP::Connection
|
||||
and AMQP::Channel classes that you saw above, but the alternative AMQP::TcpConnection
|
||||
and AMQP::TcpChannel classes instead. You also do not have to create your own class
|
||||
that implements the "AMQP::ConnectionHandler" interface - but a class that inherits
|
||||
from "AMQP::TcpHandler" instead. You especially need to implement the "monitor()"
|
||||
method in that class, as that is needed by the AMQP-CPP library to interact with
|
||||
that implements the "AMQP::ConnectionHandler" interface - but a class that inherits
|
||||
from "AMQP::TcpHandler" instead. You especially need to implement the "monitor()"
|
||||
method in that class, as that is needed by the AMQP-CPP library to interact with
|
||||
the main event loop:
|
||||
|
||||
````c++
|
||||
|
|
@ -334,7 +348,7 @@ class MyTcpHandler : public AMQP::TcpHandler
|
|||
// descriptor to the main application event loop (like the select() or
|
||||
// poll() loop). When the event loop reports that the descriptor becomes
|
||||
// readable and/or writable, it is up to you to inform the AMQP-CPP
|
||||
// library that the filedescriptor is active by calling the
|
||||
// library that the filedescriptor is active by calling the
|
||||
// connection->process(fd, flags) method.
|
||||
}
|
||||
};
|
||||
|
|
@ -344,8 +358,8 @@ The "monitor()" method can be used to integrate the AMQP filedescriptors in your
|
|||
application's event loop. For some popular event loops (libev, libevent), we have
|
||||
already added example handler objects (see the next section for that).
|
||||
|
||||
Using the TCP module of the AMQP-CPP library is easier than using the
|
||||
raw AMQP::Connection and AMQP::Channel objects, because you do not have to
|
||||
Using the TCP module of the AMQP-CPP library is easier than using the
|
||||
raw AMQP::Connection and AMQP::Channel objects, because you do not have to
|
||||
create the sockets and connections yourself, and you also do not have to take
|
||||
care of buffering network data.
|
||||
|
||||
|
|
@ -376,9 +390,9 @@ EXISTING EVENT LOOPS
|
|||
|
||||
Both the pure AMQP::Connection as well as the easier AMQP::TcpConnection class
|
||||
allow you to integrate AMQP-CPP in your own event loop. Whether you take care
|
||||
of running the event loop yourself (for example by using the select() system
|
||||
call), or if you use an existing library for it (like libevent, libev or libuv),
|
||||
you can implement the "monitor()" method to watch the file descriptors and
|
||||
of running the event loop yourself (for example by using the select() system
|
||||
call), or if you use an existing library for it (like libevent, libev or libuv),
|
||||
you can implement the "monitor()" method to watch the file descriptors and
|
||||
hand over control back to AMQP-CPP when one of the sockets become active.
|
||||
|
||||
For libev and libevent users, we have even implemented an example implementation,
|
||||
|
|
@ -394,26 +408,26 @@ int main()
|
|||
{
|
||||
// access to the event loop
|
||||
auto *loop = EV_DEFAULT;
|
||||
|
||||
|
||||
// handler for libev (so we don't have to implement AMQP::TcpHandler!)
|
||||
AMQP::LibEvHandler handler(loop);
|
||||
|
||||
|
||||
// make a connection
|
||||
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
|
||||
|
||||
|
||||
// we need a channel too
|
||||
AMQP::TcpChannel channel(&connection);
|
||||
|
||||
|
||||
// create a temporary queue
|
||||
channel.declareQueue(AMQP::exclusive).onSuccess([&connection](const std::string &name, uint32_t messagecount, uint32_t consumercount) {
|
||||
|
||||
|
||||
// report the name of the temporary queue
|
||||
std::cout << "declared queue " << name << std::endl;
|
||||
|
||||
|
||||
// now we can close the connection
|
||||
connection.close();
|
||||
});
|
||||
|
||||
|
||||
// run the loop
|
||||
ev_run(loop, 0);
|
||||
|
||||
|
|
@ -426,7 +440,7 @@ The AMQP::LibEvHandler and AMQP::LibEventHandler classes are extended AMQP::TcpH
|
|||
classes, with an implementation of the monitor() method that simply adds the
|
||||
filedescriptor to the event loop. If you use this class however, it is recommended not to
|
||||
instantiate it directly (like we did in the example), but to create your own
|
||||
"MyHandler" class that extends from it, and in which you also implement the
|
||||
"MyHandler" class that extends from it, and in which you also implement the
|
||||
onError() method to report possible connection errors to your end users.
|
||||
|
||||
Currently, we have example TcpHandler implementations for libev,
|
||||
|
|
@ -443,15 +457,15 @@ such examples.
|
|||
HEARTBEATS
|
||||
==========
|
||||
|
||||
The AMQP protocol supports *heartbeats*. If this heartbeat feature is enabled, the
|
||||
client and the server negotiate a heartbeat interval during connection setup, and
|
||||
they agree to send at least *some kind of data* over the connection during every
|
||||
iteration of that interval. The normal data that is sent over the connection (like
|
||||
publishing or consuming messages) is normally sufficient to keep the connection alive,
|
||||
The AMQP protocol supports *heartbeats*. If this heartbeat feature is enabled, the
|
||||
client and the server negotiate a heartbeat interval during connection setup, and
|
||||
they agree to send at least *some kind of data* over the connection during every
|
||||
iteration of that interval. The normal data that is sent over the connection (like
|
||||
publishing or consuming messages) is normally sufficient to keep the connection alive,
|
||||
but if the client or server was idle during the negotiated interval time, a dummy
|
||||
heartbeat message must be sent instead.
|
||||
|
||||
The default behavior of the AMQP-CPP library is to disable heartbeats. The
|
||||
The default behavior of the AMQP-CPP library is to disable heartbeats. The
|
||||
proposed heartbeat interval of the server during connection setup (the server
|
||||
normally suggests an interval of 60 seconds) is vetoed by the AMQP-CPP library so
|
||||
no heartbeats are ever needed to be sent over the connection. This means that you
|
||||
|
|
@ -460,7 +474,7 @@ lasting algorithms after you've consumed a message from RabbitMQ, without having
|
|||
to worry about the connection being idle for too long.
|
||||
|
||||
You can however choose to enable these heartbeats. If you want to enable heartbeats,
|
||||
simple implement the onNegotiate() method inside your ConnectionHandler or
|
||||
simple implement the onNegotiate() method inside your ConnectionHandler or
|
||||
TcpHandler class and have it return the interval that you find appropriate.
|
||||
|
||||
````c++
|
||||
|
|
@ -478,14 +492,14 @@ class MyTcpHandler : public AMQP::TcpHandler
|
|||
*/
|
||||
virtual void onNegotiate(AMQP::TcpConnection *connection, uint16_t interval)
|
||||
{
|
||||
// we accept the suggestion from the server, but if the interval is smaller
|
||||
// we accept the suggestion from the server, but if the interval is smaller
|
||||
// that one minute, we will use a one minute interval instead
|
||||
if (interval < 60) interval = 60;
|
||||
|
||||
// @todo
|
||||
// set a timer in your event loop, and make sure that you call
|
||||
// connection->heartbeat() every _interval_ seconds.
|
||||
|
||||
|
||||
// return the interval that we want to use
|
||||
return interval;
|
||||
}
|
||||
|
|
@ -503,7 +517,7 @@ In the libev event loop implementation the heartbeats are enabled by default.
|
|||
CHANNELS
|
||||
========
|
||||
|
||||
In the above example we created a channel object. A channel is a sort of virtual
|
||||
In the above example we created a channel object. A channel is a sort of virtual
|
||||
connection, and it is possible to create many channels that all use
|
||||
the same connection.
|
||||
|
||||
|
|
@ -517,8 +531,8 @@ documented.
|
|||
|
||||
All operations that you can perform on a channel are non-blocking. This means
|
||||
that it is not possible for a method (like Channel::declareExchange()) to
|
||||
immediately return 'true' or 'false'. Instead, almost every method of the Channel
|
||||
class returns an instance of the 'Deferred' class. This 'Deferred' object can be
|
||||
immediately return 'true' or 'false'. Instead, almost every method of the Channel
|
||||
class returns an instance of the 'Deferred' class. This 'Deferred' object can be
|
||||
used to install handlers that will be called in case of success or failure.
|
||||
|
||||
For example, if you call the channel.declareExchange() method, the AMQP-CPP library
|
||||
|
|
@ -610,7 +624,7 @@ channel object right after it was constructed.
|
|||
CHANNEL ERRORS
|
||||
==============
|
||||
|
||||
It is important to realize that any error that occurs on a channel,
|
||||
It is important to realize that any error that occurs on a channel,
|
||||
invalidates the entire channel, including all subsequent instructions that
|
||||
were already sent over it. This means that if you call multiple methods in a row,
|
||||
and the first method fails, all subsequent methods will not be executed either:
|
||||
|
|
@ -642,7 +656,7 @@ requires and extra instruction to be sent to the RabbitMQ server, so some extra
|
|||
bytes are sent over the network, and some additional resources in both the client
|
||||
application and the RabbitMQ server are used (although this is all very limited).
|
||||
|
||||
If possible, it is best to make use of this feature. For example, if you have an important AMQP
|
||||
If possible, it is best to make use of this feature. For example, if you have an important AMQP
|
||||
connection that you use for consuming messages, and at the same time you want
|
||||
to send another instruction to RabbitMQ (like declaring a temporary queue), it is
|
||||
best to set up a new channel for this 'declare' instruction. If the declare fails,
|
||||
|
|
@ -657,7 +671,7 @@ void myDeclareMethod(AMQP::Connection *connection)
|
|||
{
|
||||
// create temporary channel to declare a queue
|
||||
AMQP::Channel channel(connection);
|
||||
|
||||
|
||||
// declare the queue (the channel object is destructed before the
|
||||
// instruction reaches the server, but the AMQP-CPP library can deal
|
||||
// with this)
|
||||
|
|
@ -840,8 +854,8 @@ channel.commitTransaction()
|
|||
});
|
||||
````
|
||||
|
||||
Note that AMQP transactions are not as powerful as transactions that are
|
||||
knows in the database world. It is not possible to wrap all sort of
|
||||
Note that AMQP transactions are not as powerful as transactions that are
|
||||
knows in the database world. It is not possible to wrap all sort of
|
||||
operations in a transaction, they are only meaningful for publishing
|
||||
and consuming.
|
||||
|
||||
|
|
@ -992,4 +1006,3 @@ class that can be directly plugged into libev, libevent and libuv event loops.
|
|||
|
||||
For performance reasons, we need to investigate if we can limit the number of times
|
||||
an incoming or outgoing messages is copied.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue