第一部分 概述
1. 交易型系统设计的一些原则
1.1 高并发原则
- 无状态
- 拆分(系统维度、功能维度、读写维度、AOP 维度、模块维度)
- 服务化(进程内服务 - 单机远程服务 - 集群手动注册服务 - 自动注册和发现服务 - 服务的分组/隔离/路由 - 服务治理如限流/黑白名单)
- 消息队列
- 数据异构(数据异构、数据闭环)
- 缓存银弹(浏览器缓存、App 客户端缓存、CDN 缓存、接入层缓存、应用层缓存、分布式缓存)
- 并发化
2. 高可用原则
- 降级
- 限流
- 切流量
- 可回滚
3. 业务设计原则
- 防重设计(防重 Key、防重表)
- 幂等设计(消息中间件)
- 流程可定义
- 状态与状态机
- 后台操作系统可反馈
- 后台系统审批化(操作日志记录,保证操作可追溯、可审计)
- 文档和注释
- 备份
第二部分 高可用
2. 负载均衡与反向代理
对于一般应用来说,有 Nginx 就可以了,但 Nginx 一般用于七层负载均衡,吞吐量有一定限制,为了提升整体吞吐量,会在 DNS 和 Nginx 之间引入接入层,如使用 LVS(软件负载均衡器)、F5(硬负载均衡器)可以做四层负载均衡,即首先 DNS 解析到 LVS/F5,然后 LVS/F5转发给 Nginx,再由 Nginx 转发给后端 Server。
负载均衡主要关注几个方面:
- 上游服务器配置:使用 upstream server 配置上游服务器
- 负载均衡算法:配置多个上游服务器时的负载均衡机制
- 失败重试机制
- 服务器心跳检查
负载均衡算法 :
- round-robin:轮询算法,默认 LB 算法,配合 weight 配置可以实现基于权重的轮询
- ip_hash:根据 IP 进行 LB,相同的 IP 将 LB 到同一个upstream server
- hash key:对某一个 key 进行 hash或者使用一致性 hash 算法进行 LB
- least_conn:将请求 LB 到最少活跃连接的 upstream server
- least_time:Nginx 商业版功能,基于最小平均响应时间进行 LB
健康检查:
- TCP 心跳检查
- HTTP心跳检查
3. 隔离术
隔离是指将系统或资源分割开,系统隔离是为了在系统发生故障时,能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应,从而保证只有出问题的服务不可用,其他服务还是可用的。资源隔离通过隔离来减少资源竞争,保障服务间的相互不影响和可用性。出现系统问题时,可以考虑 LB 路由、自动/手动切换分组或者降级等手段来保障可用性。
- 线程隔离(不同的线程池)
- 进程隔离(单实例到多子系统)
- 集群隔离(不同的集群)
- 机房隔离(不同的机房)
- 读写隔离(主从模式等)
- 快慢隔离
- 动静隔离(CDN)
- 爬虫隔离
- 热点隔离(秒杀、抢购做成独立系统或服务隔离,对于读热点,可以使用多级缓存,对于写热点,可以使用缓存+队列模式削峰,)
- 资源隔离
- 环境隔离(测试环境、预发布环境、灰度环境、正式环境)
- 压测隔离(真实数据、压测数据)
- AB 测试
- 缓存隔离
- 查询隔离(简单、批量、复杂条件查询分别路由到不同集群)
- 使用 Hystrix 隔离
- 基于 Servlet 3 实现请求隔离
4. 限流
一般幵发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、 限制瞬时并发数(如Nginx的limitconn模块,用来限制瞬时并发连接数)、限制时间 窗口内的平均速率(如Guava的RateLimiter、Nginx的limitreq模块,用来限制每秒的 平均速率),以及限制远程接口调用速率、限制MQ的消费速率等。另外,还可以根据 网络连接数、网络流量、CPU或内存负载等来限流。
限流算法:
- 令牌桶算法
- 漏桶算法
- 计数器算法
应用级限流:
- 限流总并发、连接、请求数(限制 TPS、QPS)
- 限流总资源数(池化,如数据库连接池、线程池)
- 限流某个接口的总并发、请求数(使用AtomicLong或者Semaphore进行限流,Hystrix)
- 限流某个接口的时间窗请求数(如使用Guava Cache来存储计数器)
- 平滑限流某个接口的请求数(Guava RateLimiter)
分布式限流
分布式限流最关键的是要将限流服务做成原子化,解决方案可以使用 Redis+Lua 或者 Nginx+Lua 技术进行实现
接入层限流
接入层通常指请求流量的入口,主要目的有:负载均衡、非法请求过滤、请求聚合、缓存、降级、限流、A/B测试、服务质量监控等。 Nginx接入层限流可以使用 Nginx 自带的两个模块:连接数限流模块 ngxhttplimitconnmodule 和漏桶算法实现的请求限流模块 ngxhttplimitreqmodule,或者 OpenResty 提供的 Lua 限流模块 lua-resty-limit-traffic
节流
防止多个相同事件连续重复执行,主要有throttleFirst、throttleLast、throttleWithTimeout
5. 降级特技
降级的最终目的是保证核心服务可用,即使是有损的。 降级需要根据系统的吞吐量、响应时间、可用率等条件进行手工降级或自动降级。
5.1 降级预案
自动开关降级/人工开关降级,读服务降级/写服务降级,多级降级,页面降级/页面片段降级/页面异步请求降级/服务功能降级/读降级/写降级/爬虫降级/风控降级
5.2 自动开关降级
- 超时降级
- 统计失败次数降级
- 故障降级
- 限流降级
5.3 人工开关降级
5.4 读服务降级
5.5 写服务降级
5.6 多级降级
- 页面 js 降级开关
- 接入层降级开关
- 应用层降级开关
5.7. 配置中心(通过配置方式动态开启/关闭降级开关)
- 应用层 API 封装
- 使用配置文件实现开关配置
- 使用配置中心实现开关配置
5.8. 使用 Hystrix 实现降级
5.9. 使用 Hystrix 实现熔断
6. 超时与重试机制
代理层超时与重试
如Haproxy/Nginx/Twemproxy,需设置代理与后端真实服务器之间的网络连接/读/写超时时间
Web 容器超时
提供HTTP服务运行环境的,如Tomcat/Jetty,需设置客户端与容器之间的网络连接/读/写超时时间,和在此容器中默认 socket 网络连接/读/写超时时间
中间件客户端超时与重试
如 Dubbo、MQ、HttpClient 等,需设置客户的网络连接/读/写超时时间与失败重试机制
数据库客户端超时
如MySQL/Oracle/PG,需要分别设置JDBC Connection、Statement的网络连接/读/写超时时间,事务超时时间,获取连接池等待时间
NOSQL客户端超时
如Mongo、Redis,需要设置其网络连接/读/写超时时间,获取连接池等待时间
业务超时
如订单取消任务、超时活动关闭,还有如Future#get(timeout, unix)限制某个接口的超时时间
前端Ajax超时
浏览器通过Ajax访问时的网络连接/读/写超时时间
总结:
最重要的是网络相关的超时设置。 超时后应该有相应的处理策略,如重试(稍后再试、尝试其它分组服务、尝试其它机房服务)、摘掉不存活节点(负载均衡/分布式缓存场景下)、托底(返回历史数据/静态数据/缓存数据)、等待页或错误页。 对于非幂等写服务应避免重试,可以提前生成唯一流水号保证写服务操作通过流水号来实现幂等操作。 在进行DB/缓存服务器操作时,需经常检查慢查询,同时在超时严重时,可以直接将该服务降级。 对于有负载均衡的中间件,考虑配置心跳/存活检查。
7. 回滚机制
回滚是指当程序或数据出错时,将程序和数据恢复到最近的一个正确版本的行为。常见的如事务回滚、代码库回滚、部署版本回滚、数据版本回滚、静态资源版本回滚等。
7.1 事务回滚
单库事务回滚直接使用相关 SQL,分布式数据库可以使用分布式事务,如两阶段提交、三阶段提交协议。另外可以考虑入事务表、消息队列、补偿机制(执行/回滚)、TCC 模式(预占/确认/取消)、Sagas模式(拆分事务+补偿机制)等实现最终一致性。
7.2 代码库回滚
Git/SVN
7.3 部署版本回滚
部署版本化、小版本增量发布、大版本灰度发布、架构升级并发发布
7.4 数据版本回滚
设计版本化数据结构时,有全量和增量两种思路
7.5 静态资源版本回滚
全量新版本保证版本可追溯。清理CDN 缓存,在新 URL上添加随机数并清理浏览器缓存。请求参数加上版本号
8. 压测与预案
在大促来临之前,研发人员需要对现有系统进行梳理,发现系统瓶颈和问题,然后 进行系统调优来提升系统的健壮性和处理能力。一般通过系统压测来发现系统瓶颈和问 题,然后进行系统优化和容灾(如系统参数调优、单机房容灾、多机房容灾等)。即使 己经把系统优化和容灾做得非常好了,但也存在一些不稳定因素,如网络、依赖服务的 SLA不稳定等,这就需要我们制定应急预案,在出现这些因素后进行路由切换或降级处 理。在大促之前需要进行预案演习,确保预案的有效性。
8.1 系统压测
一般指性能压力测试,评估系统的稳定性和性能,通过压测数据进行系统容量评估,决定是否需要扩容和缩容。压测要有压测方案 {如压测接口、并发量、压测策略(突发、逐步加压、并发量)、压测指标(机器负载、QPS/TPS、响应时间)},之后产出压测报告{压测方案、机器负载、QPSTPS、响应时间(avg、min、max)、成功率、相关参数(JVM参数、压缩参数)等},根据压测报告分析的结果进行系统优化和容灾。
- 线下压测(使用Jmeter、Apache ab 压测某个接口或某个组件(如DB 连接池),然后进行调优,实现单个接口或组件性能最优。线下压测环境和线上不同,适合组件级压测,数据只能参考)
- 线上压测(按读写分为读压测、写压测、混合压测,按数据仿真度分为仿真压测和引流压测(如TCPCopy),按是否给用户提供服务分为隔离集群压测和线上集群压测。注意离散压测(选择的数据应该是分散的或长尾的)和全链路压测)
8.2. 系统优化和容灾
拿到压测报告后,接下来分析报告,然后进行有针对性的优化,如硬件升级、系统扩容、参数调优、代码优化、架构优化(如加缓存、读写分离、历史数据归档)等,根据压测数据因地制宜地解决。在系统优化时,要进行代码走查,发现不合理的参数配置,如超时时间、降级策略、缓存时间等。在系统压测时进行慢查询排查,如Redis、MySQL等。在应用系统扩容方面,根据往年流量和运营业务方沟通,评估是否需要扩容及扩容容量。扩容时考虑系统容灾,如分组部署、跨机房部署等,保证高可用。
8.3. 应急预案
在系统压测后会发现一些系统瓶颈,在系统优化之后会提升系统吞吐量并降低响应时间,容灾之后的系统可用性得以保障,但还存在一些风险,如网络抖动、某台机器负载过高、某个服务变慢、DB load值过高等,为了防止因为这些问题导致系统雪崩,需要制定应急预案。
应急预案可分几步执行:
- 系统分级(划分交易核心系统和交易支撑系统,对不同级别的系统实施不同的质量保障)
- 全链路分析(从用户入口到后端存储,梳理出关键路径,进行评估和预案,防止问题的级联效应和雪崩效应)
- 配置监控报警
- 制定应急预案
最后,要对关联路径实施监控报警,包括服务器监控(CPU使用率、磁盘使用率、网络带宽等)、系统监控(系统存活、URL 状态/内容监控、端口存活等)、JVM 监控(堆内存、GC 次数、线程数等)、接口监控(接口调用量『每秒/每分钟』、接口性能『TOP50/TOP99/TOP999』、接口可用率等)。然后配置报警策略,如监控时间段、报警阈值、通知方式等。在报警后要观察系统状态、监控数据或者日志来查看系统是否真的存在故障,如果确实是故障,则应即及时执行相关预案处理,避免故障扩散。
本文暂时没有评论,来添加一个吧(●'◡'●)