Backgroud
该程序是用python3开发的,基于pika【pika是AMQP协议client端的一个python实现。其他的client端的python库还有如kombu,py-amqplib等】。
发送的数据是程序内模拟产生的各类传感器监测数据,具体数据格式下面详细介绍。数据发送可以指定发送频率,【最高支持毫秒级】,不支持微秒等更高频率的,还可以配置传感器的数量。
需要注意,配置的频率越高、传感器的个数越多,数据发送频率误差越大,目前我使用的配置是300个位移传感器,频率是20HZ,200个位移传感器,频率是10HZ。
发送消息格式
模拟产生的数据是逗号分隔的字符串,例如:100,20201015170020100,1,14,1,2,3,1,-7.182,-6.868,2.397,23,-9.077,5.666,-6.547
其中加粗斜体1,23这两个代表测点编号,后面跟的三个浮点数是三个指标对应的数值。
前7位代表的含义如下所示
序号 | 缩写 | 含义 |
---|---|---|
1 | pid | 项目id |
2 | time | 时间(17位字符串)(例子:20201015170020100) |
3 | funCode | 功能码 |
4 | targetType | 测点类型 |
5 | reserve | 预留字节 |
6 | targetAmount | 测定个数 |
7 | quotaAmount | 指标个数 |
程序源码
# coding: utf-8
# import json
import _thread
import datetime
import random
import threading
import time
import pika
class RabbitMQUtil(object):
"""
构造方法:
config = {
'host': '192.168.16.156',
'port': 5672,
'vHost': 'wlf',
'username': 'wlf',
'password': '123456',
'exchange': 'sensorPayload_test',
'routingKey': 'payload'
}
"""
# 初始化配置
def __init__(self, conf):
super().__init__()
self.host = conf['host']
self.port = conf['port']
self.vHost = conf['vHost']
self.username = conf['username']
self.password = conf['password']
self.exchange = conf['exchange']
self.routingKey = conf['routingKey']
self.credential = None
self.connection = None
self.channel = None
try:
self.credential = pika.PlainCredentials(self.username, self.password)
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(self.host, self.port, self.vHost, self.credential))
self.channel = self.connection.channel()
except():
print("connect error, please check the config")
# 发布消息
def publicMsg(self, json):
# body = json.dumps({"test": "test"})
self.channel.basic_publish(exchange=self.exchange, routing_key=self.routingKey, body=json)
# 接收处理消息的回调函数
def callback(self, channel, method, properties, body):
super()
# channel.basic_ack(delivery_tag=method.delivery_tag)
print(str(body).replace('b', '').replace('\'', ''))
# 订阅消息
def consumeMsg(self, queue):
self.channel.queue_declare(queue=queue, durable=True, arguments={
"x-message-ttl": 259200000})
self.channel.basic_consume(queue, self.callback, True)
self.channel.start_consuming()
# 关闭连接
def close(self):
if not self.connection:
self.connection.close()
else:
print("connection already disconnected")
class MsgUtil(object):
"""
构造方法:
config = {
'projectId': '100',
'targetType': '1',
'targetAmount': 300,
'startSerialCode': 1,
'quotaAmount': 6,
'minValue': 1,
'maxValue': 10
}
"""
def __init__(self, conf):
self.projectId = conf['projectId']
self.targetType = conf['targetType']
self.targetAmount = conf['targetAmount']
self.startSerialCode = conf['startSerialCode']
self.quotaAmount = conf['quotaAmount']
self.minValue = conf['minValue']
self.maxValue = conf['maxValue']
self.funCode = '1'
self.reserve = '1'
self.timestamp = None
# 获取逗号分隔的字符串(项目id,时间(17位字符串),功能码,测点类型,预留字节,测定个数,指标个数,测点1id,指标1数值,指标2数值,...,测点2id,指标1数值,指标2数值,...,)
# 例子:100,20201015170020100,1,14,1,2,3,1,-7.182,-6.868,2.397,23,-9.077,5.666,-6.547
def get_msg(self):
self.timestamp = get_format_time()
base_info_seq = (self.projectId, str(self.timestamp), self.funCode, self.targetType, self.reserve,
str(self.targetAmount), str(self.quotaAmount))
val_seq = ()
for serialCode in range(self.startSerialCode, self.startSerialCode + self.targetAmount):
val_seq = val_seq + (str(serialCode),)
for i in range(1, self.quotaAmount + 1):
random_value = get_random_value(serialCode, self.minValue, self.maxValue)
val_seq = val_seq + (random_value,)
return get_comma_str(base_info_seq, val_seq)
# 自定义线程类,指定频率向 RabbitMQ 发送消息
class myThread(threading.Thread):
def __init__(self, thread_id, interval, msg_util):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.interval = interval
self.msg_util = msg_util
def run(self):
conn = get_rabbit_connection()
lastTime = 0
while True:
message = self.msg_util.get_msg()
thisTime = get_milli_now()
diffTime = thisTime - lastTime
if diffTime < self.interval:
delay = (self.interval - diffTime) / 1000
time.sleep(delay)
# 发布消息
print(message)
conn.publicMsg(message)
lastTime = get_milli_now()
# 获取获逗号分隔的字符串,参数是元素为字符串的 tuple
def get_comma_str(*params):
comma = ','
res = ()
for p in params:
res = res + p
return comma.join(res)
# 获取获取指定范围的一个随机数(字符串类型),保留三位小数
def get_random_value(n, minValue, maxValue):
# 偶数全为正数
if n % 2 == 0:
return str(round(random.uniform(minValue, maxValue), 3))
# 奇数有负数
else:
return str(round(random.uniform(maxValue * -1, maxValue), 3))
# 获取当前时间,格式:20200929173338500
def get_format_time():
now = datetime.datetime.now()
return now.strftime("%Y%m%d%H%M%S%f")[:-3]
# 指定频率向RabbitMQ发送消息
def produce_msg(*msg_conf):
# param_list [interval1, msg_util1, interval2, msg_util2, ...]
param_list = []
for conf in msg_conf:
frequency = conf['frequency']
msg_util = MsgUtil(conf)
# 发送时间间隔(ms)
interval = 1000 / frequency
param_list.append(interval)
param_list.append(msg_util)
send_msg(param_list)
# 多线程执行指定频率向RabbitMQ发送消息
def send_msg(param_list):
intervals = param_list[::2]
msg_utils = param_list[1::2]
thread_list = []
for i in range(len(intervals)):
# 把毫秒转成微秒
interval = intervals[i]
msg_util = msg_utils[i]
msg_util.get_msg()
thread = myThread(i, interval, msg_util)
thread_list.append(thread)
# 先把所有线程启动
for thread in thread_list:
thread.start()
# 然后再加入主线程中运行
for thread in thread_list:
thread.join()
# 获取 RabbitMQ 连接
def get_rabbit_connection():
# 定义 RabbitMQ 配置参数
rmq_config = {
'host': 'elephant',
'port': 5672,
'vHost': 'wlf',
'username': 'wlf',
'password': '123456',
'exchange': 'sensorPayload_test',
'routingKey': 'payload'
}
return RabbitMQUtil(rmq_config)
# 获取13位当前毫秒时间戳,格式:1602747077320
def get_milli_now():
return round(datetime.datetime.now().timestamp() * 1000)
if __name__ == '__main__':
# 定义发布消息的基本信息
msg_conf_1 = {
'projectId': '100',
'targetType': '1',
'targetAmount': 300,
'startSerialCode': 1,
'quotaAmount': 6,
'minValue': 1,
'maxValue': 10,
'frequency': 20
}
msg_conf_2 = {
'projectId': '100',
'targetType': '14',
'targetAmount': 200,
'startSerialCode': 501,
'quotaAmount': 5,
'minValue': 1,
'maxValue': 6,
'frequency': 10
}
produce_msg(msg_conf_1, msg_conf_2)
- 打印信息如下图所示: