我想更改我的GAE应用逻辑并开始使用任务队列发送电子邮件。
目前我有一个cron作业,每15分钟运行一次并读取要从数据存储区发送的消息:
class SendMessagesHandler(webapp2.RequestHandler):
def get(self):
emails_quota_exceeded = models.get_system_value('emails_quota_exceeded')
if emails_quota_exceeded == 0 or emails_quota_exceeded == None:
messages = models.get_emails_queue()
for message in messages:
try:
...
email.send()
models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent
except apiproxy_errors.OverQuotaError, error_message:
models.set_system_value(what='emails_quota_exceeded', val=1)
logging.warning('E-mails quota exceeded for today: %s' % error_message)
break
else:
logging.info('Free quota to send e-mails is exceeded')
如果我使用任务队列,那么我会得到类似的东西:
for message in messages:
taskqueue.add(url='/sendmsg', payload=message)
在这种情况下,相同的消息可能会被发送两次(甚至更多次) - 例如,如果它尚未发送,但是第二次执行了cron作业。 如果我在将消息添加到队列后立即更新电子邮件状态:
for message in messages:
taskqueue.add(url='/sendmsg', payload=message)
models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent
然后可能永远不会发送消息。例如,如果在发送电子邮件期间发生异常。了解该任务将被重试,但如果今天超过配额,则重试将无济于事。
我想我也可以在尝试发送之前重新读取任务队列中每条消息的状态,但这会花费额外的读取操作。
处理它的最佳方法是什么?
答案 0 :(得分:3)
为您的任务命名包括key.id()
将阻止其发送两次:
task_name = ''.join(['myemail-', str(mykey)])
try:
taskqueue.Task(
url="/someURL/send-single-email",
name=task_name,
method="POST",
params={
"subject": subject,
"body": body,
"to": to,
"from": from }
).add(queue_name="mail-queue")
except:
pass #throws TombstonedTaskError(InvalidTaskError) if tombstoned name used.
有时您可能希望为具有相同密钥的邮件发送后续电子邮件。因此,我建议在任务名称中添加date
或datetime
戳记。这将允许您稍后发送相同密钥的其他消息:
task_name = ''.join(['myemail-', str(mykey), str(datetime.utcnow()-timedelta(hours=8))]).translate(string.maketrans('.:_ ', '----'))