"12306”的架构到底有多牛逼?

每到节假日期间,一二线城市返乡、外出游玩的人们几乎都面临着一个问题:抢火车票!
虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会 。尤其是春节期间,大家不仅使用12306,还会考虑“智行”和其他的抢票软件,全国上下几亿人在这段时间都在抢票 。
“12306服务”承受着这个世界上任何秒杀系统都无法超越的QPS,上百万的并发再正常不过了!笔者专门研究了一下“12306”的服务端架构,学习到了其系统设计上很多亮点 。
在这里和大家分享一下并模拟一个例子:如何在100万人同时抢1万张火车票时,系统提供正常、稳定的服务 。
1. 大型高并发系统架构高并发的系统架构都会采用分布式集群部署,服务上层有着层层负载均衡,并提供各种容灾手段(双火机房、节点容错、服务器灾备等)保证系统的高可用,流量也会根据不同的负载能力和配置策略均衡到不同的服务器上 。下边是一个简单的示意图:

"12306”的架构到底有多牛逼?

文章插图
 
1.1 负载均衡简介上图中描述了用户请求到服务器经历了三层的负载均衡,下边分别简单介绍一下这三种负载均衡:
OSPF(开放式最短链路优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP) 。OSPF通过路由器之间通告网络接口的状态来建立链路状态数据库,生成最短路径树,OSPF会自动计算路由接口上的Cost值,但也可以通过手工指定该接口的Cost值,手工指定的优先于自动计算的值 。
OSPF计算的Cost,同样是和接口带宽成反比,带宽越高,Cost值越小 。到达目标相同Cost值的路径,可以执行负载均衡,最多6条链路同时执行负载均衡 。
LVS (linux VirtualServer),它是一种集群(Cluster)技术,采用IP负载均衡技术和基于内容请求分发技术 。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器 。
Nginx想必大家都很熟悉了,是一款非常高性能的http代理/反向代理服务器,服务开发中也经常使用它来做负载均衡 。
Nginx实现负载均衡的方式主要有三种:轮询、加权轮询、ip hash轮询,下面我们就针对Nginx的加权轮询做专门的配置和测试
1.2 Nginx加权轮询的演示Nginx实现负载均衡通过upstream模块实现,其中加权轮询的配置是可以给相关的服务加上一个权重值,配置的时候可能根据服务器的性能、负载能力设置相应的负载 。下面是一个加权轮询负载的配置,我将在本地的监听3001-3004端口,分别配置1,2,3,4的权重:
#配置负载均衡upstream load_rule {server 127.0.0.1:3001 weight=1;server 127.0.0.1:3002 weight=2;server 127.0.0.1:3003 weight=3;server 127.0.0.1:3004 weight=4;}...server {listen80;server_nameload_balance.com www.load_balance.com;location / {proxy_pass http://load_rule;}}我在本地/etc/hosts目录下配置了 http://www.load_balance.com的虚拟域名地址,接下来使用Go语言开启四个http端口监听服务,下面是监听在3001端口的Go程序,其他几个只需要修改端口即可:
package mainimport ("net/http""os""strings")func main() {http.HandleFunc("/buy/ticket", handleReq)http.ListenAndServe(":3001", nil)}//处理请求函数,根据请求将响应结果信息写入日志func handleReq(w http.ResponseWriter, r *http.Request) {failedMsg :="handle in port:"writeLog(failedMsg, "./stat.log")}//写入日志func writeLog(msg string, logPath string) {fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_AppEND, 0644)defer fd.Close()content := strings.Join([]string{msg, "rn"}, "3001")buf := []byte(content)fd.Write(buf)}我将请求的端口日志信息写到了./stat.log文件当中,然后使用ab压测工具做压测:
ab -n 1000 -c 100 http://www.load_balance.com/buy/ticket统计日志中的结果,3001-3004端口分别得到了100、200、300、400的请求量,这和我在nginx中配置的权重占比很好的吻合在了一起,并且负载后的流量非常的均匀、随机 。
具体的实现大家可以参考nginx的upsteam模块实现源码,这里推荐一篇文章:
https://www.kancloud.cn/digest/understandingnginx/202607
2.秒杀抢购系统选型回到我们最初提到的问题中来:火车票秒杀系统如何在高并发情况下提供正常、稳定的服务呢?
【"12306”的架构到底有多牛逼?】从上面的介绍我们知道用户秒杀流量通过层层的负载均衡,均匀到了不同的服务器上,即使如此,集群中的单机所承受的QPS也是非常高的 。如何将单机性能优化到极致呢?要解决这个问题,我们就要想明白一件事:


推荐阅读