Updated README
This commit is contained in:
parent
d2c17869e0
commit
3348e2881c
170
README.md
170
README.md
|
|
@ -182,15 +182,10 @@ 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 two parameters: the connection object,
|
The constructor of the Channel object accepts one parameter: the connection object.
|
||||||
and a pointer to a ChannelHandler object. In the example we did
|
Unlike the connection it does not accept a handler. Instead of this (almost) every
|
||||||
not use this ChannelHandler object. However, in normal circumstances, you should
|
function in the channel returns a Deferred object. This deferred object can be used
|
||||||
always pass a pointer to a ChannelHandler object every time you construct a channel.
|
to install handlers to be called in case of success, failure or in either case.
|
||||||
|
|
||||||
Just like the ConnectionHandler class, the ChannelHandler class is a base class that
|
|
||||||
you can extend to override the virtual methods you need. The AMQP library
|
|
||||||
will call these methods to inform you that an operation on the channel has succeeded
|
|
||||||
or has failed.
|
|
||||||
|
|
||||||
For example, if you call the channel.declareQueue() method, the AMQP-CPP library will
|
For example, if you call the channel.declareQueue() method, the AMQP-CPP library will
|
||||||
send a message to the RabbitMQ message broker to ask it to declare the
|
send a message to the RabbitMQ message broker to ask it to declare the
|
||||||
|
|
@ -198,8 +193,19 @@ queue. However, because all operations in the library are asynchronous, the
|
||||||
declareQueue() method immediately returns 'true', although it is at that time
|
declareQueue() method immediately returns 'true', although it is at that time
|
||||||
not yet known whether the queue was correctly declared. Only after a while,
|
not yet known whether the queue was correctly declared. Only after a while,
|
||||||
after the instruction has reached the server, and the confirmation from the server
|
after the instruction has reached the server, and the confirmation from the server
|
||||||
has been sent back to the client, your ChannelHandler::onQueueDeclared()
|
has been sent back to the client, your handler method will be called to inform
|
||||||
method will be called to inform you that the operation was succesful.
|
you that the operation was succesful.
|
||||||
|
|
||||||
|
````c++
|
||||||
|
Channel myChannel(&connection);
|
||||||
|
myChannel.declareQueue("my-queue")
|
||||||
|
.onSuccess([](AMQP::Channel *channel, const std::string& name, uint32_t messageCount, uint32_t consumerCount) {
|
||||||
|
// by now the queue is created
|
||||||
|
})
|
||||||
|
.onError([](AMQP::Channel *channel, const std::string message) {
|
||||||
|
// something went wrong creating the channel
|
||||||
|
});
|
||||||
|
````
|
||||||
|
|
||||||
It is important to realize that any error that occurs on a channel, will
|
It is important to realize that any error that occurs on a channel, will
|
||||||
invalidate the entire channel,. including all subsequent instructions that
|
invalidate the entire channel,. including all subsequent instructions that
|
||||||
|
|
@ -212,7 +218,7 @@ myChannel.declareQueue("my-queue");
|
||||||
myChannel.declareExchange("my-exchange");
|
myChannel.declareExchange("my-exchange");
|
||||||
````
|
````
|
||||||
|
|
||||||
If the first declareQueue() call fails in the example above, your ChannelHandler::onError()
|
If the first declareQueue() call fails in the example above, your Deferred::onError()
|
||||||
method will be called after a while to report this failure. And although the
|
method will be called after a while to report this failure. And although the
|
||||||
second instruction to declare an exchange has already been sent to the server, it will be
|
second instruction to declare an exchange has already been sent to the server, it will be
|
||||||
ignored because the channel was already in an invalid state after the first failure.
|
ignored because the channel was already in an invalid state after the first failure.
|
||||||
|
|
@ -223,7 +229,7 @@ You can overcome this by using multiple channels:
|
||||||
Channel channel1(connection, &myHandler);
|
Channel channel1(connection, &myHandler);
|
||||||
Channel channel2(connection, &myHandler);
|
Channel channel2(connection, &myHandler);
|
||||||
channel1.declareQueue("my-queue");
|
channel1.declareQueue("my-queue");
|
||||||
channel2.declareQueue("my-exchange");
|
channel2.declareExchange("my-exchange");
|
||||||
````
|
````
|
||||||
|
|
||||||
Now, if an error occurs with declaring the queue, it will not have
|
Now, if an error occurs with declaring the queue, it will not have
|
||||||
|
|
@ -233,51 +239,6 @@ RabbitMQ server, so some extra bytes are sent over the network,
|
||||||
and some additional resources in both the client application and the
|
and some additional resources in both the client application and the
|
||||||
RabbitMQ server are used (although this is all very limited).
|
RabbitMQ server are used (although this is all very limited).
|
||||||
|
|
||||||
Let's get back to the ChannelHandler class. It has many methods that you can
|
|
||||||
implement - all of which are optional. All methods in it have a default empty implementation,
|
|
||||||
so you can choose to only override the ones that you are interested in. When you're
|
|
||||||
writing a consumer application for example, you probably are only interested in
|
|
||||||
errors that occur, and in incoming messages:
|
|
||||||
|
|
||||||
````c++
|
|
||||||
#include <amqpcpp.h>
|
|
||||||
|
|
||||||
class MyChannelHandler : public AMQP::ChannelHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Method that is called when an error occurs on the channel, and
|
|
||||||
* the channel ends up in an error state
|
|
||||||
* @param channel the channel on which the error occured
|
|
||||||
* @param message human readable error message
|
|
||||||
*/
|
|
||||||
virtual void onError(AMQP::Channel *channel, const std::string &message)
|
|
||||||
{
|
|
||||||
// @todo
|
|
||||||
// do something with the error message (like reporting it to the end-user)
|
|
||||||
// and destruct the channel object because it now no longer is usable
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that is called when a message has been received on a channel
|
|
||||||
* This message will be called for every message that is received after
|
|
||||||
* you started consuming. Make sure you acknowledge the messages when its
|
|
||||||
* safe to remove them from RabbitMQ (unless you set no-ack option when you
|
|
||||||
* started the consumer)
|
|
||||||
* @param channel the channel on which the consumer was started
|
|
||||||
* @param message the consumed message
|
|
||||||
* @param deliveryTag the delivery tag, you need this to acknowledge the message
|
|
||||||
* @param consumerTag the consumer identifier that was used to retrieve this message
|
|
||||||
* @param redelivered is this a redelivered message?
|
|
||||||
*/
|
|
||||||
virtual void onReceived(AMQP::Channel *channel, const AMQP::Message &message, uint64_t deliveryTag, const std::string &consumerTag, bool redelivered)
|
|
||||||
{
|
|
||||||
// @todo
|
|
||||||
// do something with the incoming message
|
|
||||||
}
|
|
||||||
};
|
|
||||||
````
|
|
||||||
|
|
||||||
FLAGS AND TABLES
|
FLAGS AND TABLES
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
@ -304,12 +265,12 @@ tables are used by many methods.
|
||||||
* @param flags combination of flags
|
* @param flags combination of flags
|
||||||
* @param arguments optional arguments
|
* @param arguments optional arguments
|
||||||
*/
|
*/
|
||||||
bool declareQueue(const std::string &name, int flags, const AMQP::Table &arguments);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(const std::string &name, int flags, const Table &arguments);
|
||||||
bool declareQueue(const std::string &name, const AMQP::Table &arguments);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(const std::string &name, const Table &arguments);
|
||||||
bool declareQueue(const std::string &name, int flags = 0);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(const std::string &name, int flags = 0);
|
||||||
bool declareQueue(int flags, const AMQP::Table &arguments);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(int flags, const Table &arguments);
|
||||||
bool declareQueue(const AMQP::Table &arguments);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(const Table &arguments);
|
||||||
bool declareQueue(int flags = 0);
|
Deferred<const std::string&, uint32_t, uint32_t>& declareQueue(int flags = 0);
|
||||||
````
|
````
|
||||||
|
|
||||||
As you can see, the method comes in many forms, and it is up to you to choose
|
As you can see, the method comes in many forms, and it is up to you to choose
|
||||||
|
|
@ -396,18 +357,29 @@ bool publish(const std::string &exchange, const std::string &routingKey, int fla
|
||||||
bool publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size);
|
bool publish(const std::string &exchange, const std::string &routingKey, const char *message, size_t size);
|
||||||
````
|
````
|
||||||
|
|
||||||
Published messages are normally not confirmed by the server, hence there is no
|
Published messages are normally not confirmed by the server, therefore the publish method does not
|
||||||
ChannelHandler::onPublished() method that you can implement to find out if
|
return a deferred. That's by design in the AMQP protocol, to not unnecessarily slow down message
|
||||||
a message was correctly received by the server. That's by design in the
|
publishing. As long as no error is reported via the ChannelHandler::onError() method, you can safely
|
||||||
AMQP protocol, to not unnecessarily slow down message publishing. As long
|
|
||||||
as no error is reported via the ChannelHandler::onError() method, you can safely
|
|
||||||
assume that your messages were delivered.
|
assume that your messages were delivered.
|
||||||
|
|
||||||
If you use the flags parameter to set either the option 'mandatory' or
|
This can of course be a problem when you are publishing many messages. If you get an error halfway through
|
||||||
'immediate', a message that could not be routed or directly delivered to a consumer
|
there is no way to know for sure how many messages made it to the broker and how many should be republished.
|
||||||
is sent back to the client, and ends up in the ChannelHandler::onReturned()
|
If this is important, you can wrap the publish commands inside a transaction. In this case, if an error occurs,
|
||||||
method. At the time of this writing however, the 'immediate' option does not
|
the transaction is automatically rolled back by RabbitMQ and none of the messages are actually published.
|
||||||
seem to be supported by RabbitMQ.
|
|
||||||
|
````c++
|
||||||
|
channel.startTransaction();
|
||||||
|
channel.publish("my-exchange", "my-key", "my first message");
|
||||||
|
channel.publish("my-exchange", "my-key", "another message");
|
||||||
|
channel.commitTransaction()
|
||||||
|
.onSuccess([](AMQP::Channel *channel) {
|
||||||
|
// all messages were successfully published
|
||||||
|
})
|
||||||
|
.onError([](AMQP::Channel *channel) {
|
||||||
|
// none of the messages were published
|
||||||
|
// now we have to do it all over again
|
||||||
|
});
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
CONSUMING MESSAGES
|
CONSUMING MESSAGES
|
||||||
|
|
@ -454,45 +426,28 @@ The full documentation from the C++ Channel.h headerfile looks like this:
|
||||||
* @param arguments additional arguments
|
* @param arguments additional arguments
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
bool consume(const std::string &queue, const std::string &tag, int flags, const AMQP::Table &arguments);
|
DeferredConsumer& consume(const std::string &queue, const std::string &tag, int flags, const AMQP::Table &arguments);
|
||||||
bool consume(const std::string &queue, const std::string &tag, int flags = 0);
|
DeferredConsumer& consume(const std::string &queue, const std::string &tag, int flags = 0);
|
||||||
bool consume(const std::string &queue, const std::string &tag, const AMQP::Table &arguments);
|
DeferredConsumer& consume(const std::string &queue, const std::string &tag, const AMQP::Table &arguments);
|
||||||
bool consume(const std::string &queue, int flags, const AMQP::Table &arguments);
|
DeferredConsumer& consume(const std::string &queue, int flags, const AMQP::Table &arguments);
|
||||||
bool consume(const std::string &queue, int flags = 0);
|
DeferredConsumer& consume(const std::string &queue, int flags = 0);
|
||||||
bool consume(const std::string &queue, const AMQP::Table &arguments);
|
DeferredConsumer& consume(const std::string &queue, const AMQP::Table &arguments);
|
||||||
````
|
````
|
||||||
|
|
||||||
In your ChannelHandler you can override the onConsumerStarted() method, that will be
|
As you can see, the consume method returns a DeferredConsumer. This object is a regular Deferred, with the
|
||||||
first called before any messages are sent to you. Most users choose not to override this
|
addition of the onReceived method. This method can be used to retrieve incoming messages after consumption
|
||||||
method, because there is not much useful to do in it. After the consumer has started, however,
|
has begun.
|
||||||
messages are starting to be sent from RabbitMQ to your client application, and they are all
|
|
||||||
passed to the ChannelHandler::onReceived() method. This method is thus very important to implement.
|
|
||||||
|
|
||||||
````c++
|
````c++
|
||||||
class MyChannelHandler : public AMQP::ChannelHandler
|
channel.consume("my-queue").onReceived([](AMQP::Channel *channel, const AMQP::Message &message, uint64_t deliveryTag, const std::string &consumerTag, bool redelivered) {
|
||||||
{
|
// @todo
|
||||||
/**
|
// add your own processing
|
||||||
* Method that is called when a message has been received on a channel
|
|
||||||
* This message will be called for every message that is received after
|
|
||||||
* you started consuming. Make sure you acknowledge the messages when its
|
|
||||||
* safe to remove them from RabbitMQ (unless you set no-ack option when you
|
|
||||||
* started the consumer)
|
|
||||||
* @param channel the channel on which the consumer was started
|
|
||||||
* @param message the consumed message
|
|
||||||
* @param deliveryTag the delivery tag, you need this to acknowledge the message
|
|
||||||
* @param consumerTag the consumer identifier that was used to retrieve this message
|
|
||||||
* @param redelivered is this a redelivered message?
|
|
||||||
*/
|
|
||||||
virtual void onReceived(AMQP::Channel *channel, const AMQP::Message &message, uint64_t deliveryTag, const std::string &consumerTag, bool redelivered)
|
|
||||||
{
|
|
||||||
// @todo
|
|
||||||
// add your own processing
|
|
||||||
|
|
||||||
|
|
||||||
// after the message was processed, acknowledge it
|
// after the message was processed, acknowledge it
|
||||||
channel->ack(deliveryTag);
|
channel->ack(deliveryTag);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
````
|
````
|
||||||
|
|
||||||
The Message object holds all information of the delivered message: the actual content,
|
The Message object holds all information of the delivered message: the actual content,
|
||||||
|
|
@ -534,6 +489,7 @@ need additional attention:
|
||||||
- ability to set up secure connections (or is this fully done on the IO level)
|
- ability to set up secure connections (or is this fully done on the IO level)
|
||||||
- login with other protocols than login/password
|
- login with other protocols than login/password
|
||||||
- publish confirms
|
- publish confirms
|
||||||
|
- returned messages
|
||||||
|
|
||||||
We also need to add more safety checks so that strange or invalid data from
|
We also need to add more safety checks so that strange or invalid data from
|
||||||
RabbitMQ does not break the library (although in reality RabbitMQ only sends
|
RabbitMQ does not break the library (although in reality RabbitMQ only sends
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue