高可用、高并发、微服务架构
面试高频考点:SLA 可用性指标、容灾架构设计、限流降级熔断、负载均衡策略、分布式缓存、消息队列削峰、微服务拆分原则、服务注册发现、分布式事务、分布式链路追踪、云原生架构
一、高可用设计
什么是高可用?
高可用(High Availability)是指系统在面临各种故障时,仍能持续提供服务的能力。通常用 SLA(Service Level Agreement)来衡量。
可用性指标(SLA)
| SLA 等级 | 可用性 | 年停机时间 | 适用场景 |
|---|---|---|---|
| 2 个 9 | 99% | 87.6 小时 | 一般系统 |
| 3 个 9 | 99.9% | 8.76 小时 | 商业系统 |
| 4 个 9 | 99.99% | 52.6 分钟 | 金融/支付 |
| 5 个 9 | 99.999% | 5.26 分钟 | 核心交易 |
可用性计算公式:
可用性 = (总时间 - 停机时间) / 总时间 × 100%
示例:99.99% 可用性
年停机时间 = 365 × 24 × 60 × (1 - 0.9999) = 52.6 分钟容灾架构设计
同城双活
┌─────────────────┐
│ 负载均衡器 │
│ (主/备切换) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 机房 A │ │ 机房 B │ │ 机房 C │
│ (同城双活) │ │ (同城双活) │ │ (异地灾备) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────────────┼──────────────┘
▼
┌───────────────┐
│ 数据同步 │
│ (主从复制/双写) │
└───────────────┘同城双活特点:
- 两个机房距离 < 50km,网络延迟 < 2ms
- 两机房同时对外提供服务
- 单机房故障,流量自动切换
- 数据实时同步,一致性较高
异地多活
┌─────────────────┐
│ 全局负载均衡 │
│ (GSLB) │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 北京机房 │ │ 上海机房 │ │ 广州机房 │
│ (完整服务) │ │ (完整服务) │ │ (完整服务) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌───────────────┐
│ 异步数据同步 │
│ (消息队列/ Binlog)│
└───────────────┘异地多活特点:
- 机房分布在不同城市,距离 > 100km
- 每个机房独立部署完整服务
- 用户就近访问,延迟低
- 数据异步同步,可能有延迟
- 真正的灾难级容灾能力
故障转移与恢复
健康检查机制
# Nacos 健康检查配置
spring:
cloud:
nacos:
discovery:
heart-beat-interval: 5000 # 心跳间隔 5s
heart-beat-timeout: 15000 # 心跳超时 15s
ip-delete-timeout: 30000 # 实例删除超时 30s// 自定义健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 检查数据库连接
if (!checkDatabase()) {
return Health.down()
.withDetail("database", "unavailable")
.build();
}
// 检查 Redis 连接
if (!checkRedis()) {
return Health.down()
.withDetail("redis", "unavailable")
.build();
}
return Health.up().build();
}
}故障转移策略
故障转移流程:
1. 健康检查失败 → 标记实例为不可用
2. 负载均衡器剔除故障实例
3. 新请求路由到健康实例
4. 故障实例恢复后自动注册
故障转移策略:
- Failover:自动切换到备用服务
- Failfast:快速失败,抛出异常
- Failsafe:忽略异常,返回默认值
- Failback:自动恢复限流降级熔断
限流(Rate Limiting)
// Sentinel 限流规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("getUserInfo");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // QPS 限制为 100
rules.add(rule);
FlowRuleManager.loadRules(rules);
// 注解方式限流
@SentinelResource(value = "getUserInfo", blockHandler = "handleBlock")
public UserInfo getUserInfo(Long userId) {
return userService.getById(userId);
}
// 限流回调方法
public UserInfo handleBlock(Long userId, BlockException ex) {
return UserInfo.getDefault(); // 返回默认值
}限流算法:
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 固定窗口 | 固定时间窗口计数 | 简单 | 边界突发流量 |
| 滑动窗口 | 窗口滑动计数 | 平滑 | 内存占用高 |
| 令牌桶 | 恒定速率放令牌 | 允许突发 | 实现复杂 |
| 漏桶 | 恒定速率流出 | 削峰填谷 | 不能突发 |
令牌桶算法:
┌─────────────┐
│ 令牌生成器 │ ──→ 恒定速率放令牌
└──────┬──────┘
▼
┌─────────────┐ 桶满丢弃
│ 令牌桶 │ ←──────────────┐
│ (容量 N) │ │
└──────┬──────┘ │
│ │
▼ │
┌─────────────┐ │
│ 请求处理 │ ──→ 获取令牌成功 ──┘
└──────┬──────┘
│ 无令牌
▼
请求被限流熔断(Circuit Breaker)
// Sentinel 熔断规则
DegradeRule degradeRule = new DegradeRule("getUserInfo");
degradeRule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());
degradeRule.setCount(0.5); // 错误比例 50%
degradeRule.setTimeWindow(10); // 熔断时长 10s
degradeRule.setMinRequestAmount(10); // 最小请求数
DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));熔断器状态机:
失败率 >= 阈值
┌─────────────────────────┐
│ ▼
┌────────┐ 半开状态成功 ┌────────┐
│ 关闭 │ ←───────────── │ 半开 │
│ (Closed)│ │ (Half-Open)│
└────┬───┘ └────┬───┘
│ │
│ 失败率 < 阈值 │ 半开状态失败
│ (正常请求) │
│ ▼
│ ┌────────┐
└───────────────────│ 打开 │
│ (Open) │
└────────┘
│
│ 熔断时间到
└────────┐
▼
进入半开降级(Degradation)
// 降级策略
@Service
public class ProductService {
@SentinelResource(
value = "getProductDetail",
fallback = "getProductDetailFallback", // 降级方法
blockHandler = "handleBlock" // 限流熔断处理
)
public ProductDetail getProductDetail(Long productId) {
// 查询商品详情(包含库存、价格等实时数据)
return productClient.getDetail(productId);
}
// 降级方法:返回简化数据
public ProductDetail getProductDetailFallback(Long productId, Throwable ex) {
ProductDetail detail = new ProductDetail();
detail.setId(productId);
detail.setName("商品暂不可用");
// 从本地缓存获取基础信息
detail.setPrice(localCache.getBasicPrice(productId));
return detail;
}
}降级策略分类:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 自动降级 | 超时/失败率触发 | 下游服务故障 |
| 手动降级 | 配置中心开关 | 大促/高峰期 |
| 读降级 | 读缓存/默认值 | 数据一致性要求低 |
| 写降级 | 异步写入/延迟写入 | 写操作非核心 |
灰度发布与回滚
灰度发布流程
灰度发布流程:
1. 新版本部署到灰度服务器
2. 路由规则配置灰度流量比例
3. 监控灰度服务指标
4. 逐步增加灰度流量
5. 全量发布或回滚
┌──────────────────────────────────────────────────┐
│ 负载均衡器 │
└────────────────────────┬─────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 旧版本 v1 │ │ 旧版本 v1 │ │ 新版本 v2 │
│ (40%) │ │ (40%) │ │ (20%) │ ← 灰度
└──────────┘ └──────────┘ └──────────┘# Nacos 灰度配置
spring:
cloud:
nacos:
discovery:
metadata:
version: v2 # 版本标识
weight: 20 # 权重(灰度流量比例)金丝雀发布 vs 蓝绿部署
| 对比项 | 金丝雀发布 | 蓝绿部署 |
|---|---|---|
| 资源占用 | 少(逐步替换) | 多(双倍资源) |
| 回滚速度 | 较慢 | 快(切换流量) |
| 风险 | 低(小流量验证) | 中(全量切换) |
| 适用场景 | 大规模服务 | 小规模/关键服务 |
蓝绿部署架构:
┌──────────────────────────────────────┐
│ 负载均衡器/网关 │
└──────────────────┬───────────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ 蓝环境 │ │ 绿环境 │
│ (v1 生产) │ │ (v2 预发) │
│ 活跃 ✓ │ │ 待命 │
└──────────┘ └──────────┘
发布时:切换流量到绿环境,蓝环境保留便于回滚二、高并发架构
并发指标
| 指标 | 全称 | 含义 | 示例 |
|---|---|---|---|
| QPS | Queries Per Second | 每秒查询数 | 10000 QPS |
| TPS | Transactions Per Second | 每秒事务数 | 5000 TPS |
| RT | Response Time | 响应时间 | 50ms |
| 并发数 | Concurrent Users | 同时在线用户 | 10000 |
关键公式:
QPS = 并发数 / 平均响应时间
并发数 = QPS × 平均响应时间
示例:
- 平均响应时间 100ms
- QPS 目标 10000
- 需要并发数 = 10000 × 0.1 = 1000负载均衡策略
负载均衡算法
| 算法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 轮询 | 依次分配 | 简单公平 | 不考虑性能 | 服务器性能相近 |
| 加权轮询 | 按权重分配 | 考虑性能差异 | 不考虑负载 | 服务器性能不同 |
| 最少连接 | 连接数最少优先 | 动态负载均衡 | 需维护连接数 | 长连接场景 |
| IP Hash | 按 IP 分配 | 会话保持 | 分配不均 | 有状态服务 |
| 一致性 Hash | 按 Hash 环分配 | 扩缩容影响小 | 实现复杂 | 缓存场景 |
| 随机 | 随机分配 | 简单 | 分配不均 | 无状态服务 |
一致性 Hash 算法:
服务器 A
▲
│
服务器 D ◄───┼───► 服务器 B
│
▼
服务器 C
请求路由:Hash(请求 key) → 顺时针找到第一个服务器
扩缩容:只影响相邻节点,不影响全局负载均衡实现
# Nginx 负载均衡配置
upstream backend {
# 加权轮询
server 192.168.1.1:8080 weight=3;
server 192.168.1.2:8080 weight=2;
server 192.168.1.3:8080 weight=1;
# 健康检查
server 192.168.1.4:8080 backup; # 备用服务器
server 192.168.1.5:8080 down; # 下线服务器
}
# IP Hash
upstream backend_iphash {
ip_hash;
server 192.168.1.1:8080;
server 192.168.1.2:8080;
}
# 最少连接
upstream backend_least_conn {
least_conn;
server 192.168.1.1:8080;
server 192.168.1.2:8080;
}分布式缓存架构
缓存架构模式
单机缓存架构:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 应用 │ ──►│ 缓存 │ ──►│ 数据库 │
└─────────┘ └─────────┘ └─────────┘
分布式缓存架构:
┌─────────────┐
│ 客户端 │
└──────┬──────┘
│
┌──────▼──────┐
│ 缓存代理 │
│ (Twemproxy) │
└──────┬──────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Redis-1 │ │ Redis-2 │ │ Redis-3 │
│ (分片1) │ │ (分片2) │ │ (分片3) │
└─────────┘ └─────────┘ └─────────┘
│ │ │
└────────────┼────────────┘
▼
┌─────────────┐
│ 数据库 │
└─────────────┘Redis 集群架构
Redis Cluster(3主3从):
┌─────────────────────────────────────────────────┐
│ Redis Cluster │
├─────────────────┬─────────────────┬─────────────┤
│ │ │ │
▼ ▼ ▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ Master1 │ │ Master2 │ │ Master3 │ │
│ 0-5460 │ │5461-10922│ │10923-16383│ │
└────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │
▼ ▼ ▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ Slave1 │ │ Slave2 │ │ Slave3 │ │
└─────────┘ └─────────┘ └─────────┘ │
│
16384 个槽位,均匀分布在 3 个 Master 上 │
每个 Master 负责一部分槽位 │
└─────────────────────────────────────────────────┘缓存穿透、击穿、雪崩
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 缓存穿透 | 查询不存在的数据 | 布隆过滤器、空值缓存 |
| 缓存击穿 | 热点 key 过期 | 互斥锁、逻辑过期 |
| 缓存雪崩 | 大量 key 同时过期 | 随机过期时间、多级缓存 |
// 缓存穿透:布隆过滤器
@Component
public class BloomFilterCache {
private BloomFilter<String> bloomFilter;
@PostConstruct
public void init() {
// 预计元素数量 100 万,误判率 0.01%
bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000,
0.0001
);
// 初始化加载所有 key
List<String> allKeys = productService.getAllKeys();
allKeys.forEach(bloomFilter::put);
}
public Product getProduct(String key) {
// 先判断 key 是否可能存在
if (!bloomFilter.mightContain(key)) {
return null; // 一定不存在
}
// 可能存在,查询缓存
return cacheService.get(key);
}
}
// 缓存击穿:互斥锁
public Product getHotProduct(String key) {
Product product = redis.get(key);
if (product != null) {
return product;
}
// 获取互斥锁
String lockKey = "lock:" + key;
try {
boolean locked = redis.setnx(lockKey, "1", 10);
if (locked) {
// 查询数据库
product = db.query(key);
redis.set(key, product, 3600);
} else {
// 等待重试
Thread.sleep(50);
return getHotProduct(key);
}
} finally {
redis.del(lockKey);
}
return product;
}
// 缓存雪崩:随机过期时间
public void setWithRandomExpire(String key, Object value) {
// 基础过期时间 + 随机时间(0-300秒)
int baseExpire = 3600;
int randomExpire = ThreadLocalRandom.current().nextInt(300);
redis.set(key, value, baseExpire + randomExpire);
}消息队列削峰填谷
削峰架构
削峰填谷流程:
高峰期:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户 │ ──►│ 网关 │ ──►│ 消息队列 │
│ (100万) │ │ (限流) │ │ (缓冲) │
└─────────┘ └─────────┘ └────┬────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 消费者1 │ │ 消费者2 │ │ 消费者3 │
│ (1万/s) │ │ (1万/s) │ │ (1万/s) │
└─────────┘ └─────────┘ └─────────┘
削峰效果:
- 入口流量:10万 QPS
- 消费速度:3万 QPS
- 消息队列缓冲:处理流量洪峰// 削峰填谷实现
@RestController
public class OrderController {
@Autowired
private RabbitTemplate rabbitTemplate;
// 下单请求直接写入消息队列
@PostMapping("/order")
public Result createOrder(@RequestBody OrderRequest request) {
// 快速响应
rabbitTemplate.convertAndSend(
"order.exchange",
"order.create",
request
);
return Result.success("订单提交成功,正在处理");
}
}
// 消费者按固定速率处理
@Component
@RabbitListener(queues = "order.queue")
public class OrderConsumer {
@RabbitHandler
public void handleOrder(OrderRequest request) {
// 控制消费速率
orderService.processOrder(request);
}
}数据库分库分表
分库分表策略
| 策略 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 垂直分库 | 按业务拆分 | 业务隔离、独立扩展 | 跨库 JOIN 困难 |
| 垂直分表 | 按字段拆分 | 宽表变窄表 | 需要关联查询 |
| 水平分库 | 按数据行拆分 | 数据分散、性能提升 | 跨库事务复杂 |
| 水平分表 | 单表数据拆分 | 单表性能提升 | 需要路由规则 |
垂直分库:
┌─────────────────────────────────────────────────┐
│ 单库(拆分前) │
│ 用户表、订单表、商品表、支付表、库存表 │
└─────────────────────────────────────────────────┘
│
▼ 拆分
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 用户库 │ │ 订单库 │ │ 商品库 │
│ 用户表 │ │ 订单表 │ │ 商品表 │
│ │ │ 支付表 │ │ 库存表 │
└────────────┘ └────────────┘ └────────────┘
水平分表:
┌─────────────────────────────────────────────────┐
│ 订单表(拆分前:1亿数据) │
└─────────────────────────────────────────────────┘
│
▼ 按 user_id 取模拆分
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ order_0 │ │ order_1 │ │ order_2 │ │ order_3 │
│ user_id%4=0│ │ user_id%4=1│ │ user_id%4=2│ │ user_id%4=3│
│ 2500万数据 │ │ 2500万数据 │ │ 2500万数据 │ │ 2500万数据 │
└────────────┘ └────────────┘ └────────────┘ └────────────┘ShardingSphere 配置
# ShardingSphere 分库分表配置
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db0
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1
rules:
sharding:
tables:
t_order:
# 分表配置
actual-data-nodes: ds$->{0..1}.t_order_$->{0..3}
# 分库策略
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: order_db_inline
# 分表策略
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order_table_inline
sharding-algorithms:
order_db_inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
order_table_inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 4}读写分离
读写分离架构
读写分离架构:
┌─────────────┐
│ 应用层 │
└──────┬──────┘
│
┌──────▼──────┐
│ 数据库代理 │
│ (ShardingSphere)│
└──────┬──────┘
┌────────────┼────────────┐
▼ │ ▼
┌─────────┐ │ ┌─────────┐
│ Master │ ───────┼──────►│ Slave1 │
│ (写) │ │ │ (读) │
└────┬────┘ │ └─────────┘
│ │
│ ┌────▼────┐
│ │ Slave2 │
│ │ (读) │
│ └─────────┘
▼
主从复制(异步)# ShardingSphere 读写分离配置
spring:
shardingsphere:
rules:
readwrite-splitting:
data-sources:
myds:
write-data-source-name: master
read-data-source-names: slave0,slave1
load-balancer-name: round_robin
load-balancers:
round_robin:
type: ROUND_ROBIN// 强制走主库(需要读最新数据)
@Transactional(readOnly = false)
@ShardingSphereHint("write")
public Order getLatestOrder(Long userId) {
return orderMapper.selectLatest(userId);
}三、微服务架构
微服务拆分原则
拆分维度
| 拆分方式 | 说明 | 示例 |
|---|---|---|
| 按业务拆分 | 按业务领域划分 | 用户服务、订单服务、商品服务 |
| 按领域拆分 | DDD 领域驱动设计 | 用户域、订单域、支付域 |
| 按子域拆分 | 核心域、支撑域、通用域 | 商品域(核心)、短信域(通用) |
单体架构 → 微服务架构:
单体架构:
┌───────────────────────────────────────┐
│ 单体应用 │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │用户 │ │订单 │ │商品 │ │支付 │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ │
│ ↓ │
│ 单一数据库 │
└───────────────────────────────────────┘
微服务架构:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│用户服务 │ │订单服务 │ │商品服务 │ │支付服务 │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户库 │ │ 订单库 │ │ 商品库 │ │ 支付库 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘服务拆分原则
1. 单一职责原则(SRP)
- 每个服务只负责一个业务功能
- 服务粒度适中,不要太细也不要太粗
2. 高内聚低耦合
- 服务内部高内聚
- 服务之间低耦合
- 避免循环依赖
3. 边界上下文(Bounded Context)
- DDD 核心概念
- 每个服务有自己的边界
- 同一概念在不同服务可以有不同的模型
4. 数据库独立
- 每个服务有独立的数据库
- 禁止跨库 JOIN
- 通过 API 交互
5. 团队自治
- 每个服务由独立团队负责
- 技术栈可以不同
- 独立部署和扩展服务注册与发现
注册中心对比
| 特性 | Nacos | Eureka | Consul | ZooKeeper |
|---|---|---|---|---|
| 一致性 | CP/AP 可选 | AP | CP | CP |
| 健康检查 | TCP/HTTP/MySQL | 心跳 | TCP/HTTP | 心跳 |
| 配置中心 | 支持 | 不支持 | 支持 | 支持 |
| 多语言 | 支持 | Java | 支持 | 支持 |
| 管理界面 | 丰富 | 简单 | 丰富 | 无 |
Nacos 服务注册发现
# 服务注册配置
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848
namespace: dev
group: DEFAULT_GROUP
metadata:
version: 1.0.0
region: beijing// 服务发现与调用
@RestController
public class OrderController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
// 服务发现
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
ServiceInstance instance = instances.get(0);
// 服务调用
String url = instance.getUri() + "/user/" + id;
return restTemplate.getForObject(url, Order.class);
}
}
// 使用 LoadBalancer 负载均衡
@RestController
public class ProductController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/product/{id}")
public Product getProduct(@PathVariable Long id) {
// 自动负载均衡
return restTemplate.getForObject(
"http://product-service/product/" + id,
Product.class
);
}
}服务注册发现流程:
1. 服务启动
┌──────────┐
│ 服务提供者 │
└─────┬────┘
│ 注册
▼
┌──────────┐
│ 注册中心 │
└─────┬────┘
│ 订阅
▼
┌──────────┐
│ 服务消费者 │
└──────────┘
2. 服务调用
服务消费者 → 注册中心 → 获取服务列表
服务消费者 → 服务提供者 → 发起调用
3. 健康检查
服务提供者 → 心跳 → 注册中心
注册中心 → 剔除 → 不健康实例
注册中心 → 推送 → 服务列表变更配置中心
Nacos 配置中心
# bootstrap.yml
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: 192.168.1.100:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
shared-configs:
- data-id: common.yaml
group: DEFAULT_GROUP
refresh: true// 动态配置刷新
@RestController
@RefreshScope
public class ConfigController {
@Value("${app.config.value}")
private String configValue;
@GetMapping("/config")
public String getConfig() {
return configValue;
}
}Apollo 配置中心
# application.properties
app.id=user-service
apollo.meta=http://192.168.1.100:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application服务网关
Spring Cloud Gateway
# 网关配置
spring:
cloud:
gateway:
routes:
# 用户服务路由
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
key-resolver: "#{@userKeyResolver}"
# 订单服务路由
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true// 网关过滤器
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 1. 检查白名单
String path = request.getPath().value();
if (isWhitePath(path)) {
return chain.filter(exchange);
}
// 2. 验证 Token
String token = request.getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token)) {
return unauthorized(exchange);
}
// 3. 解析用户信息
try {
UserInfo user = JwtUtil.parse(token);
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", user.getUserId())
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
} catch (Exception e) {
return unauthorized(exchange);
}
}
@Override
public int getOrder() {
return -100;
}
}分布式事务
分布式事务方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 2PC | 强一致 | 低 | 中 | 传统数据库 |
| 3PC | 强一致 | 中 | 高 | 传统数据库 |
| TCC | 最终一致 | 高 | 高 | 高并发业务 |
| Saga | 最终一致 | 高 | 中 | 长流程业务 |
| 本地消息表 | 最终一致 | 中 | 低 | 简单场景 |
| 事务消息 | 最终一致 | 高 | 低 | 高并发场景 |
Seata 分布式事务
Seata 架构:
┌─────────────────────────────────────────────────┐
│ TM (事务管理器) │
│ 开启/提交/回滚全局事务 │
└────────────────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ TC (事务协调器) │
│ Seata Server (调度中心) │
└────────┬────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ RM (资源管理器) │ │ RM (资源管理器) │
│ 订单服务 │ │ 库存服务 │
└─────────────────┘ └─────────────────┘// Seata AT 模式
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StockClient stockClient;
@Autowired
private AccountClient accountClient;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
@Override
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setProductId(orderDTO.getProductId());
order.setCount(orderDTO.getCount());
orderMapper.insert(order);
// 2. 扣减库存
stockClient.decrement(orderDTO.getProductId(), orderDTO.getCount());
// 3. 扣减余额
accountClient.debit(orderDTO.getUserId(), orderDTO.getAmount());
// 任意一步失败,自动回滚所有操作
}
}// TCC 模式
@LocalTCC
public interface StockTccService {
@TwoPhaseBusinessAction(name = "prepareDeduct", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepareDeduct(@BusinessActionContextParameter(paramName = "productId") Long productId,
@BusinessActionContextParameter(paramName = "count") Integer count);
boolean commit(BusinessActionContext context);
boolean rollback(BusinessActionContext context);
}
// 实现
@Service
public class StockTccServiceImpl implements StockTccService {
@Autowired
private StockMapper stockMapper;
@Autowired
private StockFreezeMapper stockFreezeMapper;
@Override
public boolean prepareDeduct(Long productId, Integer count) {
// 1. 扣减可用库存
int rows = stockMapper.decrement(productId, count);
if (rows == 0) {
throw new RuntimeException("库存不足");
}
// 2. 记录冻结库存
StockFreeze freeze = new StockFreeze();
freeze.setProductId(productId);
freeze.setCount(count);
stockFreezeMapper.insert(freeze);
return true;
}
@Override
public boolean commit(BusinessActionContext context) {
Long productId = context.getActionContext("productId", Long.class);
Integer count = context.getActionContext("count", Integer.class);
// 删除冻结记录
stockFreezeMapper.delete(productId, count);
return true;
}
@Override
public boolean rollback(BusinessActionContext context) {
Long productId = context.getActionContext("productId", Long.class);
Integer count = context.getActionContext("count", Integer.class);
// 1. 恢复可用库存
stockMapper.increment(productId, count);
// 2. 删除冻结记录
stockFreezeMapper.delete(productId, count);
return true;
}
}分布式链路追踪
SkyWalking 架构
SkyWalking 架构:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 服务 A │ │ 服务 B │ │ 服务 C │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└────────────┼────────────┘
│ 上报数据
▼
┌───────────────┐
│ OAP Server │
│ (分析处理) │
└───────┬───────┘
│ 存储
▼
┌───────────────┐
│ Elasticsearch│
└───────┬───────┘
│ 查询
▼
┌───────────────┐
│ UI │
│ (可视化) │
└───────────────┘链路追踪概念
Trace(追踪):
- 一次完整请求的链路
- 包含多个 Span
- TraceID 全局唯一
Span(跨度):
- 一个服务调用单元
- 包含服务名、方法名、耗时
- 有父子关系
示例:
请求 → 网关 → 用户服务 → 订单服务 → 库存服务
TraceID: abc123
SpanID: 1 (网关) → 2 (用户服务) → 3 (订单服务) → 4 (库存服务)
↑ ↑ ↑ ↑
0ms 10ms 50ms 80ms使用示例
// 自定义链路追踪
@RestController
public class TraceController {
@Autowired
private Tracer tracer;
@GetMapping("/trace")
public String trace() {
// 创建 Span
Span span = tracer.spanBuilder("custom-operation")
.setAttribute("attr.key", "value")
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 业务逻辑
doSomething();
} finally {
span.end();
}
return "success";
}
}四、云原生架构
容器化部署
Docker 容器化
# 多阶段构建
# 构建阶段
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]# docker-compose.yml
version: '3.8'
services:
user-service:
build: ./user-service
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- NACOS_SERVER_ADDR=nacos:8848
depends_on:
- nacos
- mysql
networks:
- app-network
deploy:
replicas: 3
resources:
limits:
cpus: '1'
memory: 1G
networks:
app-network:
driver: bridgeKubernetes 部署
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: NACOS_SERVER_ADDR
valueFrom:
configMapKeyRef:
name: app-config
key: nacos.addr
---
# Service
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
---
# HPA(自动扩缩容)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70服务网格(Istio)
Istio 架构:
┌─────────────────────────────────────┐
│ Istio 控制平面 │
│ ┌─────────┐ ┌─────────┐ ┌───────┐ │
│ │ Pilot │ │ Citadel │ │ Galley│ │
│ │(流量管理)│ │(安全) │ │(配置) │ │
│ └─────────┘ └─────────┘ └───────┘ │
└─────────────────┬───────────────────┘
│ 下发配置
▼
┌─────────────────────────────────────────────────────────────────┐
│ Istio 数据平面 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Pod: Service A │ │ Pod: Service B │ │
│ │ ┌──────┐ ┌──────┐│ │ ┌──────┐ ┌──────┐│ │
│ │ │ Envoy│ │服务A ││◄───►│ │ Envoy│ │服务B ││ │
│ │ │(Sidecar)│ ││ │ │(Sidecar)│ ││ │
│ │ └──────┘ └──────┘│ │ └──────┘ └──────┘│ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘# Istio 虚拟服务
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- route:
- destination:
host: user-service
subset: v1
weight: 90
- destination:
host: user-service
subset: v2
weight: 10 # 10% 流量到 v2(金丝雀发布)
retries:
attempts: 3
perTryTimeout: 2s
timeout: 5s
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: UPGRADE
http1MaxPendingRequests: 100
http2MaxRequests: 1000
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50Serverless 架构
Serverless 架构:
传统架构:
┌─────────────────────────────────────┐
│ 服务器 (24h 运行) │
│ ┌─────────┐ ┌─────────┐ │
│ │ 服务 A │ │ 服务 B │ │
│ └─────────┘ └─────────┘ │
│ 固定成本 │
└─────────────────────────────────────┘
Serverless 架构:
┌─────────────────────────────────────┐
│ 云平台 │
│ ┌─────────┐ ┌─────────┐ │
│ │ 函数 A │ │ 函数 B │ │
│ │(按需运行)│ │(按需运行)│ │
│ └─────────┘ └─────────┘ │
│ 按实际调用付费 │
└─────────────────────────────────────┘// AWS Lambda 示例
public class OrderHandler implements RequestHandler<Map<String, Object>, String> {
@Override
public String handleRequest(Map<String, Object> input, Context context) {
// 获取触发器信息
String orderId = (String) input.get("orderId");
// 处理订单
OrderService orderService = new OrderService();
orderService.processOrder(orderId);
return "Order processed: " + orderId;
}
}
// 阿里云函数计算示例
public class HttpHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpRequest request, HttpResponse response, Context context) {
// 获取请求参数
String body = request.getBody();
// 处理请求
String result = processRequest(body);
// 返回响应
response.setStatus(200);
response.setContentType("application/json");
response.setBody(result);
}
}五、架构设计原则
高可用设计原则
1. 消除单点故障
- 多副本部署
- 多机房部署
- 负载均衡
2. 快速故障转移
- 健康检查
- 自动摘除
- 快速恢复
3. 降级熔断
- 服务降级
- 熔断保护
- 限流控制
4. 数据备份
- 主从复制
- 数据备份
- 灾备恢复
5. 监控告警
- 实时监控
- 快速告警
- 快速定位高并发设计原则
1. 垂直扩展
- 提升单机性能
- CPU、内存、磁盘
- 有上限
2. 水平扩展
- 增加服务器数量
- 理论无上限
- 需要无状态设计
3. 缓存优化
- 多级缓存
- 缓存预热
- 缓存穿透/击穿/雪崩处理
4. 异步处理
- 消息队列
- 削峰填谷
- 最终一致性
5. 分库分表
- 数据分散
- 提升单表性能
- 读写分离微服务设计原则
1. 服务拆分
- 单一职责
- 高内聚低耦合
- 边界清晰
2. 服务治理
- 服务注册发现
- 负载均衡
- 熔断降级
- 链路追踪
3. 数据一致性
- 分布式事务
- 最终一致性
- 幂等设计
4. 可观测性
- 日志收集
- 指标监控
- 链路追踪
5. 安全性
- 认证授权
- 数据加密
- 网络隔离六、面试要点
Q1: 如何设计一个高可用系统?
回答要点:
- 消除单点:多副本部署、多机房部署
- 故障转移:健康检查、自动摘除、快速恢复
- 限流熔断:防止雪崩效应
- 数据备份:主从复制、灾备恢复
- 监控告警:实时监控、快速响应
Q2: 如何设计一个高并发系统?
回答要点:
- 负载均衡:分发流量到多台服务器
- 缓存优化:多级缓存、缓存预热
- 异步处理:消息队列削峰填谷
- 分库分表:数据分散、提升性能
- 读写分离:分离读写压力
Q3: 微服务拆分的原则是什么?
回答要点:
- 单一职责:每个服务只负责一个业务功能
- 高内聚低耦合:服务内部紧密相关,服务之间松散依赖
- 边界清晰:DDD 领域驱动设计
- 数据库独立:每个服务有独立的数据库
- 团队自治:独立团队负责、独立部署扩展
Q4: 什么是 CAP 理论?
回答要点:
- Consistency(一致性):所有节点同一时间看到相同的数据
- Availability(可用性):每个请求都能得到响应
- Partition Tolerance(分区容错性:网络分区时系统仍能运行
- 三选二:分布式系统最多只能同时满足两个
Q5: 什么是分布式事务?有哪些解决方案?
回答要点:
- 定义:跨多个数据库/服务的事务
- 方案:
- 2PC/3PC:强一致,性能低
- TCC:最终一致,性能高,实现复杂
- Saga:长流程事务,补偿机制
- 事务消息:高并发场景,最终一致
- Seata:开源分布式事务框架
Q6: 什么是服务网格?有什么优势?
回答要点:
- 定义:基础设施层,处理服务间通信
- 核心组件:数据平面(Envoy)、控制平面(Istio)
- 优势:
- 语言无关:支持多语言
- 流量管理:路由、重试、超时
- 安全通信:mTLS 加密
- 可观测性:日志、指标、追踪
Q7: 如何保证缓存和数据库的一致性?
回答要点:
- Cache Aside:先更新数据库,再删除缓存
- 延迟双删:删除缓存 → 更新数据库 → 延迟删除缓存
- 消息队列:通过消息队列保证最终一致
- Binlog 订阅:Canal 订阅 Binlog 异步更新缓存
- 设置过期时间:作为兜底方案
Q8: 什么是熔断?熔断器有哪些状态?
回答要点:
- 定义:当下游服务故障时,快速失败,避免级联故障
- 状态:
- Closed(关闭):正常状态
- Open(打开):熔断状态,快速失败
- Half-Open(半开):探测恢复状态
- 触发条件:错误率、超时率、响应时间
小结
- 高可用设计核心:消除单点、快速故障转移、限流熔断降级
- 高并发架构核心:负载均衡、缓存优化、异步处理、分库分表
- 微服务核心:服务拆分、服务治理、分布式事务、可观测性
- 云原生架构:容器化、服务网格、Serverless
- 架构设计原则:权衡取舍,没有银弹,适合业务的才是最好的