业务需求:Django的微服务作为生产者,将算法任务插入RabbitMQ队列,后台C++算法程序作为消费者,消费RabbitMQ任务队列,结束后再调用Django微服务更新任务状态。
1.RabbitMQ的安装与配置
#安装erlang
sudo apt-get install erlang-nox
#更新apt-get源
sudo apt-get update
#安装RabbitMQ
sudo apt-get install rabbitmq-server
#添加用户并设置密码
sudo rabbitmqctl add_user admin 123456
#设置用户为管理员权限
sudo rabbitmqctl set_user_tags admin administrator
#设置可以访问的虚拟路由路径'/'
sudo rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'
#启用网页管理配置插件
sudo rabbitmq-plugins enable rabbitmq_management
#使用创建用户登录网页管理页面
localhost:15672
2.Django微服务配置RabbitMQ
Django工程下的setting.py下新增RabbitMQ的配置
#RABBITMQ Configuration
RABBITMQ_USER = 'admine'
RABBITMQ_PASSWORD = '123456'
RABBITMQ_HOST = '127.0.0.1'
RABBITMQ_PORT = 5672
Django工程下的utiles.py下新增RabbitMQ的初始化以及消息发送接口
import pika
class RabbitmqServer(object):
def __init__(self, username, password, serverip, port):
self.username = username
self.password = password
self.serverip = serverip
self.port = port
def connent(self):
user_pwd = pika.PlainCredentials(self.username, self.password)
s_conn = pika.BlockingConnection(
pika.ConnectionParameters(host=self.serverip, port=self.port, credentials=user_pwd)) # 创建连接
self.channel = s_conn.channel()
def productMessage(self, queuename, message):
self.channel.queue_declare(queue=queuename, durable=True)
self.channel.basic_publish(exchange='',
routing_key=queuename, # 写明将消息发送给队列queuename
body=message, # 要发送的消息
properties=pika.BasicProperties(delivery_mode=2, )
# 设置消息持久化,将要发送的消息的属性标记为2,表示该消息要持久化
)
def expense(self, queuename, func):
"""
:param queuename: 消息队列名称
:param func: 要回调的方法名
"""
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_consume(auto_ack=False, # 设置为应答(也就是必须处理完了,才将数据从队列里面删除)
on_message_callback=func,
queue=queuename,
)
self.channel.start_consuming()
def callback(ch, method, properties, body):
print(" [消费者] Received %r" % body)
time.sleep(1)
print(" [消费者] Done")
ch.basic_ack(delivery_tag=method.delivery_tag) # 接收到消息后会给rabbitmq发送一个确认
if __name__ != '__main__':
username = settings.RABBITMQ_USER
password = settings.RABBITMQ_PASSWORD
severip = settings.RABBITMQ_HOST
port = settings.RABBITMQ_PORT
RabbitmqClient = RabbitmqServer(username, password, severip, port)
def sendtoparse_one(data):
data = json.dumps(data)
RabbitmqClient.connent()
# 往队列intelligent里插入消息
RabbitmqClient.productMessage("intelligent", data)
# 发送消息通知算法端解析
print("AduitOneParseView", "文件发送完成")
在需要进行消息转发的地方调用sendtoparse_one(data)即可
3.C++后台服务监听消息队列
下载rabbitmq-c源码
最新版本rabbitmq-c-0.11.0.tar.gz解压到本地
#源码编译
cd rabbitmq-c-0.11.0
mkdir build
cd build
cmake ..
make
../rabbitmq-c-0.11.0/build/librabbitmq/目录下得到rabbitmq-c动态库文件
打包rabbitmq-c-0.11.0/librabbitmq/目录下所有.h头文件以及编译得到的库文件
将此头文件和库文件作为第三方库导入自己的C++工程
声明自己的RabbitMQClient
#ifndef RABBITMQ_CLIENT_H_
#define RABBITMQ_CLIENT_H_
#include <string>
#include <vector>
#include <QObject>
#include "amqp_tcp_socket.h"
#include <QThread>
using std::string;
using std::vector;
class CRabbitmqClient : public QObject
{
Q_OBJECT
public:
CRabbitmqClient();
~CRabbitmqClient();
int Connect(const string &strHostname, int iPort, const string &strUser, const string &strPasswd);
int Disconnect();
bool StartConsumer(const string &strQueueName, struct timeval *timeout = NULL);
private:
CRabbitmqClient(const CRabbitmqClient & rh);
void operator=(const CRabbitmqClient & rh);
int ErrorMsg(amqp_rpc_reply_t x, char const *context);
string m_strHostname; // amqp主机
int m_iPort; // amqp端口
string m_strUser;
string m_strPasswd;
int m_iChannel;
unsigned m_delivery_tag;
string m_strQueueName;
amqp_socket_t *m_pSock;
amqp_connection_state_t m_pConn;
bool b_finished;
amqp_envelope_t m_pElope;
timeval* m_timeval;
};
#endif
#include "rabbitmqClient.h"
#include <unistd.h>
#include <QByteArray>
#include <QJsonDocument>
#include <QJsonObject>
CRabbitmqClient::CRabbitmqClient()
: m_strHostname("")
, m_iPort(0)
, m_strUser("")
, m_strPasswd("")
, m_iChannel(1) //默认用1号通道,通道无所谓
, m_pSock(NULL)
, m_pConn(NULL) {
}
CRabbitmqClient::~CRabbitmqClient() {
if (NULL != m_pConn) {
Disconnect();
m_pConn = NULL;
}
}
int CRabbitmqClient::Connect(const string &strHostname, int iPort, const string &strUser, const string &strPasswd) {
m_strHostname = strHostname;
m_iPort = iPort;
m_strUser = strUser;
m_strPasswd = strPasswd;
m_pConn = amqp_new_connection();
if (NULL == m_pConn) {
fprintf(stderr, "amqp new connection failed\n");
return -1;
}
m_pSock = amqp_tcp_socket_new(m_pConn);
if (NULL == m_pSock) {
fprintf(stderr, "amqp tcp new socket failed\n");
return -2;
}
int status = amqp_socket_open(m_pSock, m_strHostname.c_str(), m_iPort);
if (status<0) {
fprintf(stderr, "amqp socket open failed\n");
return -3;
}
// amqp_login(amqp_connection_state_t state,char const *vhost, int channel_max, int frame_max, int heartbeat, amqp_sasl_method_enum sasl_method, ..)
if (0 != ErrorMsg(amqp_login(m_pConn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, m_strUser.c_str(), m_strPasswd.c_str()), "Logging in")) {
return -4;
}
return 0;
}
int CRabbitmqClient::Disconnect() {
if (NULL != m_pConn) {
if (0 != ErrorMsg(amqp_connection_close(m_pConn, AMQP_REPLY_SUCCESS), "Closing connection"))
return -1;
if (amqp_destroy_connection(m_pConn) < 0)
return -2;
m_pConn = NULL;
}
return 0;
}
bool CRabbitmqClient::StartConsumer(const std::string &strQueueName, struct timeval *timeout)
{
if (NULL == m_pConn)
{
fprintf(stderr, "Consumer m_pConn is null, Consumer failed\n");
return false;
}
amqp_channel_open(m_pConn, m_iChannel);
if (0 != ErrorMsg(amqp_get_rpc_reply(m_pConn), "open channel"))
{
amqp_channel_close(m_pConn, m_iChannel, AMQP_REPLY_SUCCESS);
return false;
}
//手动ACK
int ack = 0; // no_ack=1 auto no_ack=0 manual 是否需要确认消息后再从队列中删除消息
amqp_bytes_t queuename= amqp_cstring_bytes(strQueueName.c_str());
amqp_basic_consume(m_pConn, m_iChannel, queuename, amqp_empty_bytes, 0, ack, 0, amqp_empty_table);
if (0 != ErrorMsg(amqp_get_rpc_reply(m_pConn), "Consuming"))
{
amqp_channel_close(m_pConn, m_iChannel, AMQP_REPLY_SUCCESS);
return false;
}
{
while(b_finished)
{
amqp_rpc_reply_t res;
amqp_envelope_t envelope;
amqp_maybe_release_buffers(m_pConn);
res = amqp_consume_message(m_pConn, &envelope, timeout, 0);
if (AMQP_RESPONSE_NORMAL != res.reply_type)
{
break;
}
printf("Delivery %u, exchange %.*s routingkey %.*s\n",
(unsigned) envelope.delivery_tag,
(int) envelope.exchange.len, (char *) envelope.exchange.bytes,
(int) envelope.routing_key.len, (char *) envelope.routing_key.bytes);
if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG) {
printf("Content-type: %.*s\n",
(int) envelope.message.properties.content_type.len,
(char *) envelope.message.properties.content_type.bytes);
}
string str((char *)envelope.message.body.bytes, (char *)envelope.message.body.bytes + envelope.message.body.len);
printf("str:%s----\n", str.c_str());
//处理取到的消息
QJsonParseError error;
QJsonDocument message = QJsonDocument::fromJson(QByteArray::fromStdString(str.c_str()), &error);
if (error.error)
{
qWarning() << "Failed to parse text message as JSON object:"
<< "Error is:" << error.errorString();
}
else if (!message.isObject())
{
qWarning() << "Received JSON message that is not an object: ";
}
QJsonObject messageObject = message.object();
m_delivery_tag = envelope.delivery_tag;
m_pElope = envelope;
m_strQueueName = strQueueName;
m_timeval = timeout;
//TODO::添加业务处理逻辑
b_finished = false;
}
}
return true;
}
int CRabbitmqClient::ErrorMsg(amqp_rpc_reply_t x, char const *context) {
switch (x.reply_type) {
case AMQP_RESPONSE_NORMAL:
return 0;
case AMQP_RESPONSE_NONE:
fprintf(stderr, "%s: missing RPC reply type!\n", context);
break;
case AMQP_RESPONSE_LIBRARY_EXCEPTION:
fprintf(stderr, "%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;
fprintf(stderr, "%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;
fprintf(stderr, "%s: server channel error %uh, message: %.*s\n",
context, m->reply_code, (int)m->reply_text.len,
(char *)m->reply_text.bytes);
break;
}
default:
fprintf(stderr, "%s: unknown server error, method id 0x%08X\n",
context, x.reply.id);
break;
}
break;
}
return -1;
}
在main函数内启动监听
#include <QApplication>
#include "rabbitmqClient.h"
int32_t main(int32_t argc, char *argv[])
{
QApplication a(argc, argv);
CRabbitmqClient objRabbitmq;
std::string strIP = "127.0.0.1";
int iPort = 5672;
std::string strUser = "admine";
std::string strPasswd = "123456";
int iRet = objRabbitmq.Connect(strIP, iPort, strUser, strPasswd);
printf("Rabbitmq Connect Ret: %d\n", iRet);
std::string strQueuename = "intelligent";
objRabbitmq.StartConsumer(strQueuename);
return a.exec();
}