update README file to explain the Tcp module

This commit is contained in:
Emiel Bruijntjes 2015-11-01 10:43:45 +01:00
parent d41423d5db
commit 54c2c27a73
1 changed files with 180 additions and 35 deletions

215
README.md
View File

@ -5,16 +5,23 @@ 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 library can be used to parse incoming data from a RabbitMQ server, and to
generate frames that can be sent to a RabbitMQ server. generate frames that can be sent to a RabbitMQ server.
Unlike all other AMQP libraries, this AMQP-CPP library does not make a connection to This library has a layered architecture, and allows you - if you like - to
RabbitMQ by itself, nor does it create sockets and/or performs IO operations. As completely take care of the network layer. If you want to set up and manage the
a user of this library, you first need to set up a socket connection network connections yourself, the AMQP-CPP library will not make a connection to
to RabbitMQ by yourself, and implement a certain interface that you pass to the RabbitMQ by itself, nor will it create sockets and/or perform IO operations. As
AMQP-CPP library and that the library will use for IO operations. 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.
This architecture makes the library extremely flexible: it does not rely on Intercepting the network layer is optional, the AMQP-CPP library also comes
operating system specific IO calls, and it can be easily integrated into any with a predefined Tcp module that can be used if you trust the AMQP library to
event loop. It is fully asynchronous and does not do any blocking (system) calls, take care of the network handling.
so it can be used in high performance applications without the need for threads.
This architecture makes the library extremely flexible: it does not necessarily
rely on operating system specific IO calls, and it can be easily integrated into
any kind of event loop. It 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. sure that your compiler is up-to-date and supports C++11.
@ -43,12 +50,13 @@ Then check out our other commercial and open source solutions:
HOW TO USE AMQP-CPP HOW TO USE AMQP-CPP
=================== ===================
As we mentioned above, the library does not do any IO by itself, and you need As we mentioned above, the library can be used in a network-agnostic fashion.
to pass an object to the library that the library can use for that. So, before It then does not do any IO by itself, and you need to pass an object to the
you can even start using the library, you first you need to create a class that library that the library can use for IO. So, before you can even start using the
extends from the ConnectionHandler base class. This is a class library, you first you need to create a class that extends from the
with a number of methods that are called by the library every time it wants ConnectionHandler base class. This is a class with a number of methods that are
to send out data, or when it needs to inform you that an error occured. called by the library every time it wants to send out data, or when it needs to
inform you that an error occured.
````c++ ````c++
#include <amqpcpp.h> #include <amqpcpp.h>
@ -155,20 +163,22 @@ PARSING INCOMING DATA
The ConnectionHandler class has a method onData() that is called by the library The ConnectionHandler class has a method onData() that is called by the library
every time that it wants to send out data. We've explained that it is up to you to every time that it wants to send out data. We've explained that it is up to you to
implement that method. But what about data in the other direction? How does the implement that method. Inside your ConnectionHandler::data() method, you probably
make a call to 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? library receive data back from RabbitMQ?
The AMQP-CPP library does not do any IO by itself and it is therefore of course In this raw setup, the AMQP-CPP library does not do any IO by itself and it is
also not possible for the library to receive data from a socket. It is again up therefore of course also not possible for the library to receive data from a
to you to do this. If, for example, you notice in your event loop that the socket socket. It is again up to you to do this. If, for example, you notice in your
that is connected with the RabbitMQ server becomes readable, you should read out event loop that the socket that is connected with the RabbitMQ server becomes
that socket (for example by using the recv() system call), and pass the received readable, you should read out that socket (for example by using the recv() system
bytes to the AMQP-CPP library. This is done by calling the parse() method in the call), and pass the received bytes to the AMQP-CPP library. This is done by
Connection object. calling the parse() method in the Connection object.
The Connection::parse() method gets two parameters, a pointer to a buffer of The Connection::parse() method gets two parameters, a pointer to a buffer of
data received from RabbitMQ, and a parameter that holds the size of this buffer. data that you just read from the socket, and a parameter that holds the size of
The code snippet below comes from the Connection.h C++ header file. this buffer. The code snippet below comes from the Connection.h C++ header file.
````c++ ````c++
/** /**
@ -201,12 +211,126 @@ make a new call to parse() when more data is available, with a buffer that conta
both the old data, and the new data. both the old data, and the new data.
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
TCP module that takes care of setting up the network connections, and sending
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 you do need to to
create 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++
#include <amqpcpp.h>
class MyTcpHandler : public AMQP::TcpHandler
{
/**
* Method that is called by the AMQP library when the login attempt
* succeeded. After this method has been called, the connection is ready
* to use.
* @param connection The connection that can now be used
*/
virtual void onConnected(AMQP::TcpConnection *connection)
{
// @todo
// add your own implementation, for example by creating a channel
// instance, and start publishing or consuming
}
/**
* Method that is called by the AMQP library when a fatal error occurs
* on the connection, for example because data received from RabbitMQ
* could not be recognized.
* @param connection The connection on which the error occured
* @param message A human readable error message
*/
virtual void onError(AMQP::TcpConnection *connection, const char *message)
{
// @todo
// add your own implementation, for example by reporting the error
// to the user of your program, log the error, and destruct the
// connection object because it is no longer in a usable state
}
/**
* Method that is called when the connection was closed. This is the
* counter part of a call to Connection::close() and it confirms that the
* connection was correctly closed.
*
* @param connection The connection that was closed and that is now unusable
*/
virtual void onClosed(AMQP::TcpConnection *connection) {}
/**
* Method that is called by the AMQP-CPP library when it wants to interact
* with the main event loop. The AMQP-CPP library is completely non-blocking,
* and only make "write()" or "read()" system calls when it knows in advance
* that these calls will not block. To register a filedescriptor in the
* event loop, it calls this "monitor()" method with a filedescriptor and
* flags telling whether the filedescriptor should be checked for reaability
* or writability.
*
* @param connection The connection that wants to interact with the event loop
* @param fd The filedescriptor that should be checked
* @param flags Bitwise or of AMQP::readable and/or AMQP::writable
*/
virtual void monitor(AMQP::TcpConnection *connection, int fd, int flags)
{
// @todo
// add your own implementation, for example by adding the file
// 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
// connection->process(fd, flags) method.
}
};
````
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. However, be aware that the TCP module of the
AMQP-CPP library does make its own system calls, and for resolving domain names
in a non-blocking way, it even starts up a thread.
The example that we gave above, looks slightly different if you make use of
the TCP module:
````c++
// create an instance of your own tcp handler
MyTcpHandler myHandler;
// address of the server
AMQP::Address address("amqp://guest:guest@localhost/vhost");
// create a AMQP connection object
AMQP::TcpConnection connection(&myHandler, address);
// and create a channel
AMQP::TcpChannel channel(&connection);
// use the channel object to call the AMQP method you like
channel.declareExchange("my-exchange", AMQP::fanout);
channel.declareQueue("my-queue");
channel.bindQueue("my-exchange", "my-queue");
````
CHANNELS CHANNELS
======== ========
In the example we created a channel object. A channel is a virtual connection over In the above example we created a channel object. A channel is a sort of virtual
a single TCP connection, and it is possible to create many channels that all use connection, and it is possible to create many channels that all use
the same TCP connection. the same connection.
AMQP instructions are always sent over a channel, so before you can send the first AMQP instructions are always sent over a channel, so before you can send the first
command to the RabbitMQ server, you first need a channel object. The channel command to the RabbitMQ server, you first need a channel object. The channel
@ -216,11 +340,11 @@ and to publish and consume messages. You can best take a look at the channel.h
C++ header file for a list of all available methods. Every method in it is well C++ header file for a list of all available methods. Every method in it is well
documented. documented.
The constructor of the Channel object accepts one parameter: the connection object. All operations that you can perform on a channel are non-blocking. This means
Unlike the connection it does not accept a handler. Instead of a handler object, that it is not possible for a method (like Channel::declareExchange()) to
(almost) every method of the Channel class returns an instance of the 'Deferred' immediately return 'true' or 'false'. Instead, almost every method of the Channel
class. This object can be used to install handlers that will be called in case class returns an instance of the 'Deferred' class. This 'Deferred' object can be
of success or failure. 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 For example, if you call the channel.declareExchange() method, the AMQP-CPP library
will send a message to the RabbitMQ message broker to ask it to declare the will send a message to the RabbitMQ message broker to ask it to declare the
@ -236,7 +360,7 @@ To prevent any blocking calls, the channel.declareExchange() method returns a
called when the operation succeeds or fails. called when the operation succeeds or fails.
````c++ ````c++
// create a channel // create a channel (or use TcpChannel if you're using the Tcp module)
Channel myChannel(&connection); Channel myChannel(&connection);
// declare an exchange, and install callbacks for success and failure // declare an exchange, and install callbacks for success and failure
@ -283,7 +407,7 @@ method, you can also install a callback to be notified when the channel is ready
for sending the first instruction to RabbitMQ. for sending the first instruction to RabbitMQ.
````c++ ````c++
// create a channel // create a channel (or use TcpChannel if you use the Tcp module)
Channel myChannel(&connection); Channel myChannel(&connection);
// install a generic channel-error handler that will be called for every // install a generic channel-error handler that will be called for every
@ -343,6 +467,27 @@ 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 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). application and the RabbitMQ server are used (although this is all very limited).
If you can, 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), you
can set up a new channel to send this 'declare' instruction. If the declare fails,
it will not stop the consumer, because it was sent over a different channel.
The AMQP-CPP library allows you to create channels on the stack, because it is not
a problem if a channel object gets destructed before the instruction was received by
the RabbitMQ server:
````c++
void myDeclareMethod(AMQP::Connection *connection)
{
// create temporary channel to declare a queue
AMQP::Channel channel(connection);
// declare the queue
channel.declareQueue("my-new-queue");
}
````
FLAGS AND TABLES FLAGS AND TABLES
================ ================