文章目录
- 高可用系统考虑哪些?
-
- [一、架构层:从 "单点依赖" 到 "冗余容错"](#一、架构层:从 “单点依赖” 到 “冗余容错”)
- [二、数据层:确保 "数据不丢、一致可用"](#二、数据层:确保 “数据不丢、一致可用”)
- 三、故障处理:"自动容错、快速恢复"
- 四、流量与资源:"抗住峰值、避免过载"
- 五、监控与运维:"可观测、可追溯"
- 六、安全与合规:"防攻击、防破坏"
- 微服务相关知识
- 服务治理
- 指标
-
- P99,P999指标含义是什么?
- [TP50, TP99](#TP50, TP99)
- QPS和TPS的区别
- 分布式一致需要校验哪些内容?
- 线程池
-
- 线程池的作用
- 线程池的线程复用理解
- 线程创建和销毁主要消耗以下系统资源:
- 线程池的数据结构设计
- [核心线程 2,最大线程 5,提交 3 个长时间任务的执行流程](#核心线程 2,最大线程 5,提交 3 个长时间任务的执行流程)
- 先扩容到最大线程再入队列的设计分析
高可用系统考虑哪些?
一、架构层:从 "单点依赖" 到 "冗余容错"
架构是高可用的基础,核心思路是消除单点故障,通过冗余设计让系统在部分组件失效时仍能正常运行。
- 多节点冗余部署
无状态服务集群化:将应用服务(如 API 服务、Web 服务)部署为多节点集群,避免单台服务器宕机导致服务中断。
示例:Java 应用部署在 3 台服务器上,通过负载均衡(如 Nginx、SLB)分发请求,即使 1 台宕机,剩余 2 台仍能承接流量。
关键:服务需设计为无状态(即不存储本地会话 / 数据,会话可通过 Redis 共享、数据存储在分布式数据库),确保任意节点都能处理请求。
有状态服务分片 / 主从:对存储类、计算类有状态服务(如数据库、缓存、消息队列),通过 "分片" 或 "主从复制" 实现冗余。
主从复制:如 MySQL 主从(主库写入,从库读)、Redis 主从,主库故障时可切换到从库;
分片(Sharding):如 Elasticsearch、MongoDB 的分片集群,将数据分散到多个节点,单个分片故障不影响整体服务。 - 多区域 / 多可用区部署
跨可用区(AZ)部署:同一城市内的不同机房(可用区)物理隔离(独立电源、网络),集群节点分散在多个 AZ,避免单 AZ 断电 / 断网导致整体失效。
示例:阿里云 ECS 实例分布在 "上海可用区 A" 和 "上海可用区 B",负载均衡跨 AZ 分发请求。
跨地域(Region)部署:核心业务可跨城市 / 国家部署(如 "上海 + 北京" 双地域),应对极端灾难(如地震、区域网络中断),但需解决 "数据同步延迟" 和 "流量路由" 问题(如通过 DNS 智能解析将用户引导至最近可用地域)。 - 避免 "强依赖链":降级与解耦
依赖服务解耦:通过 "消息队列"(如 Kafka、RabbitMQ)或 "服务网格"(如 Istio)拆分强依赖,将 "同步调用" 改为 "异步通信",避免一个服务故障拖垮整条链路。
示例:用户下单后,"订单服务" 无需同步等待 "物流服务" 响应,而是发送消息到队列,物流服务异步消费,即使物流服务暂时不可用,订单仍能正常创建。
明确依赖层级:避免 "循环依赖"(如 A 依赖 B,B 依赖 A),同时对非核心依赖(如 "用户画像服务""统计分析服务")做 "降级开关",核心流程(如 "支付""下单")不依赖非核心服务。
二、数据层:确保 "数据不丢、一致可用"
数据是系统的核心资产,高可用设计需解决 "数据丢失""数据不一致""存储服务不可用" 三大问题。
- 数据持久化与备份
多副本存储:核心数据至少存 3 个副本(如 HDFS 默认 3 副本、Redis Cluster 1 主 2 从),单个副本损坏可通过其他副本恢复,避免 "单副本磁盘损坏导致数据丢失"。
定时备份 + 异地容灾:
定时全量备份(如 MySQL 每天凌晨全量备份)+ 增量日志备份(如 binlog),确保可回滚到任意时间点;
备份数据异地存储(如上海的备份同步到北京),应对 "本地机房彻底损毁" 的极端情况。
避免 "写单点":对写入密集型场景,采用 "分布式存储"(如 TiDB、CockroachDB),支持多节点写入,避免单主库写入瓶颈或故障导致无法写入。 - 数据一致性权衡
高可用与强一致性往往存在矛盾(如 "CAP 定理":分布式系统无法同时满足一致性 Consistency、可用性 Availability、分区容错性 Partition Tolerance,需取舍),需根据业务场景选择合适的一致性级别:
强一致性:核心场景(如支付、转账)必须保证,可通过 "分布式事务"(如 TCC、SAGA)或 "主从同步 + 读写分离"(主库写入后,从库同步完成再允许读)实现,但会牺牲部分可用性;
最终一致性:非核心场景(如商品评论、用户积分)可接受短时间不一致,通过 "异步同步"(如消息队列重试)最终达成一致,优先保证可用性。
三、故障处理:"自动容错、快速恢复"
即使做了冗余设计,故障仍会发生,需通过 "故障检测""自动切换""故障恢复" 机制,将故障影响降到最低。
- 故障检测:及时发现问题
健康检查:对所有组件(服务器、服务、数据库)做定时健康检测,常用方式包括:
心跳检测:组件定期向 "注册中心"(如 Nacos、Eureka)发送心跳,超时未发送则判定为故障;
主动探测:监控系统(如 Prometheus+Grafana)定期发送请求(如/health接口),检测返回状态、响应时间,异常则告警;
日志 / 指标异常检测:通过 ELK 分析日志中的错误信息,或监控 CPU、内存、磁盘使用率(如磁盘满 100% 时触发告警)。 - 自动故障转移(Failover)
无状态服务:负载均衡自动剔除故障节点(如 Nginx 的health_check模块,检测到节点不可用则不再分发请求),无需人工干预;
有状态服务:主节点故障时自动切换到从节点,核心是 "选主" 机制:
基于共识算法:如 ZooKeeper、etcd 的 Raft 协议,确保集群在网络分区时仍能选出唯一主节点,避免 "脑裂"(如 MySQL 主从用 MGR、Redis 用 Redis Cluster 的选主机制);
切换触发:检测到主节点故障后,自动将从节点提升为主节点,并更新路由(如数据库连接池自动切换到新主库)。 - 故障恢复:减少 downtime
自动重启:对临时故障(如进程 OOM、网络闪断),通过 "守护进程"(如 Linux 的 systemd、Docker 的 restart:always)自动重启服务,避免人工介入延迟;
数据恢复自动化:备份数据支持 "一键恢复",并提前演练恢复流程(如每月模拟一次数据丢失,验证恢复耗时是否符合预期);
灰度恢复:服务恢复后不立即承接全量流量,而是通过 "灰度发布"(如先接入 10% 流量,观察无异常再逐步扩容),避免故障复现。
四、流量与资源:"抗住峰值、避免过载"
高可用不仅要 "容错",还要 "抗造"------ 在流量峰值或资源紧张时,避免系统因过载而崩溃。
- 流量控制:削峰、限流、降级
削峰填谷:通过消息队列缓冲突发流量,将 "瞬间高并发" 转化为 "匀速消费",避免直接冲击后端服务。
示例:秒杀场景中,用户下单请求先发送到 Kafka,订单服务按能力消费,避免瞬间 10 万请求压垮数据库。
限流保护:对接口或服务设置 "流量上限",超过上限则拒绝请求(如返回 "系统繁忙,请稍后再试"),防止过载。
实现方式:令牌桶(Guava RateLimiter)、漏桶算法,或基于网关(如 Gateway、APISIX)统一限流;
粒度:可按 "接口维度"(如登录接口限 100QPS)、"用户维度"(如单用户限 10 次 / 分钟)、"IP 维度"(防恶意攻击)。
服务降级:当系统资源紧张(如 CPU 使用率达 90%)或非核心服务故障时,关闭非核心功能 ,优先保障核心流程。
示例:电商大促时,关闭 "商品历史价格查询""评价晒单" 等非核心功能,确保 "下单 - 支付" 流程可用;
实现:通过 "开关中心"(如 Apollo、Nacos 配置中心)动态控制,无需重启服务。 - 资源隔离:避免 "一损俱损"
线程池隔离:不同业务(如 "支付""订单""商品")使用独立线程池,避免一个业务线程耗尽导致其他业务不可用。
示例:支付服务的线程池耗尽时,订单服务的线程池仍能正常处理请求。
资源池隔离:数据库、缓存等资源按业务拆分,如 "订单库""用户库" 独立部署,避免订单库压力过大影响用户查询;
物理机隔离:核心服务(如支付)部署在独立服务器,不与非核心服务(如日志分析)共享资源,避免非核心服务占用 CPU / 内存导致核心服务卡顿。
五、监控与运维:"可观测、可追溯"
高可用不是 "一次性设计",而是 "持续优化"------ 需通过监控感知系统状态,通过运维保障长期稳定。
- 全链路可观测性
三大支柱:
日志(Logging):收集所有组件的日志(应用日志、数据库慢查询日志、网关访问日志),通过 ELK/PLG 栈集中分析,快速定位故障(如用户下单失败,可通过订单 ID 追溯全链路日志);
指标(Metrics):监控核心指标,如服务 QPS、响应时间(P99/P95)、错误率、服务器 CPU / 内存 / 磁盘、数据库连接数 / 慢查询数,通过 Prometheus+Grafana 可视化,设置阈值告警(如错误率 > 1% 触发短信告警);
链路追踪(Tracing):通过 SkyWalking、Jaeger 等工具,追踪一个请求从 "用户端→网关→服务 A→服务 B→数据库" 的全链路耗时,定位哪一步延迟过高(如发现数据库查询耗时 500ms,需优化 SQL)。 - 运维自动化与演练
自动化部署:通过 CI/CD(Jenkins、GitLab CI)实现 "代码提交→自动测试→自动部署",减少人工操作失误(如配置错误导致服务不可用);
基础设施即代码(IaC):用 Terraform、Ansible 管理服务器、网络、存储等基础设施,确保环境一致性(避免 "开发环境正常,生产环境故障");
混沌工程(Chaos Engineering):主动模拟故障(如关闭一台服务器、断开数据库主从同步、注入网络延迟),验证系统的容错能力,提前发现隐藏的高可用漏洞(如 Netflix 的 Chaos Monkey 工具)。
六、安全与合规:"防攻击、防破坏"
恶意攻击(如 DDoS、SQL 注入)是高可用的隐形威胁,需从安全层面保障系统不被外力破坏。
DDoS 防护:通过 CDN(如阿里云 CDN、Cloudflare)、高防 IP 清洗流量,抵御 "流量型 DDoS"(如 SYN Flood);通过 WAF(Web 应用防火墙)防御 "应用层 DDoS"(如频繁请求登录接口);
接口安全:对外部接口做身份认证(如 OAuth2.0、API 密钥)、防重放(如 timestamp+nonce)、数据加密(如 HTTPS),避免恶意调用耗尽资源;
数据安全:敏感数据(如手机号、银行卡号)加密存储(如 AES 加密),避免数据泄露导致的服务下线风险(合规要求,如 GDPR、个人信息保护法)。
微服务相关知识
服务治理
TODO:北极星
指标
P99,P999指标含义是什么?
P99 200毫秒的具体含义
"P99":是第99百分位数的缩写,表示在所有的数据样本中,有**99%**的数据点都小于或等于这个值。
"200毫秒":是在这个P99分位数下的具体数值,表示该值是200毫秒。
一组n个观测值按数值大小排列,处理P%位置的值称第P百分位数
用我们软件开发行业的例子通俗来讲就是,假设有100个请求,按照响应时间从小到大排列,位置为X的值,即为PX值。
P1就是响应时间最小的请求,P10就是排名第十的请求,P100就是响应时间最长的请求。
在真正使用过程中,最常用的主要有P50(中位数)、P95、P99。
P50: 即中位数值。100个请求按照响应时间从小到大排列,位置为50的值,即为P50值。如果响应时间的P50值为200ms,代表我们有半数的用户响应耗时在200ms之内,有半数的用户响应耗时大于200ms。如果你觉得中位数值不够精确,那么可以使用P95和P99.9
P95:响应耗时从小到大排列,顺序处于95%位置的值即为P95值。
还是采用上面那个例子,100个请求按照响应时间从小到大排列,位置为95的值,即为P95值。 我们假设该值为200ms,那这个值又表示什么意思呢?
意思是说,我们对95%的用户的响应耗时在200ms之内,只有5%的用户的响应耗时大于200ms,据此,我们掌握了更精确的服务响应耗时信息。
P99.9:许多大型的互联网公司会采用P99.9值,也就是99.9%用户耗时作为指标,意思就是1000个用户里面,999个用户的耗时上限 ,通过测量与优化该值,就可保证绝大多数用户的使用体验。
至于P99.99值,优化成本过高,而且服务响应由于网络波动、系统抖动等不能解决之情况,因此大多数时候都不考虑该指标。
TP50, TP99
https://www.cnblogs.com/zhangxinglong/p/14324858.html
https://blog.csdn.net/qq_39809613/article/details/102853986
TP50:指在一个时间段内(如5分钟),统计该方法每次调用所消耗的时间,并将这些时间按从小到大的顺序进行排序,取第50%的那个值作为TP50 值;配置此监控指标对应的报警阀值后,需要保证在这个时间段内该方法所有调用的消耗时间至少有50%的值要小于此阀值,否则系统将会报警。
TP99就是满足百分之九十九的网络请求所需要的最低耗时。
QPS和TPS的区别
https://blog.csdn.net/a745233700/article/details/117917333
QPS 基本类似于 TPS,但是不同的是,对于一个事务访问,会形成一个 " T ";但一次 " T " 中,可能产生多次对服务器的请求,服务器对这些请求,就可计入 QPS 之中。
分布式一致需要校验哪些内容?
分布式一致性校验主要比较不同节点上的数据副本,以确保它们是否相同或满足特定的共识规则,其核心是在多节点之间就某个数据状态达成共识,防止数据不一致的问题 。这通常通过共识算法(如Paxos、Raft)来实现,这些算法帮助系统在并发操作和网络延迟的情况下,确保所有数据副本都保持一致性。
核心比较内容:
- 数据内容本身:
这是最基本的一致性检查,即比较各个节点上的同一个数据的具体值。如果节点上的数据内容不一致,则表明存在问题。 - 数据版本或状态:
在许多分布式系统中,数据会不断更新。校验会关注数据的版本号、时间戳等信息,以确保所有节点都拥有最新的、并且是同一个版本的数据副本。 - 一致性状态:
除了数据内容,一致性校验还会确认数据是否在"已提交"或"未提交"等状态上达成一致,确保所有节点对数据的生命周期有相同的认知。
为什么需要这些比较?
避免数据冲突:
当多个节点同时尝试修改同一份数据时,如果没有一致性校验,就可能出现不同节点拥有不同数据版本的情况。
保证系统可用性:
即使有节点发生故障或网络发生分区,一致性校验也能帮助系统快速恢复,找到正确的数据状态。
实现高可用性:
通过确保数据在多个副本中保持一致,分布式系统可以变得更健壮,一个节点的故障不会影响到整个系统的正常运行。
线程池
线程池的作用
线程池是一种线程管理机制,主要作用包括:
控制并发线程数量:避免线程无限制创建导致的系统资源耗尽
减少线程创建销毁开销:通过复用线程提高性能
提高响应速度:预先创建线程,任务到来时无需等待线程创建
统一管理线程资源:便于监控、调优和控制线程生命周期
线程池的线程复用理解
线程复用是线程池的核心特性:
传统方式中,线程执行完一个任务后会销毁
线程池中的线程在执行完任务后不会销毁,而是回到线程池等待新任务
实现原理是线程内部通过循环获取任务队列中的任务来执行,而非执行完一个任务就终止
这种机制避免了频繁创建和销毁线程的开销,显著提升系统性能
线程创建和销毁的资源消耗
线程创建和销毁主要消耗以下系统资源:
内存资源:每个线程都需要分配栈空间 (通常 1-10MB)和线程控制块 (TCB)
CPU 资源:线程创建需要操作系统内核进行上下文切换和数据结构初始化
内核资源:操作系统对线程数量有上限限制,过多线程会导致内核管理负担加重
缓存失效:频繁创建销毁线程会导致 CPU 缓存命中率下降
线程池的数据结构设计
线程池 {
// 核心属性
核心线程数: int
最大线程数: int
任务队列: 阻塞队列
工作线程集合: 集合
拒绝策略: RejectedExecutionHandler
// 状态控制
运行状态: enum(RUNNING, SHUTDOWN, STOP, TERMINATED)
// 辅助组件
锁: ReentrantLock
条件变量: Condition (用于线程等待/唤醒)
空闲线程超时时间: long
}
工作线程 {
所属线程池: ThreadPool
执行循环: while(线程池运行中) {
从任务队列获取任务并执行
若超时无任务且当前线程数>核心线程数则销毁
}
}
核心线程 2,最大线程 5,提交 3 个长时间任务的执行流程
提交第 1 个任务:创建第 1 个核心线程执行
提交第 2 个任务:创建第 2 个核心线程执行
提交第 3 个任务:由于核心线程已用完,创建第 1 个非核心线程执行
最终状态:3 个线程同时运行(2 个核心线程 + 1 个非核心线程),均处于忙碌状态
任务完成后:这 3 个线程不会立即销毁,会等待新任务(非核心线程会在空闲超时后销毁)
先扩容到最大线程再入队列的设计分析
与正常线程池的区别
正常线程池策略:核心线程→任务队列→扩容至最大线程→拒绝策略
这种设计策略:核心线程→扩容至最大线程→任务队列→拒绝策略
优势
任务响应速度更快,无需等待队列缓冲
适合处理短耗时任务,能更快利用系统资源
避免任务在队列中积压导致的延迟
劣势
线程创建开销大 ,特别是任务数量波动大时
系统资源消耗更高,可能导致资源竞争激烈
大量长时间任务可能导致线程数暴增,影响系统稳定性
适合的业务场景
任务执行时间短,且对响应时间要求高 的场景
系统资源充足,且任务并发量可控 的场景
不希望任务有任何排队延迟的实时性业务
例如:高频次的短查询请求、实时数据处理等