目录
RabbitMQ-C使用开发详解(Windows环境)
一、概述
讨论的是windows环境下的使用RabbitMQ-c与RabbitMQ服务端的交互。
二、编译RabbitMQ-c
RabbitMq的C/C++客户端有很多,我们选用RabbitMq-c。windows环境下的MFC开发,需要把RabbitMq-c客户端编译成dll。
1.下载和安装
下载rabbitmq-c最新代码包:https://github.com/alanxz/rabbitmq-c
下载cmake最新安装包:https://cmake.org/download/
2.使用cmake编译生成适合自己编译环境的工程
第一步:填写源代码路径
第二步:填写建立后的路径,build的文件夹一般建立在源代码路径里,也可以放在其他位置
第三步:点击配置按钮,在配置里面选择属于自己编译环境的名字
第四步:点击生成按钮,不出现运行失败就说明已经编译成功了
特别说明:在编译rabbitmq-c是如果出现如图的错误,可以去掉ENABLE_SSL_SUPPORT括号里的对勾。
在以上生成的工程目录下的librabbitmq\Debug路径下会生成librabbitmq.4.lib、librabbitmq.4.dll两个文件,对应的动态库的导出文件在rabbitmq-c-master\librabbitmq目录下。
三、核心原理
3.1生产者与交换机关系
生产者与交换机的关系是多对多的有关系,多个生产者可以给同一个交换机生产消息,同时一个生产者也可以能多个交换机生产消息。
3.2交换机与队列关系
交换机与队列的关系是多对多的关系,一个交换机可以给多个队列提供消息,同时多个交换机也可以同时给一个队列提供消息。
3.3队列与消费者关系
队列与消费者的关系是多对多的关系,一个队列可以同时被多个消费者消费,同时一个消费者可以同时消费多个队列的消息。
3.4交换机与交换机的关系
交换机与交换机的关系,与交换机与队列的关系是一样的。部份交换机充当队列的角色,从其绑定的交换机上分流数据,然后再把自己角色转换成交换机,然后给绑定在自身上的队列分派消息。
所以交换机之间的关系是多对多,一个上级交换机可以绑定多个下级交换机,一个下级交换机可同时绑定多个上级交换机。
四、开发者
4.1接口文件
总共有4个导出文件:
amqp.h:主要的rabbitmq-c客户端接口都在此文件
amqp_tcp_socket.h:与socket相关的接口
mqp_framing.h:不常用的一些接口
amqp_ssl_socket.h:用户ssl方式加密访问rabbitmq-server
生产者生产消息过程:
(1)客户端连接到消息队列服务器,打开一个channel。
(2)客户端声明一个exchange,并设置相关属性。
(3)客户端声明一个queue,并设置相关属性。
(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
(5)客户端投递消息到exchange。
RabbitMQ支持消息的持久化:
也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。消息队列持久化包括3个部分:
(1)exchange持久化,在声明时指定durable => 1
(2)queue持久化,在声明时指定durable => 1
(3)消息持久化,在投递时指定delivery_mode=> 2(1是非持久化)
4.2交换机
声明交换机
AMQP_PUBLIC_FUNCTION amqp_exchange_declare_ok_t *AMQP_CALL amqp_exchange_declare(amqp_connection_state_t state, amqp_channel_t channel,amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive,amqp_boolean_t durable, amqp_boolean_t auto_delete, amqp_boolean_t internal,amqp_table_t arguments);
/**
* amqp_exchange_declare
*
* @param [in] connect连接 amqp_new_connection获取
* @param [in] channel the channel to do the RPC on,程序自己设置一个通道号,一个连接可以多个通道号。
* @param [in] exchange 指定交换机名称 eg:amqp_cstring_bytes("exchange_cat")
* @param [in] type 指定交换机类型,amqp_cstring_bytes("direct")
* "fanout" 广播的方式,发送到该exchange的所有队列上。
* "direct" 通过路由键发送到指定的队列上。
* "topic" 通过匹配路由键的方式获取,使用通配符*,#
* @param [in] passive 检测exchange是否存在,设为true,若exchange存在则命令成功返回(调用其他参数不会影响exchange属性),若不存在不会创建exchange,返回错误。设为false,如果exchange不存在则创建exchange,调用成功返回。如果exchange已经存在,并且匹配现在exchange的话则成功返回,如果不匹配则exchange声明失败。
* @param [in] durable 队列是否持久化
* @param [in] auto_delete 连接断开的时候,exchange是否自动删除
* @param [in] internal internal
* @param [in] arguments arguments
* @returns amqp_exchange_declare_ok_t
*/
Demo示例:
#include <amqp.h>
#include <amqp_tcp_socket.h>
void die_on_amqp_error2(amqp_rpc_reply_t x, char const *context) {
char sLog[1024] = {0};
switch (x.reply_type) {
case AMQP_RESPONSE_NORMAL:
return;
case AMQP_RESPONSE_NONE:
printf(sLog, "%s: missing RPC reply type!\n", context);
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
printf(sLog, "%s: %s\n", context, amqp_error_string2(x.library_error));
break;
case AMQP_RESPONSE_SERVER_EXCEPTION:
switch (x.reply.id) {
case AMQP_CONNECTION_CLOSE_METHOD: {
amqp_connection_close_t *m =
(amqp_connection_close_t *)x.reply.decoded;
printf(sLog, "%s: server connection error %uh, message: %.*s\n",
context, m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
case AMQP_CHANNEL_CLOSE_METHOD: {
amqp_channel_close_t *m = (amqp_channel_close_t *)x.reply.decoded;
printf(sLog, "%s: server channel error %uh, message: %.*s\n",
context, m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
default:
printf(sLog, "%s: unknown server error, method id 0x%08X\n",
context, x.reply.id);
break;
}
break;
}
AfxMessageBox(sLog);
}
void die_on_error2(int x, char const *context) {
if (x < 0) {
char sLog[1024] = {0};
printf(sLog, "%s: %s\n", context, amqp_error_string2(x));
AfxMessageBox(sLog);