在这个神奇的国度,每个地方都有拥堵问题,公交站台,景区门口,银行柜台,甚至食堂窗口,大家都用同一种办法来解决这个问题:排队。所以我一直很纳闷,12306 为什么一开始没有实现排队的功能,是领导拍了脑袋?还是有其它不为人知的原因?
废话少扯,还是说正经的吧。
Redis 用在这里,不仅仅是一个 NoSQL 存储工具,简直是一个完美的解决方案。下面我就来详细描述一下我想象中的一个架构:
LVS 接入,同时将 https 协议转成 http 协议。 按照网上流传的 2kw 的预算,F5 恐怕是用不起了,不过也没事,LVS 加硬件 SSL 卡,50w 预算绝对拿下。
varnish 反向代理。cache 住大部分的静态资源,每台机器 3w/s 的 rps 应该可以比较轻松的应对。即使按照 30w/s 的峰值能力计算,也就 10 台机器,30w 预算
Nginx 服务静态资源,有 2 台足够,6w 预算
Tomcat 服务动态请求,这个量会比较大。但可以从这几个方面入手去减少资源消耗:第一,使用 ajax 请求,而不是页面刷新,减少单个请求的 size;第二,做好频率检测和控制,对于部分用工具来刷的用户,甚至可以采取惩罚措施,以减少无谓的资源消耗。保守估计,20 台机器左右,这个数字要看领导们对于“用户体验”的要求程度。
Redis 服务器,96G / 台,价格也就 3w 出头。预算 10 台,将 2kw 注册用户全部加载到内存,将 2k 车次全部加载到内存,将每个车次的每个座位的状态都加载到内存,将每个在线用户都加载到内存,应该都还有内存剩余。为什么要写磁盘呢?大不了给 Redis 服务器配置双电源,再配一个电池!
DB 服务器。最重要的 db 服务器,“铁道部”的系统,如果不是 DB2 那就应该是 Orcale 了,应该是连硬件到软件一起买的解决方案吧。这个我没有用过,甚至都没有见过,所以不好估计预算了。如果用我们比较熟悉的 mysql,用户系统应该是一个单独的库;车次信息应该是一个单独的库,而且基本上是只读;车票信息应该有区分当前库和历史库,当前库用来存未售出的车票信息,或者近期刚刚售出的车票信息,数据量小,支持快速读写,以及更新,历史库用来存历史上的车票信息,存档性质;订单信息单独一个库,读写量都不会太大。除了车票信息的当前库建议用 SSD ,其它的库采用普通的硬件就行,预算不会超过 5w/台,按照 1M3S 的标准配置,总体的机器台数在 20 以内,预算 100w 左右。值得一提的是,车票信息的当前库,需要按照车次(甚至是车次+日期)进行分库分表。想提高实时性,提升用户体验,减少投诉?增加当前库的机器投入即可。
硬件设备应该就是这样了,接下来说说一些值得一提的实现细节。
当前 12306 系统一个很受人诟病的实现就是无法登录。用户打开登录页,输入了用户名密码,还耐心的填好了验证码,点击提交,再耐心的等了 30 秒,结果,弹出一个无比丑陋的对话框,说“当前访问用户过多,请稍后尝试”,太坑人了!以至于某公司甚至给做了一个“12306登录助手”出来。让用户登录进来,给他们能买到票的希望,是减少投诉的一个很重要的方面。这个其实一点也不难:将用户信息都加载到 Redis 内存,简单点,key 就是 email,value 就是密码加密串(亲,不能是明文哦!),用 cookie 而不是 session 进行身份验证,用 ajax 而不是刷新页面的方式提交数据和返回应答,这么一来,即使 2 kw 用户同时都登录进来,也只需要三五台 tomcat ,20分钟就搞定了。
用户登陆进来之后,不能立即就给出太多的输入框,让用户输入查询,那样对系统的消耗比较大,不划算。应该先让用户通过出发站和到站,查询好车次(单独的车次信息库,全部加载到内存供查询),选择好乘车日期,然后——点击“取号”按钮,拿一个号,进行排队。是不是很熟悉?去银行开卡,汇款啥的,不都是这么办的么!将每个“车次+发车日期”作为一个单独的队列进行排队,其实就是将火车站售票窗口给移动到互联网页面上而已!想提升用户体验?那就每个登录用户可以同时排 3 个队吧,比如我,想买 18 号或者 19 号的 K21,那我就同时在 “K21+18号发车” 和 “K21+19号发车” 2 个队伍里排着好了。
用户既然已经在排队了,那后台处理就很灵活了:根据 db 的承载能力,将用户有序的带到查余票和下订单的页面(后面命名为 “售票厅” 吧)即可。用户在排队页,用 ajax 请求定期轮询,server 端返回有 3 种:当还没有轮到该用户时,返回当前队列前面还有多少人,这个车次还剩多少张卧铺,多少张硬座等等,当然,这些信息都不是实时更新的,而是定期由后台线程或进程进行更新。读取的时候,只是一个缓存 get 操作而已。另一种就是已经轮到该用户,这个时候,给一个30秒或者60秒的订票时间窗口,要求在这个时间内完成查询和下单操作:查询的时候并不做任何的 lock 操作,而是仅仅显示 db select 的结果(这个地方,可能需要 select master 库),填写完票数,点击提交的时候,才做真正的 lock,随机选择座位,update db,update redis 。这个步骤是直接操作 db,不做任何的缓存,但因为是根据 db 的能力放进来的量,所以性能风险反而最小。还有一种状态,就是该用户的号已经过期,或者订票时间窗口过期,很简单,重新取号,重新排队。
具体到 Redis 操作,大约会是这样:
用户:key 为 user:abc@test.com ,value 就是 md5(email + password) ,用户登陆的时候,只需要做一次 get ,即可验证登陆是否成功。
用户参与排队的时候,给用户分配一个 token,token 中建议包含加入排队的时间。
为每个“车次+日期”建立一个队列:key 为 train:queue:k21:20120118 , value 是一个 list ,用户点击“排队”按钮,加入队尾,用 lpush token,后台进程根据 db 负载,从队伍头取出一个用户带入查票和下单页面用 rpop token 即可。后台一个定时任务,定期扫描队伍,更新每个人前面的人数,供查询使用。
为每个“车次+日期”建立一个“售票厅”:key 为 train:selling:k21:20120118 , value 是一个 hash ,field 即 token,value 为过期时间。后台进程定期从队列中 rpop 出一个或多个用户,hset 到 hash 里。并且还需要一个后台进程,定期从售票厅里清除出过期的 token
用户在排队页面,使用 token 作为参数来查询自己的状态:首先 hget 售票厅,确认是否已经轮到自己了,如果没有,则再获取前面还有多少人,预估还需要排队多久,车次还剩下多少票等等信息。如果已经轮到自己,则显示“下一步”按钮,将用户带到售票厅。这一步需要做好安全验证,防止有人采取技术手段绕过排队,直接进入售票厅。
如果不希望用户不停的按刷新按钮,消耗服务器资源?那就来一招狠的:用户每次刷新,则将他在排队中的位置往后排一个!不过采取这样的措施之前,别忘了在醒目位置提醒用户“注意节约我们的带宽资源”!
用户真正下单买票,这是纯粹的业务逻辑,这里就不再细说。下单买票完成后,记得更新一下余票信息缓存即可。
这样的系统很难做么?难,也不难。有的地方因为业务逻辑太复杂,不得不做一些改变,比如亚马逊为了应对圣诞购物季,建了一块云,比如淘宝为了双11双12的秒杀,买了一堆的机器;也有的地方因为各种其它的原因,无法做到完美,比如 12306,比如新浪微博。不过无论现实如何,在我们技术人员的心里,永远保留着一颗追求完美的心,永远保留一股追求更好的劲头,就够了。
- 浏览: 202729 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (397)
- j2se (28)
- nio (3)
- 易错点 (3)
- 面试ssh (9)
- ssh整合 (11)
- jbpm+spring (2)
- js (15)
- 高级技术 (59)
- swing (3)
- 数据库 (16)
- hibernate (18)
- spring (19)
- 开发网站知识点 (9)
- jbpm (4)
- json (5)
- 设计模式 (22)
- 自定义标签 (1)
- j2ee (9)
- lucene (3)
- cahce (11)
- maven (5)
- html5 (1)
- 多数据源 (10)
- 页面聊天 (9)
- 富客户端 (1)
- android (13)
- aop+拦截器+jms (13)
- 框架整合 (1)
- 非阻塞io (24)
- 暂时不看 (13)
- webservice (3)
- oracle (3)
- 算法 (4)
- 协程 (2)
- netty (1)
- 爬虫 (0)
- 高级基础 (1)
- JVM调优总结 (12)
- 知识点技巧 (1)
- REST (0)
- 基础 io (2)
- dubbo (8)
- 线程 (1)
- spring源码 (2)
- git (1)
- office (2)
最新评论
-
sjzcmlt:
,写的挺好的啊
一个完整的负载均衡的例子 . -
他大姨妈:
网上大部分例子都是直接通过IdleStateHandler来实 ...
Netty的超时机制 心跳机制
发表评论
-
WebLogic11g-单双向SSL配置(以Springside3为例)
2016-03-27 09:45 5232、观察weblogic方便开发部署提供的演示秘钥库 ... -
WebLogic11g-半小时让你的domain集群化
2016-03-27 09:39 681其实网上关于weblogic集群的资料非常多【大 ... -
解决nginx负载均衡的session共享问题
2016-03-27 09:29 439查了一些资料,看了一些别人写的文档,总结如下,实现ngin ... -
JProfiler学习笔记
2016-03-20 10:36 429JProfiler学习笔记 一、安装JProfile ... -
使用阻塞式队列处理大数据 excel
2016-03-20 11:05 638前言 我们都知道,JAVA对于文本文件在读时是独占的,即 ... -
RMI是什么
2016-03-14 10:21 574(14) RM ... -
Java 高并发缓存与Guava Cache
2016-03-13 21:59 750一.背景 缓存是我们在开发中为了提高系统 ... -
Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
2016-03-13 21:13 424互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无 ... -
轻量级分布式 RPC 框架
2016-03-13 15:31 438目录[-] 第一步 ... -
exe4j 可以很容易吧一个 jar 打包成 exe
2015-11-02 19:45 0一、背景 exe4j 可以很容易吧一个 jar 打包 ... -
如何预估服务器带宽需求【转】
2015-06-07 13:35 616PC=PV/T*C*t*f其中,PC是并发数,T是观测时间 ... -
年终培训关于磁盘冗余阵列、热备、群集、负载均衡、云计算、F5、Nginx等的概念和基本原理
2015-06-07 12:52 496在系统部署实施过程中,客户往往会关注系统的可用性方 ... -
年终知识分享——大型项目架构
2015-06-07 12:25 473... -
分布式缓存服务器设计原理
2015-06-07 11:48 7041.数据是如何被分布到多个服务器上的?(一致性哈希算法) ... -
Apache2.2.x+Tomcat6.x+jk2.x做集群配置
2014-10-18 10:43 415【Middleware】☆★之Apac ... -
单点登录SSO
2014-10-22 21:34 491摘要 :单点登录( ... -
java批量处理数据
2014-06-08 13:51 647程序清单ImproveReadFile .java ... -
大型网站技术架构
2014-06-06 09:38 574看完了有一本书,就 ... -
关于敏捷开发的总结 .
2014-06-06 09:38 393•用例一完全能够运行后再开发用例二。厨房里有一种说法正好可以印 ... -
应聘java项目经理经常遇到的问题
2014-06-06 09:38 601司最近招项目经理,我负责一面。下面是我面试的部分问题,有些问题 ...
相关推荐
12306购票平台UWP源码.rar
基于微信小程序的电影购票系统开题报告.zip 基于微信小程序的电影购票系统开题报告.zip 基于微信小程序的电影购票系统开题报告.zip 基于微信小程序的电影购票系统开题报告.zip 基于微信小程序的电影购票系统开题报告...
基于springboot+vue.js的购票系统毕业设计.zip基于springboot+vue.js的购票系统毕业设计.zip基于springboot+vue.js的购票系统毕业设计.zip基于springboot+vue.js的购票系统毕业设计.zip基于springboot+vue.js的购票...
本设计要实现以下设计目标: 实现会员购票制,即所有通过本订票系统订票的旅客都必须首先注册为会员,以便于系统管理; 提供会员注册、登录、修改注册信息等功能; 建立车次信息查询模块,可以通过指定车次号码,或发站到站...
12306春运快速购票攻略.docx
基于php网上航空购票系统的实现.pdf
基于springboot-vue.js的购票系统源码.zip 基于springboot-vue.js的购票系统源码.zip 基于springboot-vue.js的购票系统源码.zip 基于springboot-vue.js的购票系统源码.zip 基于springboot-vue.js的购票系统源码.zip ...
一种基于微信小程序的智能购票管理系统及应用.docx一种基于微信小程序的智能购票管理系统及应用.docx一种基于微信小程序的智能购票管理系统及应用.docx一种基于微信小程序的智能购票管理系统及应用.docx一种基于微信...
火车购票系统需求分析.doc
c++命令模式实现12306购票系统 详见博客:http://blog.csdn.net/caoshangpa/article/details/52912355
基于javaweb的电影院在线选座购票网站项目源码.zip基于javaweb的电影院在线选座购票网站项目源码.zip基于javaweb的电影院在线选座购票网站项目源码.zip基于javaweb的电影院在线选座购票网站项目源码.zip基于javaweb...
05-基于java框架springboot旅游景区景点购票系统 开题报告.zip
毕业设计基于Spring Boot的电影院购票系统项目源码,大致分为管理员、用户端、客户服务端,难度适中,新手也可自己操作,二次开发也可以,高分必看。毕业设计基于Spring Boot的电影院购票系统项目源码,大致分为管理...
课程设计基于java swing的火车购票系统源码.zip课程设计基于java swing的火车购票系统源码.zip课程设计基于java swing的火车购票系统源码.zip课程设计基于java swing的火车购票系统源码.zip课程设计基于java swing的...
"springboot基于web的电影院购票系统的实现 电影院购票系统在对开发工具的选择上也很慎重,为了便于开发实现,选择的开发工具为Eclipse,选择的数据库工具为Mysql。以此搭建开发环境实现电影院购票系统的功能。其中...
基于ssm的电影购票系统源代码.zip