上一版demo的缺陷
虽然是在接收信息上不会产生阻塞了,但是依旧有一点问题。主要是没有timeout的设置,而且对于 delivery_tag 参数,我也没有在 amqp_read_message 和 amqp_basic_get 中找到相关可以获取的方法(如果是我没有找到的话,请原谅我:-))。
总之现在我不想使用这两个函数了,在一番摸索之后,又找到了几个可以替代的函数。下面一起来看看。
新发现函数的介绍
amqp_basic_qos_ok_t *AMQP_CALL amqp_basic_qos(amqp_connection_state_t state,
amqp_channel_t channel,
uint32_t prefetch_size, //预取消息的字节数
uint16_t prefetch_count, //预取消息的数量
amqp_boolean_t global);
/** basic.qos-ok method fields */
typedef struct amqp_basic_qos_ok_t_ {
char dummy; /**< Dummy field to avoid empty struct */
} amqp_basic_qos_ok_t;
global这个变量还不太清楚,看样子是设置这个basic_qos属性是否为全局的意思吧,一般填true问题不大。这个函数重要之处在于可以设置读取信息的数量,一般都是一次取一条,再操作一条方便管理。
而且这个函数返回值判定我也没搞懂,有高手可以为我解解惑。
amqp_basic_consume_ok_t *AMQP_CALL amqp_basic_consume(
amqp_connection_state_t state,
amqp_channel_t channel,
amqp_bytes_t queue,
amqp_bytes_t consumer_tag,
amqp_boolean_t no_local,
amqp_boolean_t no_ack, //是否自动应答
amqp_boolean_t exclusive,
amqp_table_t arguments); //扩展参数目前填amqp_empty_table
exclusive和no_local 参数还不太清楚,但是不影响整体使用,一般填false。 no_ack很重要,这是是否自动应答的设置,当消费者收到消息后,并向队列ack后,队列会将此消息删除,也就是如果消费者在处理消息时宕机了又重启了,那么要使用的消息已经丢失了。所以一般都是处理完成业务之后再向队列ack。这个函数只是对消费的一些基础设置。
amqp_rpc_reply_t amqp_consume_message(amqp_connection_state_t state,
amqp_envelope_t *envelope, //信封变量
const struct timeval *timeout, //超时
int flags);
flags不清楚,一般填0就行。在上面两个函数被调用后,此时可以使用这个函数进行读取了,可以看到参数中有超时设置,还有一个信封变量。
信封变量其实是一个结构体,这里面有很多参数,主要用到的是 uint64_t delivery_tag; 和 amqp_message_t message; 参数。
int amqp_basic_ack(amqp_connection_state_t state,
amqp_channel_t channel,
uint64_t delivery_tag, //消息进行传递时的标签
amqp_boolean_t multiple); //一般传false,一条一条的ack
最后这个函数就是ack函数了,队列收到ack信号后,才会将信息删除,否则会一直留在队列中。
Rabbitmq-c接收消息的方法总结
经过一段时间的探索,我发现接收消息的方法是有两种类型的,而且接收信息这部分是很麻烦的并且要注意的很多。
https://blog.csdn.net/tom06/article/details/52982821 , 受此篇文章的提醒(感谢博主)
(目前是这么理解的,如果下文有错请高手指出)
有两种接收消息的方式,主动获取、等待推送。
主动获取的方法就是 amqp_basic_get 、amqp_read_message,根据amqp_basic_get的设置还可能需要 amqp_basic_ack来辅助一下,每次使用都是先用amqp_basic_get,然后amqp_read_message,最后amqp_basic_get。
amqp_rpc_reply_t AMQP_CALL amqp_basic_get(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_bytes_t queue,
amqp_boolean_t no_ack);
amqp_rpc_reply_t AMQP_CALL amqp_read_message(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_message_t *message,
int flags);
int AMQP_CALL amqp_basic_ack(amqp_connection_state_t state,
amqp_channel_t channel, uint64_t delivery_tag,
amqp_boolean_t multiple);
另一种等待推送的就是上面 amqp_basic_qos、amqp_basic_consume、amqp_consume_message、amqp_basic_ack这些函数了,不过amqp_basic_qos和amqp_basic_consume调用一次就可以了,以后获取数据直接使用amqp_consume_message就行了。
这里说一下自己使用时的心得(如果说错,请大佬及时指正),在使用等待推送的函数时,我是不能获取队列中消息的个数的,就是使用amqp_basic_consume函数在这里一直等,有消息了我就拿出来,而使用主动获取的函数,就是amqp_basic_get 可以获取队列中消息的个数的。
那用什么获取队列消息的个数呢,就是 amqp_rpc_reply_t 这个结构体,刚开始的时候,我也不明白怎么用,看了好多文章和测试,才逐渐了解。下面简单说一下成员变量。
typedef struct amqp_rpc_reply_t_ {
amqp_response_type_enum reply_type;
amqp_method_t reply;
int library_error;
} amqp_rpc_reply_t;
reply_type是代理响应的类型,就四种,首先要判断这个值,然后再更加不同的值进行对应的操作。
library_error 就是一个错误码,可以使用 amqp_error_string 来获取一个字符串形式的错误原因,不过文档是写的是推荐使用 amqp_error_string2函数。
reply就是我获取队列消息所要用的变量,从下面的解释中大概能知道,此变量就是存储方法的id号,以及一些有关方法的信息,用一个void* 类型存储,说明是要转换为不同的类型。
typedef struct amqp_method_t_ {
amqp_method_number_t id; /**< the method id number */
void *decoded; /**< pointer to the decoded method,cast to the appropriate type to use */
} amqp_method_t;
注意,在amqp_rpc_reply_t的结构体中有这样两段注释,大概的意思是当出现 AMQP_RESPONSE_SERVER_EXCEPTION 异常情况时 reply 变量会被设置,而出现AMQP_RESPONSE_LIBRARY_EXCEPTION 异常情况时 library_error会被设置。但是下面我的例子是在响应正常情况下的,我还是使用了reply这个变量。我猜测这可能是提醒使用者在出错的时候可以依靠这两个变量来排查问题,而非只能在异常情况下使用。
amqp_method_t reply; /**< in case of AMQP_RESPONSE_SERVER_EXCEPTION this field will be set to the method returned from the broker */ int library_error; /**< in case of AMQP_RESPONSE_LIBRARY_EXCEPTION this field will be set to an error code. An error string can be retrieved using amqp_error_string */
当然我们还需要知道void * decoded需要转换成什么类型,就是转换成类似下面这样的类型,amqp_framing.h文件中有很多这样的结构体,以及对应的宏定义数字,具体如何使用,下面我将举一个例子。
#define AMQP_BASIC_GET_OK_METHOD ((amqp_method_number_t)0x003C0047)
/** basic.get-ok method fields */
typedef struct amqp_basic_get_ok_t_ {
uint64_t delivery_tag; /**< delivery-tag */
amqp_boolean_t redelivered; /**< redelivered */
amqp_bytes_t exchange; /**< exchange */
amqp_bytes_t routing_key; /**< routing-key */
uint32_t message_count; /**< message-count */
} amqp_basic_get_ok_t;
例子:
我使用amqp_basic_get函数获取信息,在判断响应类型是AMQP_RESPONSE_NORMAL时,我开始判断 amqp_method_t reply 变量中的id是不是等于 AMQP_BASIC_GET_OK_METHOD,如果等于,那么void *decoded变量就可以转换为amqp_basic_get_ok_t类型并获取其中的信息,而这信息中就有message_count
当然,这仅是使用 amqp_basic_get函数所对应的一个例子,如果有函数的返回类型是amqp_rpc_reply_t,那么应该都可以这么用的。
amqp_rpc_reply_t temp = amqp_basic_get(m_Conn,m_Channel, amqp_cstring_bytes(m_QueName.c_str()),true);
if(temp.reply_type == AMQP_RESPONSE_NORMAL)
{
if(AMQP_BASIC_GET_OK_METHOD == temp.reply.id)
{
qDebug()<<((amqp_basic_get_ok_t *)temp.reply.decoded)->message_count; //输出队列中的消息个数
}
}