Gracece's Blog

生命不息,折腾不止。

发一封邮件有多难

2月份部署的第三方教务系统运转良好,师兄们虽然没有完全写完,但是功能基本可用了,使用体验还算不错,最主要的是 解决了非IE浏览器无法查询成绩的问题。转眼一学期过去,期末考结束,又到了学霸们煎熬等待出成绩的时候。这时候可以看到访问量是以非常猛的势头往上涨:

baidu tongji

深深地感觉学霸们不容易,一天要来查好几次成绩。其实这时候我只有两门课程而且已经出了一门的成绩了,但是怀着“不作死就不会死,我就是要作死”的伟大思想, 我觉得还是要折腾一下出成绩自动通知机制。

基本逻辑

教务系统是不可能主动给你推送成绩更新的,所以能做的就是模拟用户对教务系统进行请求,比较已经出成绩的科目数目是否有变化,当有变化的时候寻找合适的方式 通知用户。这里的模拟,现在看来有两种方式,一种是直接保存用户的密码,在合适的时候替用户去请求教务系统查成绩。这样的好处是请求频率比较友好, 对服务器(包括我的服务器和教务系统)的压力都比较小,但是风险在于保存用户的密码实在是吃力不讨好的事情。另外一种就显得暴力一些,替用户维护cookie的生命期, 在cookie过期之前向教务系统发起请求,保证cookie不死掉。这样的话,就需要每二十分钟就请求一轮教务系统,且如果教务系统意外重启导致所有cookie全部失效, 那用户侧的成本也比较高。

评估之后,决定使用后面一种方式,人肉维护cookie再发请求。

1.用户登记邮件地址。
2.拿到用户的[JSESSIONID],加上其他相关信息存到数据库。
3.crontab 20分钟执行一次扫描脚本,如果已出成绩数目有增加则通知用户。

折腾过程

其实这整个过程都没有什么难点,纯粹是体力活。关于科目数量变化这一块,之前由于教务系统返回的json太奇葩,有些有双引号有些没有双引号,所有基本都API将得到的输出直接吐到前台, 由js直接处理教务系统返回的json。现在需要用python来分析,有点头痛。后来机智地发现其实返回结果中间关于成绩的那段json看起来还是符合json标准的, 且刚好有中括号包围着,所以直接用正则抠出来就ok了!

上面那个问题解决之后,就剩下最大的问题:如何通知用户?

认真想了一下,在互联网高度发达的2014年,我等三无人员有可操作性的通知方式还是最历史悠久的邮件,其他的像微信、短信或者其他看起来都没有什么可能性。 在作为用户的时候,确实是不希望收到任何莫名其妙的推送的,这点微信一直在控制,公众账号不给主动推送,都没什么兴趣玩了。作为开发者,没有其他可用的通知方式了,发邮件就发邮件吧。

Round ①

单纯的我,注册了一个163邮箱就上路了。逻辑没什么难的,半个下午就把第一个可用版本搞出来了,测试通过,邮件能够发出来,没想太多就上线了。没多久就有约200 用户登记了邮件,然后就发现搞不动了。开始成功了一些,后来基本都被QQ邮箱退回来了。这里有个bug是我在用户登记的时候默认科目数量为0,想及时发一封邮件 给用户刷刷存在感(其实就是懒得调一次API查用户已经出成绩的科目),所以基本用户登记之后在下一轮crontab调用脚步时候就会尝试发一封邮件。基本上都是这样的: qq

Round ②

既然有问题那就解决问题,先上hack的方法,人肉注册了几个分身邮箱(网易邮箱的中文验证码好恶心),准备发邮件的时候随机选一个邮件来发邮件。测试了一下好像又 可以了,上线,然后发现发多了又不行了。看了一下,这次是网易邮箱直接就不给我发了,提示(rejected by system.)。尝试把上面说的bug改好,登记邮件的时候先 调用一次api查一下已出科目再插入到数据库,降低发信频率,结果还是不行,看来这条路是走不通了。满屏幕的退信看着好爽: 163

从前面两轮的过程看,教务系统那边应该暂时不用担心有人工干预ban我的IP,且人肉维护cookie的策略是可行的,除了代码没写好,没写异常控制,在某个地方抛出异常 之后脚步就挂了,那个晚上死掉了六十个cookie。之前写的脚本貌似没有考虑过鲁棒性(这个名词好专业),所以也很少用try excpet语句。现在看来对于你无法完全 保证100%可靠的语句,都应该加上异常控制,保证代码跑到某个循环中出问题之后还能继续进行后面的循环。

Round ③

既然别人家的不行,那就自己来吧,反正有域名有服务器。试着自己建立Postfix服务器,自己来发邮件试试。教程可以参考这里, 搭建Postfix还是比较简单的,很快就能发出邮件给自己了,倒是搞不定pop3和imap。尝试一天用洛杉矶的VPS来完成请求教务系统再发邮件,情况有所好转,邮件能发出一些了。 但是毕竟在大洋彼岸,网络延迟略高。所以曲线救国用flask建个http服务算了,反正也只有自己用,改回校园网的服务器请求教务系统,用http发邮件。之后又是 小插曲不断,比如json里面有反斜线又触发异常把脚本搞挂了…比如依旧触发了QQ邮箱的各种频率限制。自己的服务器什么都好解决,搜了个英文名列表,全部alias给自己,发的时候从这几百个名字里面随机选一个。 另外是邮件内容本身貌似进入了垃圾邮件样本列表,各种提示发布过去,基本上外域入信失败原因这里的失败 原因都遇到过了。

Round ④

折腾不动了,让用户自己加域名白名单去。搞定了QQ邮箱,貌似又进了Outlook、hotmail、live.cn的黑名单,随缘了。

总结

这就是一次作死的过程,白天实习晚上十点回来继续折腾发邮件,这么断断续续搞了一星期,真是不容易。不过折腾中也有蛮多收获的,至少可以水出一篇博客来。 很巧的是,在V2EX上也见到了各种对发邮件的讨论:关于发送激活邮件的问题关于 QQ 邮箱和 Mailgun 的问题。 大家都过得很不容易啊…用户群QQ邮箱比例太大真是件蛋疼的事情,QQ邮箱虽说是腾讯的七星级产品,从用户大家角度感觉还不错,但这次从开发者的角度来看真的是 被折腾死了,希望以后能有所改进吧。

blog comments powered by Disqus