Django微服务配置RabbitMQ与后台C++程序通信

本文详细介绍了如何配置和使用RabbitMQ进行消息队列通信,包括Django微服务如何作为生产者将任务发布到队列,以及C++后台服务如何作为消费者从队列中消费并处理任务。通过这种方式,实现了不同语言服务间的解耦合和异步处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

业务需求: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源码

最新版本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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值