背景
一个车联网项目,车辆的状态数据(电池,车速,里程等数据)每隔30秒上传一次到服务器A。
现将机器A上mysql的数据定时上传到机器B的接口。
可使用的方法:
1、开启定时任务crontab,定时将机器A中mysql的数据上传到机器B 接口。
2、 使用循环,但是每个循环之间使用 time.sleep(1000)。每次循环上传一定数量的数据。
代码如下:
vehicle_uploader.py
import requests
import time
import json
import hashlib
import pymysql
class VehicleUploader(object):
def __init__(self):
self.url = "http://120.178.240.24/jci/dmiot"
self.headers = {'Content-Type': 'application/json', "Accept": "text/html,application/xhtml+xml,*/*"}
self.auth_code = "43532546653"
self.account = "100001"
self.mysql_url = "ehjbd.cn"
self.user = "root"
self.passwd = "shui555629"
self.db = "vehicle_db"
self.conn = pymysql.connect(self.mysql_url, self.user, self.passwd, self.db, cursorclass=pymysql.cursors.DictCursor)
def read_data(self):
sql = "select * from vehicle_db order by create_time desc limit 1000"
cursor = self.conn.cursor()
cursor.execute(sql)
rows = cursor.fetchall()
results = []
ids = []
for row in rows:
group = {}
ids.append(row.get("id", ""))
recodrd_id = row.get("id", "")
vin = row.get("vin", "")
up_time = str(row.get("push_time", ""))
longitude = row.get("longitude", "")
latitude = row.get("latitude", "")
status = row.get("vehicle_status", "")
battery_status = row.get("battery_status", "")
soc = row.get("soc", "")
group.update({"recodrd_id": recodrd_id, "vin": vin, "up_time": up_time, "longitude":longitude,
"latitude":latitude, "status":status, "battery_status":battery_status, "soc":soc,
})
results.append(group)
print(group)
return results, ids
def update_data(self, ids):
idss = ",".join([str(i) for i in ids])
sql = "update vehicle_db set syn_flag=1 where id in ({ids})" .format(ids=idss)
print(sql)
cursor = self.conn.cursor()
cursor.execute(sql)
self.conn.commit()
def run(self):
data = {}
results, ids = self.read_data()
t = "T" + time.strftime("%Y%m%d") + "Z"
data.update({"account":self.account, "PushTime":t, "auth_code": self.auth_code, "result": results })
data = json.dumps(data)
r =requests.post(self.url, headers=self.headers, data=data)
resp = json.loads(r.text)
print(resp)
if resp["code"] == 0:
self.update_data(ids)
if __name__ == "__main__":
uploader = VehicleUploader()
uploader.run()
print("task done .....")
服务部署的时候可以加 linux crontab 任务
crontab -e
*/2 * * * * python3 vehicle_uploader.py
问题
如果使用crontab 任务, 可能会出现这样的问题,上一次启动的任务还未执行完,下一次的任务已经启动了,导致启动的任务很多, 但是完成的很少。(以上代码不一定会出现这种问题,但是其他的场景可能因为程序执行较长或者阻塞出现这种问题)。不同任务有重复执行的工作,而且占用服务器资源。
如果去掉定时任务改成循环执行,如下:
if __name__ == "__main__":
while True:
uploader = VehicleUploader()
uploader.run()
time.sleep(60)
print("task done .....")
如果由于网络或者数据库等原因导致一个循环阻塞或者执行时间较长,势必影响正常的任务执行。
解决方法
fork一个子进程执行任务,父进行可以对子进程进行管理。 当子进程执行时间过长(可能由于异常导致执行时间超过正常时间),父进程可以杀死子进程,并在下一个循环中重新开启一个子进程执行之前的任务。
代码如下:
import os
import signal # 导入signal 包
if __name__ == "__main__":
while True:
pid = os.fork() # fork 子进程
if pid == 0:
# 执行任务
uploader = VehicleUploader()
uploader.run()
else:
time.sleep(30)
os.kill(pid, signal.SIGKILL) # kill子进程
time.sleep(60)
print("task done .....")
说明: 与os.fork, os.kill, 子进程相关的资料请查看 python os 的文档。