背景:如果这个任务(定时或者立即)执行完毕,然后把一些执行情况生成pdf,通过邮件发送给客户。
现象:我通过celery周期查看任务的执行状态,如果任务执行完毕,异步更改任务状态,然后执行发送邮件。但是时不时会出现重复发送邮件,原因异步任务重复进入celery任务队列,导致有的任务会重复发送邮件。
解决方案:通过django的signal信号机制来监测任务状态表数据的变更,一旦任务状态变成完成,触发signal信号,然后执行发送邮件逻辑代码。(但是需要注意不要在周期之中反复更改这个状态)
要监控的表结构:
class PdfTask(models.Model):
"""pdf任务表"""
IS_CREATE_CHOICES = (
(0, u'未生成'),
(1, u'生成中'),
(2, u'已生成'),
(3, u'异常')
)
task_url = models.TextField(verbose_name='任务详情链接')
pdf_path = models.TextField(verbose_name='pdf的保存的目录')
pdf_name = models.TextField(verbose_name='pdf的名称')
foreign_key_id = models.IntegerField(verbose_name='外键的id', null=True)
is_create = models.IntegerField(verbose_name=u'是否生成', choices=IS_CREATE_CHOICES, default=0)
result = models.BooleanField(verbose_name='生成的结果', default=False)
run_num = models.IntegerField(verbose_name='执行生成的次数', default=0)
is_delete = models.IntegerField(choices=[(0, u'未删除'), (1, u'已删除')], default=0)
class Meta:
verbose_name = 'pdf任务表'
db_table = 't_pdf_task'
在这里主要监测is_create字段的变化,当组的哑巴
监测加发送邮件代码:
# 采用信号监听 能够确保邮件发送只被触发一次 PDF邮件无论是否生成正确还是不正确的
@receiver(signals.post_save, sender=PdfTask)
def inspect_instance(instance, created, **kwargs):
if not created and instance.is_create in [2, 3]:
logger.warning('第一步:触发邮件发送')
from apps.inspection_tasks.models import InspectionReport
inspection_report_objs = InspectionReport.objects.filter(id=instance.foreign_key_id)
if inspection_report_objs:
inspection_report = inspection_report_objs[0]
if inspection_report.is_send_email is False:
logger.warning('第二步:触发邮件状态更改')
inspection_report.is_send_email = True
inspection_report.save()
title = "{}巡检报告-{}".format(
inspection_report.data_center.data_center_name,
datetime.datetime.strftime(inspection_report.begin_time, '%Y%m%d')
)
content = """
亲爱的业务系统管理员:蓝鲸平台于{0}生成{2}系统巡检报告,巡检过程耗时{1}秒。请登录蓝鲸平台,在"系统巡检->巡检报告->巡检历史"中找到当日巡检任务,查看巡检报告内容详情!
""".format(
datetime.datetime.strftime(inspection_report.begin_time, '%Y%m%d %H:%M'),
inspection_report.use_time,
inspection_report.data_center.data_center_name
)
receiver = inspection_report.temp_id.receiver
logger.warning('第三步:触发邮件信息组装')
pdf_name = instance.pdf_name
pdf_path = instance.pdf_path
pwd_file = pdf_path + pdf_name
with open(pwd_file, 'rb') as f:
base64_data = base64.b64encode(f.read())
file_data = base64_data.decode()
attachments = [{
"filename": pdf_name,
"content": file_data
}]
from apps.commons.cmsi_handle import send_email
logger.warning('第四步:执行邮件信息发送')
send_email(title, content, receiver, attachments)
logger.warning('第五步:执行邮件信息发送完毕')