一,编写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.youkuaiyun.com/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