RabbitMQ--rabbitmq-c-master学习心得_慢慢飞的笨笨的博客-CSDN博客 参考此篇文章可以使用cmake编译出 lib文件和dll文件 感谢博主
在Windows下创建简单的mqtt客户端_洛阳鱼紫怡的博客-CSDN博客 这篇是我自己使用mqtt客户端写的,里面有使用cmake的步骤
Clients Libraries and Developer Tools — RabbitMQ 各种语言的Rabbitmq接口
rabbitmq-c接口参数说明文档_Hopoxipo的博客-CSDN博客 rabbitmq-c的参数解释文档 感谢博主
rabbitmq-c api详细说明_小慧慧_的博客-CSDN博客 rabbitmq-c api详细说明 感谢博主
上班排查公司的bug,最后发现是客户端接收mq的错误,还是别的同事帮忙检查的,原谅我是一只小白。
听着他们说着Rabbitmq,我是一脸懵逼,于是回家开始了解它,不求精通,只求下回交流时能够跟上他们的思路。去哪学? 我选择B站,但是学到使用的时候,发现讲的是java,没有C/C++的接口(应该是叫接口吧),不知道为什么。
还好吧,github上有能用的,起码能让我写写小demo,知道里面的一些细节什么的,这就够了。
这系列的文章我会持续的写一段时间,没有想着一口把Rabbitmq全吃透了,然后写完这篇文章,对我没有意义。我要记录的是不光是成品,还要记录过程就像一篇日记。
当然,我也是参考着前辈们的积累来学习,只能说我写的这些仅仅一个搜集和验证的过程,但这带给我的成长也是巨大的。
有知识点拿不准的地方,我会指出,不想误导大家,而且请大家带着质疑的态度来看这篇文章,如果有高手能指出我的问题那真是感激不尽。
今天搞了一个小demo,最简单的那种,就是给rabbitmq代理发送消息。这样的东西简直不值一提,所以我准备记一下 lib 的调用。
当我用cmake编译成功的时候,目录中出现了下面的几个文件。
注意生成了两个.lib文件,其实我也不知道为什么这两个库的区别(高手可以在下面评论一下二者的区别)
LIBS += -L./ -lrabbitmq.4 LIBS += -L./ -llibrabbitmq.4
接着就是添加文件了,其实编译完是有很多的头文件,我按着例子写,目前就是用了四个,utils.h这个文件里面提供了一些辅助的函数,比如输出报错什么的,记得加上它对应的.c文件
目前这样就是可以编译成功了,然后去下载的文件中找一个人家写好的例子,先复制到自己这里,先看看能不能成,能不能想mq中发送消息。目前就是先做到了可以成功编译,并且能与Rabbitmq连通的样子,这是最最基础的,另外建议rabbitmq还是安装在linxu虚拟机上比较好,一般现实中也是这么用。
其实这里面好多函数的参数还不清楚,底下还是要看一下对参数的说明,又是一大坨的英文,慢慢看吧。
在这里,我附上一个自己根据官方例程用qt写的小demo,很简陋,能够像代理发送消息,然后再向代理接收消息,仅仅是实现了这个功能,其他什么返回值判断什么的都没有特别去写,就是提供一个现成的东西供和我一样的初学者玩一玩,当然官方文档里面也是有例子。后续我还会继续的研究rabbitmq,而且还要和自己的工作结合起来。
char const *hostname = "192.168.0.104"; //主机ip 注意修改
int port = 5672; //端口
char const *messagebody = "12345678"; //发送的消息
char const *queue = "myque"; //队列名称 注意要确保你的代理中 / 的虚拟主机下有 myque这个队列
amqp_socket_t *socket = NULL;
amqp_connection_state_t conn;
char const * key = ""; //路由秘钥,不需要就填空
//登录代理步骤
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket)
{
qDebug()<<"amqp_tcp_socket_new error";
}
int status;
status = amqp_socket_open(socket, hostname, port);
if (status)
{
qDebug()<<"amqp_socket_open error";
}
die_on_amqp_error(amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN,"guest", "guest"),"Logging in");
amqp_channel_open(conn, 1);
die_on_amqp_error(amqp_get_rpc_reply(conn), "Opening channel");
//将队列绑定到交换机上
amqp_queue_bind(conn,1,amqp_cstring_bytes("myque"),amqp_cstring_bytes("myexc"),amqp_cstring_bytes(key),amqp_empty_table);
//发送消息 接收消息
for(int i = 0; i < 300; i++)
{
//消息属性的设置
amqp_basic_properties_t props;
props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;
props.content_type = amqp_cstring_bytes("text/plain");
props.delivery_mode = 2; /* persistent delivery mode */
//发布消息
int res = amqp_basic_publish(conn, 1,amqp_cstring_bytes("myexc"),amqp_cstring_bytes(key), 0, 0,&props, amqp_cstring_bytes(messagebody));
qDebug()<<"send msg,and res = "<
上面的例子中,消费者消费消息有不如人意的地方,队列中有30条消息,我本想是一次读一条消息,但是上面的例子是一次取走所有的消息然后一个一个再输出,这样的话,很多模式都无法实现,比如能者多劳什么的。
RabbitMQ笔记-使用rabbitmq-c实现Fair dispatch(公平分发)_IT1995的博客-CSDN博客 感谢博主
在学习上面这篇博客之后,知道了有另外两个函数可以接受消息
/**
* Do a basic.get
*
* Synchonously polls the broker for a message in a queue, and
* retrieves the message if a message is in the queue.
*/
amqp_rpc_reply_t amqp_basic_get(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_bytes_t queue,
amqp_boolean_t no_ack); //这个是消息确认,分为自动确认和手动确认的,这涉及消息确认机制,而且需要根据实际情况来选择
/**
* Reads the next message on a channel
*
* Reads a complete message (header + body) on a specified channel. This
* function is intended to be used with amqp_basic_get() or when an
* AMQP_BASIC_DELIVERY_METHOD method is received.
*/
amqp_rpc_reply_t amqp_read_message(amqp_connection_state_t state,
amqp_channel_t channel,
amqp_message_t *message,
int flags);
通过这两个函数可以实现一次读取一个数据,先调用amqp_basic_get再调用amqp_read_message即可。下面是我在上个例子基础上的升级,仅粘贴部分代码。
for(int i = 0; i < 31; i++)
{
qDebug()<<"============================";
amqp_message_t *msg = new amqp_message_t;
do
{
res_get = amqp_basic_get(conn,channel,amqp_cstring_bytes(queue.toStdString().data()),true);
if(AMQP_BASIC_GET_EMPTY_METHOD == res_get.reply.id)
{
qDebug()<<"the queue is empty......";
}
QThread::msleep(100);
}
while(AMQP_BASIC_GET_EMPTY_METHOD == res_get.reply.id || AMQP_RESPONSE_NORMAL != res_get.reply_type);
res_read = amqp_read_message(conn, channel, msg, 0);
if(res_read.reply_type != AMQP_RESPONSE_NORMAL)
{
qDebug()<<"read message error......";
}
else
{
qDebug() <<"i = "<
我自己在写的过程中,发现amqp_basic_get 和 amqp_read_message函数似乎没有等待超时的设置(不知道是不是自己没找到),而且获取队列的消息数量我也没找到,感觉自己弱的一匹。这样就会有问题,我不能知道队列信息的数量,也没有等待超时的设置,那就剩下阻塞了,这肯定不行啊。然后我在网上逛,发现可以用返回值来做判断的,说实话,这些返回值里面的参数对我来说好复杂(可能是我不了解rabbitmq的机制,所以一些参数读起来不知道是干嘛的)。
下面就来说一说这个返回值,准确的说是 amqp_rpc_reply_t 类型的返回值。
typedef struct amqp_rpc_reply_t_ {
amqp_response_type_enum reply_type; /**< the reply type:
* - AMQP_RESPONSE_NORMAL - the RPC completed successfully
* - AMQP_RESPONSE_SERVER_EXCEPTION - the broker returned an exception, check the reply field
* - AMQP_RESPONSE_LIBRARY_EXCEPTION - the library encountered an error, check the library_error field*/
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 */
} amqp_rpc_reply_t;
其实这个类型是一个结构体,太可怕了,rabbitmq-c这个样的结构体很多,不过看多了就麻木了,下面我们简单了解一下这里面的东西。
amqp_response_type_enum 可以看出来这应该是一个枚举(其实就是枚举),里面枚举了几种回复类型,比较简单。
amqp_method_t 又是一个结构体,从字面意思来说是一个方法什么的。(方法这个翻译我感觉很不合适,应该有更合理的解释)下面列出这个结构体。
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_method_number_t 这个类型的变量(至于另一个以后碰到再补充吧),代理会在AMQP_RESPONSE_SERVER_EXCEPTION异常的情况下(依据注释的翻译),将此字段设置成一个方法。(其实我感觉这个 "方法" 翻译成状态也不错,当客户端向代理发送一个命令后,代理会给客户端一个执行后状态) 我找了找头文件,里面大约有60多个状态,不过大多数是连接状态呀,通道打开状态呀,这一类的。下面列出的三个就是就是此处要用到的几个状态。在上面的例子中在通过do while 返回状态(method)是否为AMQP_BASIC_GET_EMPTY_METHOD来判断队列中是否还有消息,这样起码不会阻塞(也只是简单的完成了不阻塞的功能)
需要说明的一点,这里判断的是amqp_basic_get函数的返回值,虽然amqp_read_message函数返回类型也是amqp_rpc_reply_t,但是似乎没有类似read_message_method这样的状态定义(也可能是我没有找到),所以还是判断前者的返回值比较可靠。
#define AMQP_BASIC_GET_METHOD
#define AMQP_BASIC_GET_OK_METHOD
#define AMQP_BASIC_GET_EMPTY_METHOD
至于 int library_error这个结构体参数,日后遇到再补充吧。
今天就先写到这里吧,从目前的学习程度上来说,起码在消费消息上不再阻塞了,这对我来说是一个不小的进步,至于性能什么的,我完全没有考虑。再后面的话,需要学习一下消息队列的运行机制了,否则仅仅看头文件也看不懂了。
上面笔记中提到的,librabbitmq.4.lib 和 rabbitmq.4.lib,我又看了一下,当时是自己才疏学浅,以为动态库就是dll,静态库就是.lib。
librabbitmq.4.lib是静态库,而 rabbitmq.4.lib是动态库的导入库。其实从文件体积大小上可以区分,但是要有说法的区分,可以使用VS命令行区分。
参考博客 快速判断lib文件是静态库还是导入库 - 乘于时 - 博客园
如下图,这是动态库导入库
如下图,这是纯纯的静态库