Rabbitmq-C 的简单使用(一) -- 准备工作和一个demo

参考博客文章

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全吃透了,然后写完这篇文章,对我没有意义。我要记录的是不光是成品,还要记录过程就像一篇日记。

        当然,我也是参考着前辈们的积累来学习,只能说我写的这些仅仅一个搜集和验证的过程,但这带给我的成长也是巨大的。

        有知识点拿不准的地方,我会指出,不想误导大家,而且请大家带着质疑的态度来看这篇文章,如果有高手能指出我的问题那真是感激不尽。

Rabbitmq-C文件的准备

       今天搞了一个小demo,最简单的那种,就是给rabbitmq代理发送消息。这样的东西简直不值一提,所以我准备记一下  lib 的调用。

       当我用cmake编译成功的时候,目录中出现了下面的几个文件。

Rabbitmq-C 的简单使用(一) -- 准备工作和一个demo_第1张图片

         注意生成了两个.lib文件,其实我也不知道为什么这两个库的区别(高手可以在下面评论一下二者的区别)

     LIBS += -L./ -lrabbitmq.4        LIBS += -L./ -llibrabbitmq.4

        接着就是添加文件了,其实编译完是有很多的头文件,我按着例子写,目前就是用了四个,utils.h这个文件里面提供了一些辅助的函数,比如输出报错什么的,记得加上它对应的.c文件

Rabbitmq-C 的简单使用(一) -- 准备工作和一个demo_第2张图片

         目前这样就是可以编译成功了,然后去下载的文件中找一个人家写好的例子,先复制到自己这里,先看看能不能成,能不能想mq中发送消息。目前就是先做到了可以成功编译,并且能与Rabbitmq连通的样子,这是最最基础的,另外建议rabbitmq还是安装在linxu虚拟机上比较好,一般现实中也是这么用。

         其实这里面好多函数的参数还不清楚,底下还是要看一下对参数的说明,又是一大坨的英文,慢慢看吧。

Demo的雏形

       在这里,我附上一个自己根据官方例程用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 = "<

解决Demo中阻塞的问题

   上面的例子中,消费者消费消息有不如人意的地方,队列中有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这个结构体参数,日后遇到再补充吧。

        今天就先写到这里吧,从目前的学习程度上来说,起码在消费消息上不再阻塞了,这对我来说是一个不小的进步,至于性能什么的,我完全没有考虑。再后面的话,需要学习一下消息队列的运行机制了,否则仅仅看头文件也看不懂了。

2022-11-20 更新

        上面笔记中提到的,librabbitmq.4.lib 和 rabbitmq.4.lib,我又看了一下,当时是自己才疏学浅,以为动态库就是dll,静态库就是.lib。

        librabbitmq.4.lib是静态库,而 rabbitmq.4.lib是动态库的导入库。其实从文件体积大小上可以区分,但是要有说法的区分,可以使用VS命令行区分。

        参考博客  快速判断lib文件是静态库还是导入库 - 乘于时 - 博客园

        如下图,这是动态库导入库

Rabbitmq-C 的简单使用(一) -- 准备工作和一个demo_第3张图片

       

        如下图,这是纯纯的静态库

Rabbitmq-C 的简单使用(一) -- 准备工作和一个demo_第4张图片

 

你可能感兴趣的:(Rabbitmq-C,rabbitmq,c语言)