优化微信红包抢购系统

近期,公司出台了1个新的方案,发送微信红包1个星期(截止到2016年7月12号),每天早晚10点进行发放。不得不说,这对于我们的系统是1个很大的考验,好不容易从匆忙的开发中将这个系统给完成了,又不得不匆忙中上线。
上线的第1天由于数据抖动的问题,导致一些用户没有领取到红包,因此我们引入了redis。
在这里,我们采用Python的Flask进行开发,使用gunicorn进行部署,而没有采用Django这样的框架,主要考虑的是轻装上阵,越简单越好。

问题

然而,上线第2天,7号上班的时候就收到反馈到,用户连进入抢购的页面都进不去,从10点开始就一直白屏,即使开着WIFI也要等待差不多2分钟才能进去,当点击领取的时候,又是漫长的等待,而结果却被告知红包已经领取完了。这样的用户体验实在令人发指。
对于这样的问题,留给我们的时间已经不是很多,我们必须在晚上10点之前出1个新的版本,让这个系统平稳的度过微信红包的抢购环节。
于是,我跟我隔壁那个搞Python的哥们又不得不对当前的系统进行优化。我们列出了我们系统实现一些的细节,主要如下:

  • 客户端每隔5秒中请求服务器,看活动是否已经开始。
  • 客户端每点击1个请求,则往服务器端发送1个请求。
  • redis将请求过来的用户推入到队列中。
  • 与此同时,调用微信的接口给用户发放红包

可以看到,这些实现的细节都是存在一点的不合理性的。

目标

为此,我们首先要解决第1个问题是,是解决用户点击链接白屏的问题,然后是点击活动按钮页面一直在等待的问题。下面我们将我们的这2个目标分为2步来进行处理。

解决白屏的问题

对于白屏的问题,让我们先来理下思路。首先,当用户点击微信上的某个链接时,将请求1次微信的服务器,然后微信服务器再请求我们的服务器。
而实际的情况是,在白屏的时候进度条总是停留在90%的时候,就是进不去。这说明,当微信服务器请求我们的服务器时,我们的服务器无法快速的响应。
对于这样的问题,我们采取了如下的步骤来解决:

压缩图片

由于我们的页面由2个简单的png图片组成,而这2个图片的大小分别为30k和28k。于是,我们采用矢量量化算法对其压缩处理,分别减少了65%和58%的大小,这样,图片的大小分别为11k和13k。

压缩静态文件

接着,我们对css、html、js文件进行压缩,让其只有1行。对于css和js使用CDN加速,之后开启nginx的压缩功能。

静态化文件

之后,我们调用nginx的对static目前的文件缓存1年的时间,而不是使用Flask进行托管。

其他优化

通过上述的方式,我们基本解决了前端优化的问题,但是后端服务器方面我们还需要进行优化,才能比较好的解决白屏的问题。
我们来想1个问题,现在当用户点击连接时,微信服务器对我们服务器发起了1个请求,而用户如果不断的请求我们的页面,那么我们的服务器就处理不过来了。可能你会问,用户哪里可能会不断的请求我们的页面呢?
用户当然不可能不断请求我们的页面,但是他可以使用程序不断的请求我们的服务器。在下午的时候,我们发现我们的服务器不断有请求进来,而此时距离开始领取红包还有6个小时。
于是,我们就对nginx日志进行统计,使用python统计其中的IP地址每分钟出现的次数,然后我们使用1个离线数据库查询其IP的归属地,发现是1个上海电信的IP。
我隔壁那个搞python的哥们说,以前自己搞爬虫的时候也会进行限速和限次数,反倒这家伙还不断的请求。
接着,我们使用nginx提供的对应模块对其进行限制其访问的次数,每个IP只能在1秒中内访问2次,否则将返回1个503错误,然后发现之后的请求少了很多。
但是很快,我们发现在日志中有4~5个IP还是比较活跃,而且有些还是使用阿里云的IP。因此我们猜测对方手上有批量的IP,因此我们只能采取其访问次数达到一定的次数直接封锁IP地址1天的策略了。
之后我们访问我们的页面,一点就进去了,而不需要等待1秒后再进入,说明我们这部分优化有点效果。

解决一直等待的问题

关于领取页面一直在等待的问题,跟用户的操作有关。1个用户,当他点击对应的按钮发现还没有弹出领取成功的结果,他会不断的在点击对应的按钮。而我们之前的系统对应这方面完全就是空白,当用户点击按钮时就发送1个请求,这样当1000个用户平均每人点击5次,那么无形中就多了4000个无用的请求了。
对于,我们采用如下的方式来解决。

用户点击1次

当用户点击按钮后,我们就发送1个请求到服务器。当用户再次点击按钮后,我们将丢弃这个点击,不向服务器进行任何的操作。

客户端计算时间

之前我们的系统在活动开始之前会每隔几秒查询活动是否开始,这也是1个很大的问题,增加了很多无用的请求。因此,我们使用后端输出1个开始时间到前端,而在前端来计算活动开始的时候。

使用动态签名

为了让每个用户只能领取1次,我们使用1个动态签名的方式生成1个唯一的Token。如果该Token不存在我们才会将其推入我们发放的队列中,否则将忽略这次的请求,这样也防止了一些程序直接请求我们发放的接口。当然,最好还是实现1个随机的URL地址来进行发放红包。

使用Tornado进行异步请求

本来个人倾向于使用Twisted的,这个库如果你熟悉网络基础,使用起来会很顺手。但是,我们还是决定使用Tornado,主要在于相比Twisted,其会更简单些,而且我们也不需要用到一些比较复杂的东西。
这里我们使用Tornado异步跟微信服务器进行交互,获取到用户对应的信息。然后再存储到队列中,并进行红包的发放。
当队列的达到一定的大小,自动进行服务降级,丢弃后面所有的请求,告诉用户红包已经发放完毕。

总结

最后,到了晚上发现还是无压力的过了,系统负载保持在0.3以下,不得不说这次的优化还是起到了效果。
这里关于微信红包抢购的优化,是生活中点滴的积累而成,不是一天两天可以完成的,重要的在于坚持学习。另外,越是紧急要不要着急,要相信团队的力量。
最后,如果你如果对我们的公司发送微信红包有兴趣,想领取几个红包,请扫描下面的二维码。

logo

然后在活动中点击限时活动即可。

若文章对您有帮助,请打赏1块钱。您的支持,可以让我分享更多精彩的文章。转载请注明来源


知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。