QQ登录

只需一步,快速开始

查看: 529|回复: 0
打印 上一主题 下一主题

如何建设一个靠谱的火车票网上订购系统

[复制链接]
跳转到指定楼层
1#
发表于 2012-1-15 05:47:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
, x4 d: h9 q2 D# W
  昨天,2012年1月11日,网友 @fenng 写了一篇文章,批评铁道部火车票网上订购系统,http://www.12306.cn [1]。同时在新浪发了一条言辞激烈的微博,“去你妈的‘海量事务高速处理系统’”,引起热议 [2]。
+ W9 H2 z/ c) M  春节将到,大家买不着车票,赶不上大年三十与家人团聚,急切心情可以理解。但是拍桌子开骂,只能宣泄情绪,解决不了实际问题。& {/ H3 D9 ]" }3 G: E5 u
  开发一套订票系统并不难,难在应对春运期间,日均 10 亿级别的洪峰流量。日均 10 亿级别的洪峰请求,在中国这个人口全球第一大国,不算稀罕,不仅火车票订票系统会遇到,而且电子商务在促销时,也会遇到,社交网站遇到新闻热点时,也会遇到。
  K- V, b# b( b( E1 v/ I  所以,能够在中国成功运行的云计算系统,推广到全球,一定也能成功。但是在美国成功运行的云计算系统,移植到中国,却不一定成功。  v, o3 c& m0 h9 P' n
  如果我们能够设计建造一套,稳定而高效的铁路订票系统,不仅解决了中国老百姓的实际问题,而且在全球高科技业界,也是一大亮点,而且是贴着中国标签的前沿科技的亮点。& W1 `( w1 A; L# t# B: W8 f) N
  于是软件工程师们献计献策,讨论如何改进 12306 网上购票系统 [3]。其中比较有代表性的,有两篇 [4,5]。
2 k& u& [  y6 a2 m# m; `; m  网友的评论中,有观点认为,[4] 利用“虚拟排队”的手段,将过程拉长负载降低,是网游的设计思路。而 [5] 利用缓存技术,一层层地降低系统负荷, 是互联网的设计思路。, s) t& `3 g" N  ]8 E1 t
  个人认为,[4] 和 [5] 并不是相互排斥的两种路线,两者着重解决的问题不同,不妨结合起来使用,取长补短。下面介绍一下我们的设计草案,追求实用,摈弃花哨。抛砖引玉,欢迎拍砖。
: \2 N; r8 |" C  {4 l; Z
2 w2 K. P- A* x% G0 F( O  R! x; W: n8 n- A2 E6 h9 i3 _
  图一。12306.cn 网站系统架构设想图。
* L3 o( A7 M' _! y5 p- R  Courtesy 4 R  T2 b6 Q% r9 L: T' n
  图一是系统架构图,典型的“展现层”/ “业务层”/ “数据层”的三段论。
" Z4 V0 s: Y/ i- N( d  用户接入有两类,一个是运行在电脑里的浏览器,例如 IE,另一个是手机。# N8 D. s. }2 E9 a0 \6 c  `* f7 W
  无论用户用电脑浏览器,还是手机访问 http://www.12306.cn 网站,用户请求首先被网站的负载均衡器接收。负载均衡器连接着一群门户服务器,根据各个门户服务器的负载轻重,负载均衡器把用户请求,转发到某一相对清闲的门户服务器。6 @8 O2 t! \9 n" \$ h, o& _6 {: W0 ]% {- a
  门户服务器的任务类似于收发室老头儿,它只读每个用户请求的前几个 bytes,目的是确定用户请求的类型,然后把请求投放到相应类型的队列中去。门户服务器的处理逻辑非常简单,这样做的好处,是让它能够快速处理大批量用户请求。4 W2 y; k9 Y- e, v8 R$ i& y- S
  根据 [5] 的分析,12306 处理的用户请求,大致分为三类,: }% n; d: U- u0 \2 ^: V
  1. 查询。用户订票前,查询车次以及余票。用户下订单后,查询是否已经订上票。
3 M' C9 j/ u& J- c  2. 订票,包括确定车次和票数,然后付款。用户付款时,需要在网银等网站上操作。
+ x2 r6 M  m/ C$ v  3. 第一次访问的用户,需要登记,包括姓名和信用卡等信息。
8 }8 G/ y2 {: J% I2 I7 f# p" ^  三类请求的业务处理过程,被分为两个阶段,0 i: i1 e* R% q7 `) t+ e
  1. 运行于缓存中的任务队列。设置队列的目的,是防止处理过程耗时太长,导致大量用户请求拥塞于门户服务器,导致系统瘫痪。. K. N6 ?& n! d% ?% f1 B* ?/ i
  2. 业务处理处理器,对于每一类业务,分别有一群业务服务器。不同业务的处理流程,各不相同。
. Z0 E) s& V/ w- W) F+ ]+ T% I2 l6 h) J; R& s: G/ U2 u* A$ `

3 B7 f2 [4 l/ \  图二。12306.cn 网站查询和订票业务流程设想图。
1 N7 J: A" g( R% c, j- u) O  Courtesy
, i! L5 Q7 o8 H9 `( J% H  图二描述了查询和订票,两个业务的处理流程。登记业务流程从略。
: P# f9 u6 w# V1 Q" N" R  查询的业务流程,参见图二上半部,分五步。这里有两个问题需要注意,
# `' @% Q2 f/ y  e. n: G; u% w  1. 用户发出请求后,经过短暂的等待时间,能够迅速看到结果。平均等待时间不能超过 1 秒。. |1 D  X$ ?) I! D2 J
  2. 影响整个查询速度的关键,是“查询服务器”的设计。* c0 u+ y3 b$ x
  查询任务可以进一步细化,大致分成三种。2 v& j! w( W( v
  1. 查询车次和时间表,这是静态内容,很少与数据库交互,数据量也不大,可以缓存在内存中。
& h/ k5 @1 ^5 y+ \2 I! o$ n; L  车次和时间表的数据结构,不妨采用 Key-Value 的方式,开发简单,使用效率高。Key-Value 的具体实现有很多产品,[5] 建议使用 Redis。1 V9 I, |5 {) z9 ?( ?. C
  这些是技术细节,不妨通过对比实验,针对火车票订票系统的实际流量,以及峰值波动,确定哪一个产品最合适。
. o. q3 e3 G9 S  2. 查询某一班次的剩余车票,这需要调用数据库中不断更新的数据。) v' x& h2 M4 C; z+ E
  [5] 建议把剩余车票只分为两种,“有”或“无”,这样减少调用访问数据库的次数,降低数据库的压力。但是这样做,不一定能够满足用户的需求,说不定会招致网友的批评讥讽。
2 ~$ }( s' G' t+ S  [4] 建议在订票队列中,增加测算订票队列长度的功能,根据订票队列长度以及队列中每个请求的购票数量,可以计算出每个车次的剩余座位。如果 12306.cn 网站只有一个后台系统,这个办法行之有效。
* }( a4 |9 t, a% q7 V4 u$ V  但是假如 12306.cn 网站采用分布式结构,每个铁路分局设有子系统,分别管理各个铁路分局辖区内的各个车次。在分布式系统下,这个办法面临任务转发的麻烦。不仅开发工作量大,而且会延长查询流程处理时间,导致用户长久等待。
: ^9 Y  u. h6 q% f9 K: y! Y0 }$ `. m1 [  3. 已经下单的用户,查询是否已经成功地订上票。
/ Y: l+ d& B4 c3 s5 l- r( }9 L) G  每个用户通常只关心自己订的票。如果把每个用户订购的车票的所有内容,都缓存在内存里,不仅非常耗用内存空间,内存空间使用效率低下,更严重的问题是,访问数据库过于频繁,数据量大,增大数据库的压力。
& s% P0 {) q/ W9 o; V  解决上述分布式同步,以及数据库压力的两个问题,不妨从订票的流程设计和数据结构设计入手。
$ q8 m* ^) E- D" y  Q7 p  假如有个北京用户在网上订购了一套联票,途经北京铁路局和郑州铁路局辖区的两个车次。用户从北京上网,由北京铁路局的子系统,处理他的请求。北京铁路局的订票服务器把他的请求一分为二,北京铁路局的车次的订票,在北京子系统完成,郑州铁路局的车次在郑州子系统完成。
6 |5 e4 D: \  a5 l  y! H  每个子系统处理四种 Key-Value 数据组。) G: G9 d$ ^+ W% {& t7 }( J
  1. 用户ID:多个 (订单ID)s。
* Z4 C. @+ y4 r; h& R- W0 F( g: k  2. 订单ID:多个 (订票结果ID)s。
; m- e8 j& W" \' k& a! P; f* J  3. 订票结果ID: 一个 (用户ID,车次ID)。
- Z  T9 K$ A' N7 F) b( V; |  4. 车次ID:一个(日期),多个 (座位,用户ID)。
# x7 `# p) N0 L9 V4 t  北京订票服务器完成订票后,把上述四个数据组,写入北京子系统的数据库,同时缓存进北京的查询服务器,参见图二下半部第6步和第7步。* ]& y: d2 f) c8 F1 r# t8 B* Q4 d
  郑州订票服务器完成订票后,把上述四个数据组,写入郑州子系统的数据库,同时缓存进北京的查询服务器,而不是郑州的服务器。
, ?7 i: _8 n6 t7 T' S  让订票服务器把订票数据,同时写入数据库和查询服务器的缓存,目的是让数据库永久保留订票记录,而让大多数查询,只访问缓存,降低数据库的压力。1 m7 f: V6 o, X2 |: H
  北京用户的订票数据,只缓存在北京的查询服务器,不跨域缓存,从而降低缓存空间的占用,和同步的麻烦。这样做,有个前提假设,查询用户与订票用户,基本上是同一个人,而且从同一个城市上网。
+ F) l2 J9 j2 V, Y  但是这里有个缺陷,某用户在北京上网订了票。过了几天,他在北京上网,输入用户ID和密码后,就会看到他订购的所有车票。可是又过了几天,他去了郑州,从郑州上网,同样输入用户ID和密码,却看不到他订购的所有车票。8 x, j- M, N) Y- U
  解决这个缺陷的办法并不麻烦,在用户查询订票信息时,需要注明订票地点,系统根据订票地点,把查询请求转发到相应区域的子系统。$ x# M. s+ t' [( T/ Z! z7 E% }
  另外,每次订票的时候,网站会给他的手机发送短信,提供订票信息,参见图二下半部第8步和第9步。
( ^. ?/ r% L4 R2 \% y* O( p  以上是一个初步设计,还有不少细节需要完善,例如防火墙如何布置等等。这个设计不仅适用于单一的集中式部署,而且也适合分布式部署。* s8 F, c/ y- j) H
  或许有读者会问,为什么没有用到云计算?其实上述架构设计,为将来向云计算演变,留下了伏笔。
5 M2 \/ B0 o  x- @. {  在上述架构设计中,我们假定每个环节需要用多少服务器,需要多大容量的数据库,预先都已经规划好。但是假如事先的规划,低于实际承受的流量和数据量,那么系统就会崩溃。所以,事先的规划,只能以峰值为基准设立。7 j5 ^) f! |" A% y+ f. s
  但是峰值将会是多少?事先难以确定。即便能够确定峰值,然后以峰值为基准,规划系统的能力,那么春运过后,就会有大量资源冗余,造成资源浪费?
9 Y6 F1 y  ^2 D+ a  如何既能抗洪,又不造成资源浪费?解决方案是云计算,而且目前看来,除了云计算,没有别的办法。
! k7 G' _6 m1 }: H% O  }* y8 k7 A6 f  Reference,6 o1 J2 P) M% P! y
  [1] 海量事务高速处理系统。1 R) b( {0 Z* ~* ^) `
  http://www.douban.com/note/195179318/2 `8 F3 b6 K& G2 p
  [2] 去你妈的‘海量事务高速处理系统’。
7 Y7 x7 R( Z1 k  http://weibo.com/1577826897/y0jGYcZfW! [: S0 B( B$ C* c
  [3] 火车订票系统的设想。. \( ?( E3 ~# `4 y/ U, _
  http://weibo.com/1570303725/y0l9Y2mwE" h# ]* c; P! _5 G3 i- y
  [4] 铁路订票系统的简单设计。
! _4 h& x5 r' U; A- `' O0 U8 l  http://blog.codingnow.com/2012/01/ticket_queue.html  x/ _7 m0 J. @; W" y# r
  [5] 铁路订票网站个人的设计浅见。3 U* p8 x5 G& y; h$ A  \; G
  http://hi.baidu.com/caoz/blog/item/f4f1d7caee09b558f21fe780.html
& s2 ~: M+ o/ p4 _) \7 A4 c7 g3 Q  题图来自 Designyoutrust% w# _8 {# x# }5 Z$ x9 L
        作者:邓侃 美国卡内基梅隆计算机机器人专业博士+ q& B$ G+ k: u/ O
http://blog.sina.com.cn/s/blog_46d0a3930100yc6x.html3 [; X' n: }- r5 {

0 c+ Z! M8 h- g
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 转播转播 分享分享 分享淘帖 顶 踩
内乡社区网免责声明:本文来源于网络、媒体投稿和用户自行发帖,与内乡社区网无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。如果本文内容有侵犯你的权益,请发送信息至702079960@qq.com,我们会及时处理。点击进入客服处理点击进入客服处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|内乡社区网 ( 豫ICP备12007913号-2 

GMT+8, 2025-5-5 15:23

Discuz! X3.3

© 2001-2018 Comsenz Inc.

快速回复 返回顶部 返回列表