浅谈人文知识竞赛的框架设计

前一段时间,参与搭建了学校人文知识竞赛的网页设计,说实话,做这玩儿如果只是简单的做一做的话,根本不是什么大问题,但是考虑到之前上一届做的不小心崩掉了最后只有180人答题,所以还是仔细思考了一下在高并发下,如何保证服务器不崩,以及与此同时保证足够的漂亮不俗。

实际上做这个东西,确实是费神费心,关键是我还在上班,上班的内容也不算特别轻松,所以大概所有的时间,是某一个晚上熬夜到3、4点,然后每天下班后修修补补,不过所幸,在最后也基本没有出现什么较大的问题,整体上来说,是成功的。

方案设计

人文知识竞赛是一个及时性很强的东西,一方面要保证答题的迅速,另一方面要尽可能的减少作弊的可能性。

去年的方案是这样设计的:

  1. 每答一道题,请求下一道题
  2. 三十分钟十道题
  3. 报名系统在主页上,随时可以报名

这之间其实需要斟酌的地方有很多,首先,三十分钟十道题,本身就无法抵制作弊的发生,其次,每一个答题者,起码要发生11+的请求,这对服务器是考验的(何况是渣PHP虚拟主机)。而且,报名系统是公开的,可以从这里对服务器进行攻击。

所以,我们索性先不从技术上的角度去考虑,而是从赛制上去考虑:

  1. 注册系统需要改,我们最后商议,改成了邮箱注册,因为我觉得除非是够无聊,否则不会注册几十个邮箱去试探我们的系统。并且注册在答题前一天就关闭了,尽可能的减少由于恶意注册而带来的损失。

  2. 十分钟五十道题,尽可能的减少作弊的可能性,意味着,网上搜索答案是有限的。 虽然最后我们发现十分钟五十道题依旧不是很多

  3. 必须是答完一道题出现下一道,这个无法更改,但是我们可以在一开始就下载到全部的题库。

  4. 分时答题,进一步减少服务器压力。

至于防作弊:

  1. 一个人的帐号是有限制的,除非无聊去注册辣么多的帐号。
  2. 无论是激活,还是抓题库,只能干一次。
  3. 验证都在后端完成,前端改东西木有用。

技术设计

大框架设计了,那么剩下的只有实现了,我先把中心的设计说一下:

框架

前端设计

前端采用的是Polymer,至于Polymer是什么,我在之前的文章里面已经说过了。

为什么采用Polymer呢?首先,Polymer是在本地渲染的,一定程度上缓解了服务器的压力,其次,Polymer适合构建单页App,可以非常方便的完成逻辑以及UI设计。

也就是说,在你第一次访问主页的时候,所有的前端代码都已经下载到本地,不需要在对服务器进行请求,对服务器造成的压力是一次性的,更何况服务器不用去渲染数据,进一步减轻压力。

需要数据的时候,向后端发起AJAX请求,获取数据。这个过程是基于JSON BASED TOKEN的,可以更好防止受到污染。

所以最后给用户的效果就像是使用一个app一样,虽说在pc上可能略诡异,爪机上的体验更加,但是总体来说效果是不错的。

前端方面,我使用了Google的Topeka的大部分源代码,不过在逻辑上,也进行了大幅度的修改,等我整理好这一部分代码,过段时间可以开源。

后端设计

Nginx 反向代理+静态页面

高并发就不要作死用Apache了
反向代理用于将API的接入交给Nodejs处理

Nodejs Stateless Web API

选用nodejs就是为了高并发,我相信选用其他的语言或多或少在性能上都弱于nodejs,我们的需求是IO密集型的,刚好适合nodejs发挥其特长。

后端和普通的后端不一样,只有一个提供JSON API的功能,其他的页面逻辑,全部由前端MVC框架实现,这样可以尽可能的减少后端生成页面所产生的压力。

具体的实现就和普通的程序一样了,无非是CURD,但是考虑到可能的数据库压力,我主要是用MongoDB做数据的持久化,以及Redis缓存临时的数据(比如题库,比如临时生成的答案),事实上,在后来的整理分析中,如果只是使用MongoDB甚至MySQL都是足矣完成任务的,不过使用Redis的性能最佳,只是开发周期可能长一些。

SendGrid Mail Service

我们使用了邮箱注册,为了尽可能的减少恶意注册所带来的影响。

实际上在使用的过程中,SendGrid表现的并不出色,每一次请求的成功率大概也只有40%左右,可以说在前端的体验非常不好(这一点也是由于我懒得去重写一个代码),只是有耐心的同学还是挺多的,所以后来邮件注册用户超过700人。

可能在生产环境中,寻求SendCloud是更好的选择。只是你们连49元的最便宜方案都不想用,我就只能用这种免费的了

这里提一下,一开始我们使用的是MailGun服务,邮件成功率很高,但由于服务器的姿势不对,经常就爆出异常(分析之后是访问了空域名),不知道为何,总之,我替换掉了。

总结

数据分析


因为学校非常非常抠,所以我就只能找免费的方案了
所幸,找到了一个非常好的方案,M$的Azure正在进行一元免费试用……(这里不是打广告啊喂),大概就是出一元,然后这一个月给你2000的信用额度……所以实际上还是挺赚的,于是我们的服务器就华丽丽的采用了独立4核+7G内存(啥,你问我为啥不用更高的配置?Nodejs是单线程的啊……)

最终的结果还是令人满意的,最开始请求页面并不算慢,并且之后的每一次请求都非常之快,几乎秒开(至少比各大网站登录快),我觉得就算是让我用1G内存+单核CPU,可能是在Redis方面有点瓶颈,但是总体上来说,也可以完成目标的,甚至于全部在一个时间内答题,也是可以Hold住的。

所以,好的架构是成功的一半,虽然目前的架构也说不上完美,但是总比PHP虚拟主机好(很多)吧...

改进

需要改进的还有很多的……不过由于懒癌晚期被窝忽视了

前端方面:进一步的美化和剔除Bug(咳咳,有个Bug应该被很多人忽视了……)

后端:

  1. 完全剔除Mongo,全部采用Redis做持久化
  2. Token加入验证机制,并且每完成一次请求,更新一次Token,进一步防止作弊
  3. 前端请求的数据加密,至少不应该明文传输
  4. 可以用socket.io重写AJAX,这样,无论是在安全性还是性能上来说都会更胜一愁

真正的总结

其实到现在,总共的成本是 近十个小时的工时+1元VPS+49元的域名,还是挺便宜的……

总共是1200多人完成了信息的绑定,最终900+人答题,除去部分没有答题的,以及少部分因为自己刷新了页面或者其他无聊操作找我们扯淡的,这个任务还是完成的不错。

客观上来说,我觉得收获之一就是不要一开始想着技术能解决的问题都不是问题,而是应该从策划上出发,再去决定使用什么计划,否则只能陷入技术陷阱了。

Table of Contents