突然心血来潮,想给自己的小站(小站仍在建设)增加一个发送邮件的功能,方便当用户回复的时候能够通过通过邮件进行通知。
一开始打算直接通过调用web.py的mail模块来完成(前面有介绍),但是想到mail的发送会使页面卡住了,这样感觉不是很好,所以就想了下面的方法:
首先创建一张发送邮件的任务表,把发生邮件的相关信息保存进去,然后添加一个Task Queue,这样的话就可以让SAE的任务队列自己去调用发送邮件的页面。为了保险起见,再在Cron中添加一个定时任务,通过定时任务来检查任务表中是否还存在没有发送的邮件,有的话就发送。
其实我测试的结果是Task Queue还是比较可靠的,我测试了40封邮件,40封都能正常发送。
上代码吧,首先是邮件任务表相关的东西:
# -*- coding: UTF-8 -*
'''
SinMailTask.py
Created on 2012-12-6
@author: RobinTang
'''
import config;
import web
import time
from sae import taskqueue
mailtasktable = 'tb_mailtasks'
inittableable = False
class MailTask:
"""邮件任务对象"""
def __init__(self, mtid = None, from_address = None, to_address = None, subject = None, message = None, status = 0, addtime = None, sendtime = None):
if addtime == None:
addtime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
self.id = mtid # id
self.from_address = from_address # 来自地址
self.to_address = to_address # 目的地址
self.subject = subject # 邮件主题
self.message = message # 邮件内容
self.addtime = addtime # 创建任务时间
self.sendtime = sendtime # 发送时间
self.status = status # 状态, 0:未发送 1:已发送
def initMailTaskTable():
"""初始化邮件任务表, 第一次运行的时候调用,主要完成任务表的创建"""
if not inittableable:
raise Exception('can\'t init table!')
drop_sql = 'DROP TABLE IF EXISTS `%s`'%mailtasktable
create_sql = 'CREATE TABLE `%s` (`id` int(11) NOT NULL AUTO_INCREMENT,`from_address` varchar(256) NOT NULL,`to_address` varchar(256) NOT NULL,`subject` varchar(1024) NOT NULL,`message` text NOT NULL,`status` int(11) NOT NULL DEFAULT 0,`addtime` datetime DEFAULT NULL,`sendtime` datetime DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8'%mailtasktable
config.db.query(drop_sql)
config.db.query(create_sql)
def addMailTask(mailtask):
"""添加一个邮件任务,无须直接调用"""
config.db.insert(mailtasktable,
from_address=mailtask.from_address,
to_address=mailtask.to_address,
subject=mailtask.subject,
message=mailtask.message,
status=mailtask.status,
addtime=mailtask.addtime)
def updateMailTask(mailtask):
"""修改邮件任务,无需直接调用"""
config.db.update(mailtasktable,
where = "id = %d"%mailtask.id,
from_address=mailtask.from_address,
to_address=mailtask.to_address,
subject=mailtask.subject,
message=mailtask.message,
status=mailtask.status,
addtime=mailtask.addtime,
sendtime=mailtask.sendtime)
def delMailTask(mailtask):
"""删除一个邮件任务,在发送之后将该任务删除,无需直接调用"""
mailtask.status = 1
mailtask.sendtime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
updateMailTask(mailtask)
def getMailTask():
"""获取一个未发送的邮件任务,无需直接调用"""
itms = config.db.select(mailtasktable, None, where="status=0", limit=1).list()
mt = None
if len(itms)>0:
itm = itms[0]
mt = MailTask(mtid = itm.id,
from_address = itm.from_address,
to_address = itm.to_address,
subject = itm.subject,
message = itm.message,
status = itm.status,
addtime = itm.addtime,
sendtime= itm.sendtime)
return mt
def getMailTaskCount(where = '`status` = 0'):
"""获取邮件任务数,默认参数是获取未被执行的邮件任务数"""
sql = "select count(*) as `count` from `%s` where %s"%(mailtasktable, where)
res = config.db.query(sql)
return res[0].count
def doMailTask():
"""执行任务,也就是处理一个邮件任务,一般是用过cron来调用"""
mt = getMailTask()
if mt != None:
delMailTask(mt)
web.sendmail(mt.from_address, mt.to_address, mt.subject, mt.message)
return mt
def sendMail(from_address = None, to_address = None, subject = None, message = None):
"""发送邮件,发送任务将会被添加到,调用之前你需要配置好webpy的邮件部分,之后通过调用doMailTask来完成发送"""
mailtask = MailTask()
mailtask.from_address = from_address
mailtask.to_address = to_address
mailtask.subject = subject
mailtask.message = message
addMailTask(mailtask)
taskqueue.add_task('mailtask', '/domailtask') # 添加任务
"""
说明:
第一次首先调用initMailTaskTable()来初始化任务表,调用之后会自动的创建相关的表,表名通过mailtasktable来设置。
创建完成之后一般应该设置inittableable为False,以免以后不小心被再次初始化了。
通过sendMail()来添加邮件任务,需要提的是参数中from_address在使用gmail邮件服务器时没有用,所以一般设置成你的gmail邮件地址。
调用sendMail()会自动的在任务队列中增加一个任务
如果不放心的话,可以在cron任务表中添加定时调用diMailTask()来执行邮件任务,每次能够发送一封,cron的周期根据自己的需要进行设置。
"""
初始的时候记得调用初始化方法:
SinMailTask.initMailTaskTable()
增加调用发送任务的处理:
class DoMailTask:
def POST(self):
self.GET(self)
def GET(self):
web.header('Content-Type', 'text/html; charset=UTF-8')
SinMailTask.doMailTask()
count = SinMailTask.getMailTaskCount();
return "<html><head></head><body>%d</body></html>"%count
然后在url中添加下面这行:
'/domailtask', 'DoMailTask',
在SAE对应的应用中增加一个队列,名称为“mailtask”,和SinMailTask中添加队列任务的时候的名称一样。
好了,那么以后就可以通过调用http://appname.sinaapp.com/domailtask来调用发送任务了。
如果你不放心还想在Cron中也添加定时任务的话就添加吧:
cron:
- description: domailtask
url: domailtask
schedule: every 5 mins
timezone: Beijing
时间不用太短的,之前提过 Task Queue还是比较可靠的。
以后你只需要在代码中这样就可以发送邮件了(不过拜托,你可别真这样写,我可不想收到你的测试邮件~_- ):
SinMailTask.sendMail('trbbadboy@gmail.com', 'trbbadboy@qq.com', '测试邮件', '来自bybreeze')
之后发送邮件的事情就交给Task Queue或者Cron(Task Queue漏网的就给Cron了),你可以写一个查看邮件任务表的页面方便你观察。
想要讲的就这些,后面是一些讨论。
其实一开始(昨天)我都是打算使用Cron来完成的,今天早上上SAE的时候发现了Task Queue了,发现可以用它来实现我的需求,所以就改成Task Queue为主了。
需Task Queue和Cron的左右基本上是一样的(如果你细心的话你会发现Python的文档中这两个东西是放到一块介绍的),都是通过调用url来完成一些任务。
不同的是Task Queue是来完成一些零散无规律的任务调用,比如这次的邮件任务,因为什么时候有邮件是一个不确定的事情。
而Cron则是用来完成一些规律性的调用,比如你么个小时刷新一下你的抓取任务。
正因为这样,所以这次使用了Task Queue来做主要的调用。
再讲点Task Queue,事实上我们甚至可以不用创建任务表的,因为Task Queue允许我们指定Post的数据,只要我们把发送邮件的相关信息作为Task Queue的Post数据就行的。刚刚提到,一开始没看到Task Queue,所以自己做了邮件任务表,不过没觉得走了弯路,因为通过邮件任务表我们可以详细的记录邮件的发送情况,包括何事创建,何时被发送等等。
就讲这么多了,喜欢 SAE、喜欢Python、喜欢code。