现在的位置: 首页 > 综合 > 正文

SAE web.py-借助gmail实现我们自己的邮件服务器

2013年10月09日 ⁄ 综合 ⁄ 共 4822字 ⁄ 字号 评论关闭

突然心血来潮,想给自己的小站(小站仍在建设)增加一个发送邮件的功能,方便当用户回复的时候能够通过通过邮件进行通知。

一开始打算直接通过调用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。

抱歉!评论已关闭.