引言
随着云原生与微服务架构的普及,业务系统从单体架构拆分为数十甚至上百个分布式服务,调用链路成倍拉长,依赖关系愈发复杂。线上80%以上的严重故障,根因并非代码bug,而是架构设计的先天缺陷。分布式架构评审的核心价值,就是在系统上线前,提前识别并化解隐藏的架构风险,从源头筑牢系统的稳定性、安全性与可演进性防线。
一、分布式架构评审的底层逻辑与核心前提
1.1 分布式系统的本质矛盾
分布式架构的底层核心矛盾,是基于不可靠的网络、硬件、软件组件,构建对外可靠的服务能力。网络分区、节点宕机、服务超时是分布式系统的固有属性,而非异常情况。所有架构设计的出发点,所有评审动作的核心标尺,都必须围绕这个本质矛盾展开。
1.2 架构设计的两大理论基石
CAP定理
由Eric Brewer于1998年提出,2002年被麻省理工学院学者严格证明,是分布式系统的第一性原理:
- Consistency(一致性):所有节点在同一时间看到的数据完全一致
- Availability(可用性):每一个请求都能收到非错误的响应,不保证返回最新数据
- Partition Tolerance(分区容错性):网络出现分区故障(节点间通信中断)时,系统仍能正常运行
核心误区纠正:CAP并非“三选二”,分布式系统中分区容错性P是必须满足的前提——网络分区是分布式环境的固有属性,无法彻底避免。因此架构设计只能在CP(一致性优先)和AP(可用性优先)之间做权衡,不存在同时满足CA的分布式系统。
场景示例:银行核心转账系统必须选择CP,宁可服务暂时不可用,也不能出现账实不符;电商商品详情页选择AP,即使暂时看不到最新库存,也不能让用户无法访问页面。
BASE理论
由eBay架构师Dan Pritchett于2008年提出,是CAP定理在工程实践中的落地延伸,核心是牺牲强一致性换取系统的高可用性:
- Basically Available(基本可用):系统故障时,允许损失非核心功能的可用性,保障核心功能正常运行
- Soft State(软状态):允许系统数据存在中间状态,且该状态不影响系统整体可用性
- Eventually Consistent(最终一致性):系统所有数据副本,经过一段时间同步后,最终能达到一致状态
核心关系澄清:BASE并非CAP的对立面,而是CAP中AP方案的工程化实现,解决了AP场景下的数据一致性问题,是互联网分布式系统的主流设计理念。
1.3 架构评审的四大核心原则
- 业务导向原则:架构的唯一目标是支撑业务发展,所有脱离业务场景的架构设计都是无本之木
- 最小复杂度原则:分布式架构的复杂度本身就是最大的风险,能用简单方案解决的问题,绝不引入复杂方案
- 风险前置原则:评审的核心是提前识别风险,而非事后救火,所有可能引发线上故障的设计,必须在上线前完成整改
- 可落地原则:评审结论必须是可执行、可验证的,禁止空洞的优化建议,必须明确整改标准与验收条件
二、分布式架构评审的7大核心维度
2.1 业务适配性评审
业务适配性是所有评审的第一步,架构与业务不匹配,再完美的技术设计都毫无价值。
核心评审要点
- 架构拆分是否匹配DDD领域边界,是否存在跨领域强依赖,是否出现“为拆分而拆分”的情况
- 服务粒度是否匹配业务迭代节奏,高频迭代的核心业务粒度可适当细化,稳定不变的基础业务应保持粗粒度
- 架构设计是否匹配业务峰值特征,是否适配业务未来1-2年的增长预期
- 核心业务链路是否有优先资源保障,非核心链路是否不会影响核心链路运行
风险红线
- 服务拆分混乱,跨领域强耦合,形成“分布式单体”
- 服务粒度过细,单次请求跨十几个服务,可用性指数级下降
- 架构设计脱离业务场景,用通用方案解决特殊业务问题
- 架构存在硬性性能瓶颈,无法支撑业务增长
易混淆点区分:服务拆分的核心逻辑
高内聚低耦合≠拆分得越细越好。很多人误以为拆分越细耦合度越低,实则不然:过度拆分导致服务间依赖激增,反而会提升耦合度,降低迭代效率。正确的拆分逻辑是:按照业务域边界,把强相关的业务逻辑收敛到同一个服务实现高内聚,服务间仅通过标准接口通信实现低耦合。
2.2 高可用与容错性评审
高可用是分布式架构的核心能力,评审的核心是验证:单个节点的故障,是否会导致整个系统不可用。
核心评审要点
- 核心链路是否无单点故障,服务、数据库、缓存、消息队列、注册中心是否都实现集群部署
- 是否有完整的容错机制,重试、熔断、降级、隔离、限流的适用场景是否正确,参数配置是否合理
- 故障节点是否能自动剔除,流量是否能自动切换到健康节点,切换过程对用户无感知
- 是否有完善的雪崩防护机制,下游故障不会向上游蔓延
- 是否有跨可用区、跨地域的灾备部署,RTO、RPO是否满足业务要求
容错全流程
实战示例:Resilience4j容错机制实现
pom依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
application.yml配置
resilience4j:
circuitbreaker:
instances:
orderService:
slidingWindowSize: 100
minimumNumberOfCalls: 10
failureRateThreshold: 50
waitDurationInOpenState: 10000
permittedNumberOfCallsInHalfOpenState: 5
retry:
instances:
orderService:
maxRetryAttempts: 3
waitDuration: 1000
retryExceptions:
- org.springframework.web.client.ResourceAccessException
ignoreExceptions:
- java.lang.IllegalArgumentException
ratelimiter:
instances:
orderService:
limitForPeriod: 1000
limitRefreshPeriod: 1000
timeoutDuration: 0
bulkhead:
instances:
orderService:
maxConcurrentCalls: 50
maxWaitDuration: 0
业务代码实现
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.annotation.Retry;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
private final RestTemplate restTemplate;
private final String STOCK_SERVICE_URL = "http://stock-service/stock/deduct";
public OrderService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@RateLimiter(name = "orderService", fallbackMethod = "rateLimitFallback")
@Bulkhead(name = "orderService", fallbackMethod = "bulkheadFallback")
@CircuitBreaker(name = "orderService", fallbackMethod = "circuitBreakerFallback")
@Retry(name = "orderService", fallbackMethod = "retryFallback")
public String deductStock(Long orderId, Long productId, Integer num) {
String requestUrl = STOCK_SERVICE_URL + "?productId=" + productId + "&num=" + num;
return restTemplate.postForObject(requestUrl, null, String.class);
}
public String rateLimitFallback(Long orderId, Long productId, Integer num, Exception e) {
return "当前流量过大,请稍后再试";
}
public String bulkheadFallback(Long orderId, Long productId, Integer num, Exception e) {
return "系统繁忙,请稍后再试";
}
public String circuitBreakerFallback(Long orderId, Long productId, Integer num, Exception e) {
return "库存服务暂时不可用,请稍后再试";
}
public String retryFallback(Long orderId, Long productId, Integer num, Exception e) {
return "库存扣减失败,请稍后重试";
}
}
易混淆点明确区分
- 熔断 vs 降级
- 熔断:应对下游服务故障的防护机制,下游失败率达到阈值时停止调用,防止故障蔓延,核心是“拒绝对故障服务的调用”
- 降级:应对自身系统压力过大的防护机制,系统负载过高时主动关闭非核心功能,释放资源保障核心业务,核心是“主动舍弃非核心,保核心”
- 重试 vs 熔断
- 重试:应对临时故障的机制,如网络抖动、瞬时超时,仅可对幂等接口使用,必须有明确的次数限制与重试间隔
- 熔断:应对持续故障的机制,下游持续故障时停止调用,防止重试加重下游压力,两者为互补关系,而非替代关系
- 限流 vs 隔离
- 限流:控制进入系统的总流量,防止系统被超出承载能力的流量打垮,是入口处的流量管控
- 隔离:按业务维度划分系统资源,如核心与非核心业务使用独立线程池,防止非核心业务耗尽资源影响核心业务,是内部的资源防护
2.3 一致性与数据安全评审
分布式数据一致性是最容易引发线上资损的环节,评审的核心是验证数据是否安全、是否会出现不一致的风险。
核心评审要点
- 一致性方案选型是否匹配业务场景,是否出现方案与场景错配的情况
- 分布式事务实现是否正确,回滚机制是否完善,是否处理了悬挂、空回滚等问题
- 数据副本同步策略是否合理,是否存在主从延迟导致的读写不一致问题
- 所有分布式接口是否实现幂等性设计,是否存在重复调用导致的数据错误
- 敏感数据是否实现加密存储与传输,数据备份与恢复机制是否完善
分布式事务主流方案与适用场景
| 方案 | 一致性级别 | 适用场景 | 核心优缺点 |
| 2PC两阶段提交 | 强一致性 | 短事务、核心强一致场景(如银行转账) | 对业务无侵入,性能差,协调者存在单点风险,易出现阻塞 |
| TCC | 强一致性 | 核心交易场景、短事务、高性能要求场景 | 性能好、灵活性高,对业务侵入性大,开发成本高,需处理幂等、悬挂、空回滚 |
| SAGA | 最终一致性 | 长事务、跨多服务的复杂业务场景(如订单履约) | 适配长事务,无阻塞风险,无隔离性,易出现脏读,需开发补偿逻辑 |
| 本地消息表+消息队列 | 最终一致性 | 异步化业务场景、实时性要求不高的场景(如下单发积分) | 实现简单,可靠性高,消息表与业务表耦合,仅支持异步场景 |
| 事务消息 | 最终一致性 | 互联网通用异步场景,与本地消息表场景一致 | 对业务侵入性低,性能好,依赖消息队列的事务消息能力 |
实战示例1:分布式接口幂等性实现
幂等表DDL
CREATE TABLE `idempotent_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`request_no` varchar(64) NOT NULL COMMENT '唯一请求号',
`business_type` varchar(32) NOT NULL COMMENT '业务类型',
`business_id` bigint NOT NULL COMMENT '业务ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_request_no` (`request_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='幂等记录表';
幂等性拦截器
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class IdempotentInterceptor implements HandlerInterceptor {
private final IdempotentService idempotentService;
public IdempotentInterceptor(IdempotentService idempotentService) {
this.idempotentService = idempotentService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestNo = request.getHeader("X-Request-No");
if (requestNo == null || requestNo.isBlank()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().write("请求号不能为空");
return false;
}
String businessType = request.getRequestURI();
boolean isFirstRequest = idempotentService.checkAndRecord(requestNo, businessType);
if (!isFirstRequest) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("重复请求,请勿重复提交");
return false;
}
return true;
}
}
幂等服务实现
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class IdempotentService {
private final IdempotentRecordMapper idempotentRecordMapper;
public IdempotentService(IdempotentRecordMapper idempotentRecordMapper) {
this.idempotentRecordMapper = idempotentRecordMapper;
}
@Transactional(rollbackFor = Exception.class)
public boolean checkAndRecord(String requestNo, String businessType) {
IdempotentRecord record = idempotentRecordMapper.selectByRequestNo(requestNo);
if (record != null) {
return false;
}
try {
IdempotentRecord newRecord = new IdempotentRecord();
newRecord.setRequestNo(requestNo);
newRecord.setBusinessType(businessType);
idempotentRecordMapper.insert(newRecord);
return true;
} catch (Exception e) {
return false;
}
}
}
实战示例2:RocketMQ事务消息实现
pom依赖配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-cloud-stream-binder</artifactId>
<version>5.3.0</version>
</dependency>
application.yml配置
spring:
cloud:
stream:
rocketmq:
binder:
name-server: 127.0.0.1:9876
bindings:
orderOutput:
destination: order-topic
content-type: application/json
producer:
group: order-producer-group
orderInput:
destination: order-topic
content-type: application/json
group: order-consumer-group
事务消息发送实现
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderTransactionService {
private final OrderMapper orderMapper;
private final StreamBridge streamBridge;
public OrderTransactionService(OrderMapper orderMapper, StreamBridge streamBridge) {
this.orderMapper = orderMapper;
this.streamBridge = streamBridge;
}
public void createOrder(Order order) {
String transactionId = "order-" + order.getOrderId();
Message<Order> message = MessageBuilder.withPayload(order)
.setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
.build();
streamBridge.send("orderOutput", message);
}
@RocketMQTransactionListener
public class OrderTransactionListenerImpl implements RocketMQLocalTransactionListener {
@Override
@Transactional(rollbackFor = Exception.class)
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
Order order = (Order) msg.getPayload();
orderMapper.insert(order);
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
String transactionId = (String) msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
Long orderId = Long.parseLong(transactionId.split("-")[1]);
Order order = orderMapper.selectById(orderId);
if (order != null) {
return RocketMQLocalTransactionState.COMMIT;
}
return RocketMQLocalTransactionState.ROLLBACK;
}
}
}
消息消费实现
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import java.util.function.Consumer;
@Service
public class OrderConsumerService {
private final StockService stockService;
private final IdempotentService idempotentService;
public OrderConsumerService(StockService stockService, IdempotentService idempotentService) {
this.stockService = stockService;
this.idempotentService = idempotentService;
}
@Bean
public Consumer<Order> orderInput() {
return order -> {
String requestNo = "order-" + order.getOrderId();
boolean isFirstRequest = idempotentService.checkAndRecord(requestNo, "stock-deduct");
if (!isFirstRequest) {
return;
}
stockService.deductStock(order.getProductId(), order.getNum());
};
}
}
易混淆点明确区分
- 2PC vs TCC:2PC是资源层的分布式事务,对业务无侵入,性能差,易阻塞;TCC是业务层的分布式事务,对业务侵入性大,性能好,灵活性高,无阻塞风险
- TCC vs SAGA:TCC是两阶段提交,先预留资源再确认,有隔离性,适合短事务;SAGA是一阶段直接提交,失败后补偿,无隔离性,易出现脏读,适合长事务
2.4 性能与可扩展性评审
分布式架构的核心优势是可扩展性,评审的核心是验证系统能否支撑业务流量增长,能否通过水平扩展提升性能。
核心评审要点
- 系统TP99、TP999响应时间、QPS承载能力是否满足业务峰值要求
- 核心调用链路各环节耗时是否合理,是否存在慢调用、长链路导致的性能问题
- 所有组件是否支持水平扩展,是否存在有状态设计导致无法扩容
- CPU、内存、磁盘、网络资源利用率是否合理,是否存在资源瓶颈
- 缓存设计是否合理,是否存在缓存穿透、击穿、雪崩的风险,缓存命中率是否达标
可扩展分布式架构拓扑
实战示例:Redis缓存风险防护实现
pom依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.37.0</version>
</dependency>
缓存服务实现
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CacheService {
private final RedisTemplate<String, Object> redisTemplate;
private final RedissonClient redissonClient;
private final String NULL_VALUE = "NULL";
private final long NULL_EXPIRE_TIME = 60;
private final long LOCK_WAIT_TIME = 3;
private final long LOCK_LEASE_TIME = 10;
public CacheService(RedisTemplate<String, Object> redisTemplate, RedissonClient redissonClient) {
this.redisTemplate = redisTemplate;
this.redissonClient = redissonClient;
}
public Object getWithPenetrationProtect(String key, java.util.function.Supplier<Object> dbFallback) {
Object value = redisTemplate.opsForValue().get(key);
if (value != null) {
if (NULL_VALUE.equals(value)) {
return null;
}
return value;
}
Object dbValue = dbFallback.get();
if (dbValue == null) {
redisTemplate.opsForValue().set(key, NULL_VALUE, NULL_EXPIRE_TIME, TimeUnit.SECONDS);
return null;
}
redisTemplate.opsForValue().set(key, dbValue, 3600 + (long) (Math.random() * 300), TimeUnit.SECONDS);
return dbValue;
}
public Object getWithBreakdownProtect(String key, java.util.function.Supplier<Object> dbFallback) {
Object value = redisTemplate.opsForValue().get(key);
if (value != null) {
return value;
}
String lockKey = "lock:" + key;
RLock lock = redissonClient.getLock(lockKey);
try {
boolean locked = lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS);
if (!locked) {
Thread.sleep(100);
return getWithBreakdownProtect(key, dbFallback);
}
value = redisTemplate.opsForValue().get(key);
if (value != null) {
return value;
}
Object dbValue = dbFallback.get();
if (dbValue != null) {
redisTemplate.opsForValue().set(key, dbValue, 3600 + (long) (Math.random() * 300), TimeUnit.SECONDS);
}
return dbValue;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public void setWithAvalancheProtect(String key, Object value, long baseExpireTime, TimeUnit timeUnit) {
long randomTime = (long) (Math.random() * baseExpireTime / 10);
long expireTime = baseExpireTime + randomTime;
redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
}
}
易混淆点明确区分:水平扩展 vs 垂直扩展
- 水平扩展:通过增加服务器数量提升系统性能,无性能上限,是分布式架构的核心扩展方式
- 垂直扩展:通过提升单台服务器配置提升性能,有物理上限,成本高,仅可作为临时扩容方案
2.5 可观测性与可运维性评审
分布式系统链路长,出问题能否快速定位、能否便捷运维,是这个维度的评审核心。
核心评审要点
- 是否接入全链路追踪系统,能否通过traceId追踪单次请求的全链路耗时、异常信息
- 是否有完善的监控告警体系,覆盖基础监控、应用监控、业务监控,告警阈值是否合理
- 日志规范是否统一,是否通过traceId串联全链路日志,日志级别是否合理,是否存在敏感信息泄露
- 服务部署、扩容、缩容、回滚是否实现自动化,是否有健康检查机制,能否自动剔除故障实例
- 是否有完善的应急预案,是否定期开展故障演练,能否快速恢复服务
全链路追踪核心流程
易混淆点明确区分:监控 vs 告警 vs 可观测性
- 监控:采集系统各项指标,展示系统运行状态,核心是“看得到”
- 告警:指标超出阈值时主动通知相关人员,核心是“及时发现问题”
- 可观测性:通过指标、日志、追踪三大支柱,快速定位问题根因,理解系统内部状态,核心是“知道为什么出问题”,监控和告警是可观测性的组成部分,而非全部
2.6 安全性与合规性评审
安全性是架构的底线,一旦出现问题,可能引发重大安全事故甚至触犯法律,评审必须零容忍。
核心评审要点
- 所有接口是否实现身份认证与权限控制,是否存在未授权访问风险,是否有防重放、防篡改机制
- 接口输入是否做了严格校验,是否存在SQL注入、XSS攻击的风险
- 敏感数据是否实现加密存储与传输,数据访问是否有权限控制与审计日志
- 网络架构是否实现隔离,核心服务是否部署在内网,是否有防火墙、WAF、DDoS防护能力
- 是否符合《个人信息保护法》《数据安全法》等法律法规要求,第三方依赖是否存在安全漏洞
实战示例:Spring Security JWT统一认证实现
pom依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
JWT工具类
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration * 1000);
return Jwts.builder()
.subject(username)
.issuedAt(now)
.expiration(expiryDate)
.signWith(getSigningKey(), Jwts.SIG.HS256)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
JWT认证过滤器
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
filterChain.doFilter(request, response);
return;
}
String username = jwtUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
Security配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/login", "/auth/register").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2.7 可维护性与演进性评审
架构不是一成不变的,需要跟随业务发展持续演进,评审的核心是验证系统能否长期维护,能否平滑升级。
核心评审要点
- 是否有统一的代码规范与架构规范,团队开发风格是否一致,是否存在架构腐化风险
- 第三方依赖版本是否统一,是否存在版本冲突、循环依赖、依赖冗余的问题
- 是否有技术债务的统计与整改计划,是否存在临时方案长期未整改的情况
- 架构设计是否支持平滑演进,能否在不影响线上业务的情况下完成升级改造,是否预留扩展点
- 是否有完善的架构文档、设计文档、接口文档,文档是否与代码同步更新
易混淆点明确区分:技术债务 vs 临时方案
- 技术债务:为短期进度牺牲长期代码质量与架构设计,无明确整改计划,不及时整改会越积越多,最终导致系统无法维护
- 临时方案:为解决紧急线上问题采用的短期方案,有明确的整改时间与计划,上线后会及时替换为正式方案,不会转化为长期技术债务
三、分布式架构风险评估科学方法论
架构风险评估不能依赖主观判断,需采用权威的FMEA(失效模式与影响分析)方法,该方法源自ISO 9000体系,广泛应用于各行业的风险管控。
3.1 FMEA核心评分维度
对每个可能的失效模式,从三个维度进行1-10分的量化评分:
- 严重度S:失效发生后对业务的影响程度,10分为最严重,如系统完全不可用、重大资损
- 发生频度O:失效发生的概率,10分为最容易发生,如每次上线都会出现
- 可探测度D:失效发生前被发现的难度,10分为最难发现,如仅线上峰值才会出现,测试环境无法复现
3.2 风险等级划分
通过风险优先数RPN = S × O × D,对风险进行分级管控:
- P0致命风险:RPN ≥ 500 或 严重度S=10,必须立即整改,否则禁止上线
- P1高危风险:200 ≤ RPN < 500,必须在上线前制定整改计划,限期完成
- P2中危风险:50 ≤ RPN < 200,需在后续迭代中完成整改
- P3低危风险:RPN < 50,记录在案,定期回顾
3.3 FMEA风险评估示例
| 失效模式 | 业务影响 | 严重度S | 发生频度O | 可探测度D | RPN | 风险等级 | 整改措施 |
| 订单接口无幂等性设计,重复请求导致重复下单 | 用户重复支付,出现资损与大量客诉 | 10 | 4 | 4 | 160 | P0 | 新增唯一请求号+幂等表的幂等性设计 |
| 库存服务故障时,订单服务持续调用导致线程池打满,下单链路不可用 | 用户无法下单,业务收入受损 | 9 | 5 | 3 | 135 | P2 | 新增熔断机制,失败率达到50%时熔断打开,直接返回降级结果 |
| 缓存无随机过期时间,大量key同时过期导致数据库被打垮 | 数据库响应缓慢,下单链路超时 | 8 | 6 | 5 | 240 | P1 | 缓存过期时间增加随机值,新增互斥锁防止缓存击穿 |
四、分布式架构质量保障全流程体系
架构评审不是一次性活动,需融入整个研发流程,形成全流程的质量保障闭环。
- 设计阶段:架构设计完成后,组织跨角色评审会,邀请架构、开发、测试、运维、安全负责人参与,按照7大核心维度完成评审,识别风险并明确整改要求,评审通过后方可进入开发阶段
- 开发阶段:开发人员按照评审通过的架构设计实现功能,遵守统一的开发规范,编写单元测试,代码提交前完成交叉评审,确保代码符合架构设计要求
- 测试阶段:开展功能测试、性能测试、压力测试、安全测试、混沌测试,验证架构设计的各项指标是否满足业务要求,测试通过后方可进入上线阶段
- 上线阶段:采用灰度发布策略,先切分少量流量观察监控指标,无异常后逐步放大流量至全量,同时制定完善的回滚方案,出现问题可立即回滚
- 运维阶段:上线后持续监控系统各项指标,定期开展故障演练验证应急预案有效性,定期进行架构复盘,识别新的风险并持续优化,形成闭环治理
五、分布式架构评审的常见误区
- 为技术而技术,脱离业务场景:很多架构设计过度追求高大上的技术,完全不匹配业务需求,比如将简单的管理系统拆分为十几个微服务,反而提升了复杂度,降低了迭代效率。架构的唯一目标是支撑业务发展,而非炫技。
- 只关注功能需求,忽略非功能需求:多数架构设计只关注业务功能能否实现,完全忽略高可用、性能、安全、可运维性等非功能需求,导致上线后频繁出现故障,维护成本极高。非功能需求决定了系统的最终质量。
- 评审走形式,整改不落地:很多架构评审只是走过场,评审会上提出的问题没有明确的责任人、整改时间与验收标准,评审结束后无人跟进,风险依然存在,评审完全失去意义。
- 只看静态设计,不看动态运行:很多评审只关注静态的架构图,完全不考虑系统动态运行时的流量峰值、故障场景、并发场景,导致静态设计完美的架构,上线后频繁出现问题。
- 过度设计,人为增加复杂度:分布式架构的复杂度本身就是最大的风险,很多设计提前考虑了未来3-5年的需求,做了过度复杂的设计,但业务根本发展不到对应阶段,反而增加了当下的开发与维护成本。正确的架构设计应遵循“够用就好,适度超前”的原则。
总结
分布式架构评审,不是一次性的检查动作,而是持续的架构治理过程,是保障分布式系统稳定、高效、安全运行的核心手段。评审的核心不是挑错,而是提前识别风险,从源头解决问题,避免线上故障的发生。
一个优秀的分布式架构,从来不是用了多少前沿技术,而是最匹配业务需求的架构——能在不可靠的分布式环境中,提供稳定可靠的服务能力,同时能跟随业务发展平滑演进。架构评审的最终目标,是让架构真正成为业务发展的驱动力,而非绊脚石。