django多任務開啓rabbitmq,並進行聲明隊列、發送、阻塞監聽消息

一,編寫rabbitmq基礎模塊類

1,安裝pika模塊
pip install pika

這裏需要注意的是: pika官網明確說明 pika==0.11.0版本只支持python2.6以前的版本
重點: 在下載時可以進入官網確定你的版本所需要的pika版本號。
pika官網地址https://pypi.org/project/pika/
在這裏插入圖片描述

2,實現rabbitmq基礎模塊類的編寫

這裏實現了Rabbitmq對象初始化、連接mq、發送mq消息、阻塞監聽消息並回調。
本模塊代碼作爲rabbitmq基礎模塊類,爲業務模塊調用提供方法。
對於代碼的詳解已經寫道註釋中了。

這裏的一個connection就是一個tcp連接。爲了提升tcp連接複用性,在每個連接基礎上可以建立多個channel信道,每個信道都會被指派一個唯一的 ID。同時 RabbitMQ 可以確保每個線程的私密性,就像擁有獨立的連接一樣。但考慮到如果數據量過大,會導致連接阻塞,最終這裏選擇一個connect連接只對應了一個channel信道。
關於RabbitMQ 中 Connection 和 Channel 詳解:https://www.cnblogs.com/eleven24/p/10326718.html
類似於下圖:
在這裏插入圖片描述
模塊代碼如下:

import pika
import time
import logging
logger = logging.getLogger('mydjango')
# import sys
from django.conf import settings
from retrying import retry

class RabbitmqServer(object):
    def __init__(self,username,password,serverip,port,virtual_host):
        self.username =username
        self.password = password
        self.serverip = serverip
        self.port = port
        self.virtual_host = virtual_host

    # def connent(self):
    #    for i in range(3):
    #         try:
    #             logger.info("into mq connet")
    #             user_pwd = pika.PlainCredentials(self.username,self.password)
    #             logger.info("create mq ...")
    #             logger.info("%s,%s,%s,%s,%s"%(self.virtual_host,self.serverip,self.port,self.password,self.username))
    #             s_conn = pika.BlockingConnection(pika.ConnectionParameters(virtual_host=self.virtual_host,host= self.serverip,port=self.port, credentials=user_pwd))  # 創建連接
    #             logger.info('create channel...')
    #             self.channel = s_conn.channel()
    #             logger.info('connect successful')
    #             break
    #         except Exception as e:
    #             logger.info("連接mq失敗,沉睡10s再試,共沉睡三次,失敗原因:%s",e)
    #             time.sleep(10)

    @retry(stop_max_delay=30000, wait_fixed=5000)
    def connent(self):
        logger.info("into mq connet")
        user_pwd = pika.PlainCredentials(self.username, self.password)
        logger.info("create mq ...")
        logger.info("%s,%s,%s,%s,%s" % (self.virtual_host, self.serverip, self.port, self.password, self.username))
        # 創建 mq連接
        s_conn = pika.BlockingConnection(
            pika.ConnectionParameters(virtual_host=self.virtual_host, host=self.serverip, port=self.port,
                                      credentials=user_pwd))  
        logger.info('create channel...')
        self.channel = s_conn.channel()
        logger.info('connect successful')

    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(
                            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__':   # 測試服務是否能啓動時使用
    from django.conf import settings
    # username = settings.RABBITMQCONFIG.get("username")
    # password = settings.RABBITMQCONFIG.get("password")
    # severip = settings.RABBITMQCONFIG.get("severip")
    # port = settings.RABBITMQCONFIG.get("port")
    username,password,severip,port,virtual_host = settings.PONEDITOR_RMQ_USER,settings.PONEDITOR_RMQ_PASSWD,settings.PONEDITOR_RMQ_IP,\
                                                  settings.PONEDITOR_RMQ_PORT,settings.PONEDITOR_RMQ_VIRHOST
    RabbitmqClient = RabbitmqServer(username,password,severip,port,virtual_host)

if __name__ == '__main__':
    import json
    RabbitmqClient = RabbitmqServer("root", "ssb@2019",'172.31.0.54',5673,"YuXinIBTool")
    RabbitmqClient.connent()
    data = {"code":3}
    RabbitmqClient.productMessage("test3",json.dumps(data))
    RabbitmqClient.expense("test3",callback)

二,django中多進程開啓Rabbitmq聲明隊列、發送消息、阻塞監聽

此模塊中將使用多進程,爲方便結束開啓的多個進程,這裏使用kill -15 方法刪掉進程。
kill -9 和kill -15的區別:https://www.cnblogs.com/domestique/p/8241219.html

from django.core.management.base import BaseCommand
from utils.echo_display import zip_unzip
import os
import json
import pymysql
import pandas as pd
import time
from django.conf import settings
# from utils.structure_modify import modify
from utils.annual_entrusted_modify import modify_annual
import signal
import multiprocessing
# import threading

class Command(BaseCommand):
    def handle(self, *args, **options):
        import logging
        logger = logging.getLogger('mydjango')
        from utils.Rabbitmqserver import RabbitmqClient
        from django.conf import settings
        import json

        def parse_result_func(ch, method, properties, body):
            ### 邏輯程序
            logger.info("%s start to Analytical data..." %(queue_name))
            logger.info(" [接收到的請求頭] Received %r [接收到的請求體] Received %r" % (properties.headers, body))
            try:
                req_res = json.loads(body)
                req_head = dict(properties.headers)
                project_id = str(req_res["projectId"])
                logger.info("Analytical data successful")
            except Exception as e:
                logger.error("there is a failed cause : rabbitmq parameter not correct %s"%(e) )
                logger.error("failed info -- properties : %s  body : %s"%(properties,body))
                return
            try:
                logger.info("start logical")
                finlall_docx_name = modify_annual.main(body)
                logger.info("logical successful")
            except Exception as e:
                logger.error(str(body))
                logger.info("send a reject to rabbitmq queue : %s" % (queue_name))
                # 接收到消息後會給rabbitmq發送一個拒絕
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)  
                logger.info("reject process is end")
            else:
                logger.info("send a ack to rabbitmq queue : %s" % (queue_name))
                # 接收到消息後會給rabbitmq發送一個確認
                ch.basic_ack(delivery_tag=method.delivery_tag)  
		
        def term(sig_num, addtion):
            logger.info("stop the process current pid is %s ,group id is %s" % (os.getpid(), os.getpgrp()))
            os.killpg(os.getpgid(os.getpid()), signal.SIGKILL)

        def func(args):
            arguments = {
                'x-dead-letter-exchange': "exchange.e50.oneditor",  # 延遲結束後指向交換機(死信收容交換機)
                'x-dead-letter-routing-key': "rkey.oneditor5.dlx",  # 延遲結束後指向隊列(死信收容隊列),可直接設置queue name也可以設置routing-key
            }
            logger.info("into %s sub-process pid is %s ,group id is %s"%(args,os.getpid(), os.getpgrp()))
            global queue_name
            queue_name = args
            logger.info("start to connect the RabbitmqClient")
            # 連接mq,建立connention和channel
            RabbitmqClient.connent()
            # 聲明隊列
            RabbitmqClient.channel.queue_declare(queue=args, durable=True,arguments=arguments)
            logger.info("connect the RabbitmqClient successful")
            # 調用回調函數並祖澤監聽隊列
            RabbitmqClient.expense(args, parse_result_func)

        logger.info("into listening logical --queue_listener--")
        # 接受kill -15 消息後,調用term函數
        signal.signal(signal.SIGTERM, term)
        logger.info("current pid is %s ,group id is %s" % (os.getpid(), os.getpgrp()))

		# 開啓多進程
        processes_list = []
        listenque_list = ["queue.p"+str(i)+".oneditor.docx" for i in range(10)]
        # listenque_list = ["queue.p0.oneditor.docx"]
        for listenque in listenque_list:
            t = multiprocessing.Process(target=func, args=(listenque,))
            t.daemon = True
            t.start()
            processes_list.append(t)
        for p in processes_list:
            p.join()

###########################################################
        # print("current pid is % s" % os.getpid())
        # processes_list = []
        #
        # listenque_list = ["queue.p1.oneditor.docx","queue.p2.oneditor.docx"]
        # # listenque_list = ["queue.p"+str(i)+".oneditor.docx" for i in range(2)]
        # print(listenque_list)
        # for listenque in listenque_list:
        #     t = multiprocessing.Process(target=func, args=(listenque,))
        #     t.daemon = True
        #     t.start()
        #     processes_list.append(t)
        # time.sleep(2)
        #
        # try:
        #     for p in processes_list:
        #         p.join()
        # except Exception as e:
        #     print(str(e))
##########################################################
        # t1 = threading.Thread(target=func, args=("queue.p1.oneditor.docx",))
        # t2 = threading.Thread(target=func, args=("queue.p2.oneditor.docx",))
        #
        # t1.start()
        # t2.start()

#########################################################
        # class Mythreadsend1(threading.Thread):
        #     def run(self):
        #         logger.info("start to listening the info...")
        #         RabbitmqClient.expense("queue.p1.oneditor.docx", parse_result_func)
        #
        # class Mythreadsend2(threading.Thread):
        #     def run(self):
        #         logger.info("start to listening the info...")
        #         RabbitmqClient.expense("queue.p2.oneditor.docx", parse_result_func)
        #
        # t1 = Mythreadsend1()
        # t2 = Mythreadsend2()
        # t1.start()
        # t2.start()

三,django中使用manage.py開啓獨立進程

參考如下:https://blog.csdn.net/luslin1711/article/details/87885145

1、開發時會使用django環境進行一些初始化操作,這些程序一般只執行幾次,但是需要django中的環境變量。
2、使用django運行阻塞監聽的程序,比如Rabbitmq監聽,放在主程序中就阻塞住了,需要另外開命令執行

在創建的app下創建文件夾management,在management文件夾下創建文件夾commands,將要執行的文件放到文件將愛下,記得把__init__.py文件一併創建了,init.py是聲明這個文件夾是一個包。然後在主目錄(就是manage.py文件所在目錄)執行 python manage.py 文件名即可

# 終端前臺開啓
python manage.py queue_listener

# 終端後臺開啓
nohup python manage.py queue_listener&

# 終端後臺開啓,並將打印log放入“黑洞”中
nohup python manage.py queue_listener > /dev/null 2>&1&

# 查看進程pid
ps -aux | grep python
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章