“12306”的架构到底有多6?
![](/assets/blank.gif)
每到节假日期间,一二线城市返乡、外出游玩的人们几乎都面临着一个问题:抢火车票!
12306 抢票,极限并发带来的思考
大型高并发系统架构
![](/assets/blank.gif)
负载均衡简介
- 轮询
- 加权轮询
- IP Hash 轮询
Nginx 加权轮询的演示
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 {
listen 80;
server_name load_balance.com www.load_balance.com;
location / {
proxy_pass http://load_rule;
}
}
import (
"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, "\r\n"}, "3001")
buf := []byte(content)
fd.Write(buf)
}
秒杀抢购系统选型
下单减库存
![](/assets/blank.gif)
- 在极限并发情况下,任何一个内存操作的细节都至关影响性能,尤其像创建订单这种逻辑,一般都需要存储到磁盘数据库的,对数据库的压力是可想而知的。
- 如果用户存在恶意下单的情况,只下单不支付这样库存就会变少,会少卖很多订单,虽然服务端可以限制 IP 和用户的购买订单数量,这也不算是一个好方法。
支付减库存
![](/assets/blank.gif)
预扣库存
![](/assets/blank.gif)
扣库存的艺术
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
代码演示
初始化工作
//localSpike包结构体定义
package localSpike
type LocalSpike struct {
LocalInStock int64
LocalSalesVolume int64
}
...
//remoteSpike对hash结构的定义和redis连接池
package remoteSpike
//远程订单存储健值
type RemoteSpikeKeys struct {
SpikeOrderHashKey string //redis中秒杀订单hash结构key
TotalInventoryKey string //hash结构中总订单库存key
QuantityOfOrderKey string //hash结构中已有订单数量key
}
//初始化redis连接池
func NewPool() *redis.Pool {
return &redis.Pool{
MaxIdle: 10000,
MaxActive: 12000, // max number of connections
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", ":6379")
if err != nil {
panic(err.Error())
}
return c, err
},
}
}
...
func init() {
localSpike = localSpike2.LocalSpike{
LocalInStock: 150,
LocalSalesVolume: 0,
}
remoteSpike = remoteSpike2.RemoteSpikeKeys{
SpikeOrderHashKey: "ticket_hash_key",
TotalInventoryKey: "ticket_total_nums",
QuantityOfOrderKey: "ticket_sold_nums",
}
redisPool = remoteSpike2.NewPool()
done = make(chan int, 1)
done <- 1
}
本地扣库存和统一扣库存
//本地扣库存,返回bool值
func (spike *LocalSpike) LocalDeductionStock() bool{
spike.LocalSalesVolume = spike.LocalSalesVolume + 1
return spike.LocalSalesVolume < spike.LocalInStock
}
......
const LuaScript = `
local ticket_key = KEYS[1]
local ticket_total_key = ARGV[1]
local ticket_sold_key = ARGV[2]
local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))
local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))
-- 查看是否还有余票,增加订单数量,返回结果值
if(ticket_total_nums >= ticket_sold_nums) then
return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)
end
return 0
`
//远端统一扣库存
func (RemoteSpikeKeys *RemoteSpikeKeys) RemoteDeductionStock(conn redis.Conn) bool {
lua := redis.NewScript(1, LuaScript)
result, err := redis.Int(lua.Do(conn, RemoteSpikeKeys.SpikeOrderHashKey, RemoteSpikeKeys.TotalInventoryKey, RemoteSpikeKeys.QuantityOfOrderKey))
if err != nil {
return false
}
return result != 0
}
响应用户信息
...
func main() {
http.HandleFunc("/buy/ticket", handleReq)
http.ListenAndServe(":3005", nil)
}
//处理请求函数,根据请求将响应结果信息写入日志
func handleReq(w http.ResponseWriter, r *http.Request) {
redisConn := redisPool.Get()
LogMsg := ""
<-done
//全局读写锁
if localSpike.LocalDeductionStock() && remoteSpike.RemoteDeductionStock(redisConn) {
util.RespJson(w, 1, "抢票成功", nil)
LogMsg = LogMsg + "result:1,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)
} else {
util.RespJson(w, -1, "已售罄", nil)
LogMsg = LogMsg + "result:0,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)
}
done <- 1
//将抢票状态写入到log中
writeLog(LogMsg, "./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, "\r\n"}, "")
buf := []byte(content)
fd.Write(buf)
}
单机服务压测
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software:
Server Hostname: 127.0.0.1
Server Port: 3005
Document Path: /buy/ticket
Document Length: 29 bytes
Concurrency Level: 100
Time taken for tests: 2.339 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 1370000 bytes
HTML transferred: 290000 bytes
Requests per second: 4275.96 [#/sec] (mean)
Time per request: 23.387 [ms] (mean)
Time per request: 0.234 [ms] (mean, across all concurrent requests)
Transfer rate: 572.08 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 8 14.7 6 223
Processing: 2 15 17.6 11 232
Waiting: 1 11 13.5 8 225
Total: 7 23 22.8 18 239
Percentage of the requests served within a certain time (ms)
50%18
66%24
75%26
80%28
90%33
95%39
98%45
99%54
100%239 (longest request)
...
result:1,localSales:145
result:1,localSales:146
result:1,localSales:147
result:1,localSales:148
result:1,localSales:149
result:1,localSales:150
result:0,localSales:151
result:0,localSales:152
result:0,localSales:153
result:0,localSales:154
result:0,localSales:156
...
总结回顾
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
“12306”的架构到底有多6?相关推荐
- “12306” 的架构到底有多牛逼?
来源: https://juejin.im/post/5d84e21f6fb9a06ac8248149 " 每到节假日期间,一二线城市返乡.外出游玩的人们几乎都面临着一个问题:抢火车票! 1 ...
- 12306”的架构到底有多牛逼?
每到节假日期间,一二线城市返乡.外出游玩的人们几乎都面临着一个问题:抢火车票! 虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会.尤其是春节期间,大家不仅使用12306,还 ...
- 从程序员角度分析,到底“12306”的架构到底有多牛逼?
来源:https://juejin.im/post/5d84e21f6fb9a06ac8248149 每到节假日期间,一二线城市返乡.外出游玩的人们几乎都面临着一个问题:抢火车票! 12306 抢票, ...
- “12306”的架构到底有多牛逼?
每到节假日期间,一二线城市返乡.外出游玩的人们几乎都面临着一个问题:抢火车票! 虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会.尤其是春节期间,大家不仅使用12306,还 ...
- 又到抢票季:12306 的架构到底是不是国内最牛逼的架构?
- 12306 抢票:极限并发带来的思考 - 虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会. 尤其是春节期间,大家不仅使用 12306,还会考虑"智 ...
- “12306”的架构到底有多强大?
本文转载自 GitHub爱好者社区 12306 抢票,极限并发带来的思考 虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会. 尤其是春节期间,大家不仅使用 12306,还会 ...
- 春运抢票:“12306”的架构到底有多牛逼?
作者:绘你一世倾城 来源:https://juejin.im/post/5d84e21f6fb9a06ac8248149 " 每到节假日期间,一二线城市返乡.外出游玩的人们几乎都面临着一个问 ...
- “12306” 的架构到底有多牛逼
在网上看到一篇讲 12306 抢票的文章,我看完后,觉得文章写很完整. 不仅给出了模拟场景的代码,而且也用压测工具测试了并发情况,是一个很好的学习案例,分享给大家共读. 提纲 作者:绘你一世倾城 ht ...
- 架构解密从分布式到微服务:微服务架构到底是什么?
架构解密从分布式到微服务:微服务架构到底是什么? https://www.toutiao.com/i6937907188505657870/?tt_from=weixin&utm_campai ...
最新文章
- Maven实战(八)——常用Maven插件介绍(下)
- 有关web接受管理邮件
- kafka java获取topic_通过编程方式获取Kafka中Topic的Metadata信息
- 【codevs2301】【BZOJ2186】沙拉公主的困惑,数论练习之逆元与φ
- C语言printf函数
- 2013阿里技术嘉年华:阿里数据同步前世今生
- GCD Timer事件的精度
- 基本music matlab,求利用MUSIC算法进行DOA估计的Matlab源程序
- 关于Vue项目导入谷歌翻译api
- JSP-tomcat设置编码格式 配置utf-8(以防网页框以及网页显示的时候中文乱码)
- SQLSERVER不同数据库联表查询
- cs229 课程知识点 简要记录
- java 判断正态分布_如何判断一个样本是否来自正态分布的总体?
- Python 实战之淘宝手机销售分析(数据清洗、可视化、数据建模、文本分析)
- 老牌安全公司CYBER ARK眼中的RPA部署安全问题
- 虚拟机中无ens33文件的解决办法
- Renesas:定时器输入捕获
- 存储卡中各种文件夹用处
- ABAP 销售订单创建BAPI:BAPI_SALESORDER_CREATEFROMDAT2总结
- 【F2C】git常见命令
热门文章
- 面试项目亮点_码农:面试被问到自己项目亮点时,感觉自己的回答虚伪的不行!...
- mysql 按日期拆分成多条记录_mysql性能优化2 设计规范 设计原则 结构优化 拆分 配置优化...
- 安卓手机软件开发_无代码手机app软件开发,让人人都是专业开发工程师
- 计算机网络项目——最小网元设计(阶段三)
- 各科老师的语言风格一览,太真实了哈哈哈哈哈哈
- 实地探访重庆“最复杂立交”:其实并不容易走错路
- jqc3ff继电器引脚图_单片机控制继电器驱动电路图原理分析
- java5新特性静态引用、foreach、自动装箱和泛型枚举以及可变参数的总结
- GCC编译器和GDB调试器常用选项
- 一文理解 K8s 容器网络虚拟化