目录导航
-
撤销(Ctrl+Z)
-
重做(Ctrl+Y)
-
清空
-
H
标题(Ctrl+1~6)
- 一级标题
- 二级标题
- 三级标题
- 四级标题
- 五级标题
- 六级标题
-
粗体(Ctrl+B)
-
斜体(Ctrl+I)
-
删除线
-
插入引用(Ctrl+Q)
-
无序列表(Ctrl+U)
-
有序列表(Ctrl+O)
-
表格
-
插入分割线
-
插入链接(Ctrl+L)
-
插入图片
- 添加图片链接
-
插入代码块
-
保存(Ctrl+S)
-
开启预览
-
开启目录导航
-
关闭同步滚动
-
全屏(按ESC还原)
# 目标 * 秒杀系统高并发的服务器架构设计和实践,保证服务不会因为流量超载而中断。 # 特点 * 瞬时并发访问量比较高 * 读多写少,读操作比较简单 * 写 强一致性:例如:商品的库存跟卖出的商品数保持实时一致。 * 读 弱一致性:例如:可以读到有库存,但是下单时没有库存,(12306就是看到有票,但是买的时候提示卖完了)。 # 难点 ## 稳定性难 * 高并发下,某个小依赖可能会雪崩 * 流量预期难精确,过高也造成雪崩 * 分布式集群,机器多,出故障概率高 ## 准确性难 * 库存、抢购成功数、创建订单数要保持一致 ## 高性能难 * 有限成本下需要做到极致性能 # 基本需求 ![图片alt](/media/article/image/2021-01-04/1609750510226.png ''图片title'') ## 使用预扣库存方案 * 创建订单先扣库存,支付超时时,把库存还回。 # 设计思路 ## 减而治之 * CDN原理:减少读的压力 * nginx限流:进行过载保护 * 异步队列:均匀分配流量 ## 分而治之 * nginx负载均衡:分散流量 # 实现 * 我们可以把秒杀活动分为三个部分 ## 活动开始之前 ### 问题 * 这个阶段用户会大量刷新详情页,导致详情页瞬时访问请求激增。 ### 解决 * 使用CDN服务或者浏览器缓存服务,减少服务器的压力,保证详情页能够正常访问 ## 活动开始(查库存) ### 问题 * 用户大量点击秒杀按钮,导致大量并发请求查询库存。 ### 解决办法 #### 服务器接入层 * 限制前端按钮点击频率,防止用户瞬时重复点击,减少服务器接入层的压力,如:1s内只能按一次或只有一次有效。 #### 服务器server层 * 服务器接入层限制单个IP的访问访问频率,减少服务器server层的压力,如:1个IP在1s内只有一个请求有效,其他请求直接返回503显示错误提示页。 * 服务器接入层限制最大并发连接数和总连接数,防止流量过载导致服务器崩溃。 #### 数据库层 * 使用Redis缓存,减少对数据库层的读压力。 ## 活动进行(扣库存) ### 问题一 * 当查到库存有余量时,就会进行扣除库存和处理订单,此时面临,查库存和扣库存的一致性问题。 ### 解决办法一 * 由于查库存和扣库存是两个操作,无法保证其一致性,可以使用Lua脚本原子性的去执行这两个操作。(Redis会把Lua脚本当成一个操作,执行过程中不会被打断) ### 解决办法二 * 使用Redis分布式锁,先让客户端去申请分布式锁,只有拿到分布式锁的客户端才会进行查库存和扣库存的操作。 ### 问题二 * 查库存和扣库存都是在Redis中进行的,操作简单再加上Redis的高性能,是可以抗住压力,但是库存扣除成功后,数据库面临大量的订单的写入问题。 ### 解决办法 * 创建Redis消息队列,把写入订单的请求存到Redis中,数据库读取Redis消息队列中的写入订单的请求,进行批量写入。(也可以使用专门的消息队列服务Kafka、rabbitmq) # 架构设计 ## 架构原则 ### 稳定性 * 减少第三方依赖,同时自身服务部署也需要做到隔离 * 压测、降级、限流方案、确保核心服务可用 * 需要健康度检查,整个链路避免单点 ### 高性能 * 缩短单请求访问路径,减少IO * 减少接口数、降低吞吐数据量(精简字段名字、字段长度),请求次数减少 ## CDN架构 ![图片alt](/media/article/image/2021-01-05/1609827098974.png ''图片title'') ## 服务架构 ![图片alt](/media/article/image/2021-01-05/1609827318616.png ''图片title'') ## 架构优化 ![图片alt](/media/article/image/2021-01-05/1609827560868.png ''图片title'') ## 提高单台服务器性能 ### 减少IO * 使用本地内存 ### 减少CPU上下文切换(多进程切换,多线程切换) * 单进程多协程 * 异步IO # 压测工具 ## 安装 ``` yum -y install httpd-tools ab ``` ## 使用 * -n表示总计请求多少次,-c表示一次并发是多少 ``` ab -n 100 -c 10 [url] ``` # nginx限流 ## 配置 * 连接数限制,即并发数(ngx_http_limit_conn_module) * 请求速率限速,限制ip在单位时间内的请求数(ngx_http_limit_req_module) ## nginx.conf配置 ``` limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; server{ # 可以配置到整个php,也可以配置到指定访问地址 location ~ \.php$ { limit_req zone=mylimit burst=1 nodelay; } # 最大并发连接数100 limit_conn one 100; # 该服务提供的总连接数不得超过1000,超过请求的会被拒绝 limit_conn perserver 1000; } ``` ### 第一段配置参数 * $binary_remote_addr :表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址 * remote_addr变量的长度为7字节到15字节。 * binary_remote_addr变量的长度是固定的4字节。 * zone=mylimit:10m:表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息 * rate=1r/s:表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,即每秒只处理一个请求,还可以有比如30r/m的,即限制每2秒访问一次,即每2秒才处理一个请求。 ### 第二段配置参数: * zone=mylimit :设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应 * burst=1:重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为1的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有1个,超过的请求会直接报503的错误然后返回。 * nodelay:如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况。(这里的rate的单位是:r/s)如果没有设置,则所有请求会依次等待排队。当前设置是1r/s,burst=1,表示1s内有2个请求是可以正常返回的。 ## 限流算法 * 令牌桶:均匀的生成令牌放到令牌桶中,每请求一次就从令牌桶中扣除一个令牌,当令牌桶中没有令牌时,返回503(可以处理突发流量) * 漏桶:把请求放到漏桶中进行排队,通过漏桶均匀流出,桶满则溢出返回503。(不能处理突发流量) * 计数器:(在应用层)单位时间内计数器进行计数,超过计数则返回503。 # CDN * 内容分发网络(content delivery network) * 缩短访问路径,减少源站压力,提高内容响应速度 * 为源站提供安全保护 ![图片alt](/media/article/image/2021-01-04/1609743273728.png ''图片title'') ![图片alt](/media/article/image/2021-01-04/1609743320532.png ''图片title'') ![图片alt](/media/article/image/2021-01-04/1609743391228.png ''图片title'') # server层带权轮询 ``` upstream test{ service 192.168.0.2:8080 weight=1; # 表示占所有服务器的1/4 service 192.168.0.2:8080 weight=3; # 表示占所有服务器的3/4 } ``` # 消息队列 * 实际是链表,头插尾出,高并发下容易发生堵塞,为避免消息丢失,可通过写入实时消息队列进行延时处理。 ## 消息队列 作用 * 提高请求响速度,通过异步,如:创建订单发push,短信提醒等。 * 高并发时,可以起到削峰,如:双十一创建订单 * 延时队列,时间维度任务触发,如:发货提醒
目标
- 秒杀系统高并发的服务器架构设计和实践,保证服务不会因为流量超载而中断。
特点
- 瞬时并发访问量比较高
- 读多写少,读操作比较简单
- 写 强一致性:例如:商品的库存跟卖出的商品数保持实时一致。
- 读 弱一致性:例如:可以读到有库存,但是下单时没有库存,(12306就是看到有票,但是买的时候提示卖完了)。
难点
稳定性难
- 高并发下,某个小依赖可能会雪崩
- 流量预期难精确,过高也造成雪崩
- 分布式集群,机器多,出故障概率高
准确性难
- 库存、抢购成功数、创建订单数要保持一致
高性能难
- 有限成本下需要做到极致性能
基本需求
![图片alt](/media/article/image/2021-01-04/1609750510226.png ‘‘图片title’’)
使用预扣库存方案
- 创建订单先扣库存,支付超时时,把库存还回。
设计思路
减而治之
- CDN原理:减少读的压力
- nginx限流:进行过载保护
- 异步队列:均匀分配流量
分而治之
- nginx负载均衡:分散流量
实现
- 我们可以把秒杀活动分为三个部分
活动开始之前
问题
- 这个阶段用户会大量刷新详情页,导致详情页瞬时访问请求激增。
解决
- 使用CDN服务或者浏览器缓存服务,减少服务器的压力,保证详情页能够正常访问
活动开始(查库存)
问题
- 用户大量点击秒杀按钮,导致大量并发请求查询库存。
解决办法
服务器接入层
- 限制前端按钮点击频率,防止用户瞬时重复点击,减少服务器接入层的压力,如:1s内只能按一次或只有一次有效。
服务器server层
- 服务器接入层限制单个IP的访问访问频率,减少服务器server层的压力,如:1个IP在1s内只有一个请求有效,其他请求直接返回503显示错误提示页。
- 服务器接入层限制最大并发连接数和总连接数,防止流量过载导致服务器崩溃。
数据库层
- 使用Redis缓存,减少对数据库层的读压力。
活动进行(扣库存)
问题一
- 当查到库存有余量时,就会进行扣除库存和处理订单,此时面临,查库存和扣库存的一致性问题。
解决办法一
- 由于查库存和扣库存是两个操作,无法保证其一致性,可以使用Lua脚本原子性的去执行这两个操作。(Redis会把Lua脚本当成一个操作,执行过程中不会被打断)
解决办法二
- 使用Redis分布式锁,先让客户端去申请分布式锁,只有拿到分布式锁的客户端才会进行查库存和扣库存的操作。
问题二
- 查库存和扣库存都是在Redis中进行的,操作简单再加上Redis的高性能,是可以抗住压力,但是库存扣除成功后,数据库面临大量的订单的写入问题。
解决办法
- 创建Redis消息队列,把写入订单的请求存到Redis中,数据库读取Redis消息队列中的写入订单的请求,进行批量写入。(也可以使用专门的消息队列服务Kafka、rabbitmq)
架构设计
架构原则
稳定性
- 减少第三方依赖,同时自身服务部署也需要做到隔离
- 压测、降级、限流方案、确保核心服务可用
- 需要健康度检查,整个链路避免单点
高性能
- 缩短单请求访问路径,减少IO
- 减少接口数、降低吞吐数据量(精简字段名字、字段长度),请求次数减少
CDN架构
![图片alt](/media/article/image/2021-01-05/1609827098974.png ‘‘图片title’’)
服务架构
![图片alt](/media/article/image/2021-01-05/1609827318616.png ‘‘图片title’’)
架构优化
![图片alt](/media/article/image/2021-01-05/1609827560868.png ‘‘图片title’’)
提高单台服务器性能
减少IO
- 使用本地内存
减少CPU上下文切换(多进程切换,多线程切换)
- 单进程多协程
- 异步IO
压测工具
安装
yum -y install httpd-tools ab
使用
- -n表示总计请求多少次,-c表示一次并发是多少
ab -n 100 -c 10 [url]
nginx限流
配置
- 连接数限制,即并发数(ngx_http_limit_conn_module)
- 请求速率限速,限制ip在单位时间内的请求数(ngx_http_limit_req_module)
nginx.conf配置
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
server{
# 可以配置到整个php,也可以配置到指定访问地址
location ~ \.php$ {
limit_req zone=mylimit burst=1 nodelay;
}
# 最大并发连接数100
limit_conn one 100;
# 该服务提供的总连接数不得超过1000,超过请求的会被拒绝
limit_conn perserver 1000;
}
第一段配置参数
- $binary_remote_addr :表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址
- remote_addr变量的长度为7字节到15字节。
- binary_remote_addr变量的长度是固定的4字节。
- zone=mylimit:10m:表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息
- rate=1r/s:表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,即每秒只处理一个请求,还可以有比如30r/m的,即限制每2秒访问一次,即每2秒才处理一个请求。
第二段配置参数:
- zone=mylimit :设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应
- burst=1:重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为1的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有1个,超过的请求会直接报503的错误然后返回。
- nodelay:如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况。(这里的rate的单位是:r/s)如果没有设置,则所有请求会依次等待排队。当前设置是1r/s,burst=1,表示1s内有2个请求是可以正常返回的。
限流算法
- 令牌桶:均匀的生成令牌放到令牌桶中,每请求一次就从令牌桶中扣除一个令牌,当令牌桶中没有令牌时,返回503(可以处理突发流量)
- 漏桶:把请求放到漏桶中进行排队,通过漏桶均匀流出,桶满则溢出返回503。(不能处理突发流量)
- 计数器:(在应用层)单位时间内计数器进行计数,超过计数则返回503。
CDN
- 内容分发网络(content delivery network)
- 缩短访问路径,减少源站压力,提高内容响应速度
- 为源站提供安全保护
![图片alt](/media/article/image/2021-01-04/1609743273728.png ‘‘图片title’’)
![图片alt](/media/article/image/2021-01-04/1609743320532.png ‘‘图片title’’)
![图片alt](/media/article/image/2021-01-04/1609743391228.png ‘‘图片title’’)
server层带权轮询
upstream test{
service 192.168.0.2:8080 weight=1; # 表示占所有服务器的1/4
service 192.168.0.2:8080 weight=3; # 表示占所有服务器的3/4
}
消息队列
- 实际是链表,头插尾出,高并发下容易发生堵塞,为避免消息丢失,可通过写入实时消息队列进行延时处理。
消息队列 作用
- 提高请求响速度,通过异步,如:创建订单发push,短信提醒等。
- 高并发时,可以起到削峰,如:双十一创建订单
- 延时队列,时间维度任务触发,如:发货提醒
评论
请
登录后发表观点