
随着云原生与微服务架构的普及,业务系统从单体架构拆分为数十甚至上百个分布式服务,调用链路成倍拉长,依赖关系愈发复杂。线上80%以上的严重故障,根因并非代码bug,而是架构设计的先天缺陷。分布式架构评审的核心价值,就是在系统上线前,提前识别并化解隐藏的架构风险,从源头筑牢系统的稳定性、安全性与可演进性防线。
分布式架构的底层核心矛盾,是基于不可靠的网络、硬件、软件组件,构建对外可靠的服务能力。网络分区、节点宕机、服务超时是分布式系统的固有属性,而非异常情况。所有架构设计的出发点,所有评审动作的核心标尺,都必须围绕这个本质矛盾展开。
由Eric Brewer于1998年提出,2002年被麻省理工学院学者严格证明,是分布式系统的第一性原理:
核心误区纠正:CAP并非“三选二”,分布式系统中分区容错性P是必须满足的前提——网络分区是分布式环境的固有属性,无法彻底避免。因此架构设计只能在CP(一致性优先)和AP(可用性优先)之间做权衡,不存在同时满足CA的分布式系统。
场景示例:银行核心转账系统必须选择CP,宁可服务暂时不可用,也不能出现账实不符;电商商品详情页选择AP,即使暂时看不到最新库存,也不能让用户无法访问页面。
由eBay架构师Dan Pritchett于2008年提出,是CAP定理在工程实践中的落地延伸,核心是牺牲强一致性换取系统的高可用性:
核心关系澄清:BASE并非CAP的对立面,而是CAP中AP方案的工程化实现,解决了AP场景下的数据一致性问题,是互联网分布式系统的主流设计理念。
业务适配性是所有评审的第一步,架构与业务不匹配,再完美的技术设计都毫无价值。
高内聚低耦合≠拆分得越细越好。很多人误以为拆分越细耦合度越低,实则不然:过度拆分导致服务间依赖激增,反而会提升耦合度,降低迭代效率。正确的拆分逻辑是:按照业务域边界,把强相关的业务逻辑收敛到同一个服务实现高内聚,服务间仅通过标准接口通信实现低耦合。
高可用是分布式架构的核心能力,评审的核心是验证:单个节点的故障,是否会导致整个系统不可用。

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
publicclass OrderService {
privatefinal RestTemplate restTemplate;
privatefinal 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"库存扣减失败,请稍后重试";
}
}
分布式数据一致性是最容易引发线上资损的环节,评审的核心是验证数据是否安全、是否会出现不一致的风险。
方案 | 一致性级别 | 适用场景 | 核心优缺点 |
|---|---|---|---|
2PC两阶段提交 | 强一致性 | 短事务、核心强一致场景(如银行转账) | 对业务无侵入,性能差,协调者存在单点风险,易出现阻塞 |
TCC | 强一致性 | 核心交易场景、短事务、高性能要求场景 | 性能好、灵活性高,对业务侵入性大,开发成本高,需处理幂等、悬挂、空回滚 |
SAGA | 最终一致性 | 长事务、跨多服务的复杂业务场景(如订单履约) | 适配长事务,无阻塞风险,无隔离性,易出现脏读,需开发补偿逻辑 |
本地消息表+消息队列 | 最终一致性 | 异步化业务场景、实时性要求不高的场景(如下单发积分) | 实现简单,可靠性高,消息表与业务表耦合,仅支持异步场景 |
事务消息 | 最终一致性 | 互联网通用异步场景,与本地消息表场景一致 | 对业务侵入性低,性能好,依赖消息队列的事务消息能力 |
幂等表DDL
CREATE TABLE`idempotent_record` (
`id`bigintNOTNULL AUTO_INCREMENT COMMENT'主键',
`request_no`varchar(64) NOTNULLCOMMENT'唯一请求号',
`business_type`varchar(32) NOTNULLCOMMENT'业务类型',
`business_id`bigintNOTNULLCOMMENT'业务ID',
`create_time` datetime NOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',
PRIMARY KEY (`id`),
UNIQUEKEY`uk_request_no` (`request_no`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COMMENT='幂等记录表';
幂等性拦截器
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
publicclass IdempotentInterceptor implements HandlerInterceptor {
privatefinal 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("请求号不能为空");
returnfalse;
}
String businessType = request.getRequestURI();
boolean isFirstRequest = idempotentService.checkAndRecord(requestNo, businessType);
if (!isFirstRequest) {
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("重复请求,请勿重复提交");
returnfalse;
}
returntrue;
}
}
幂等服务实现
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
publicclass IdempotentService {
privatefinal 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) {
returnfalse;
}
try {
IdempotentRecord newRecord = new IdempotentRecord();
newRecord.setRequestNo(requestNo);
newRecord.setBusinessType(businessType);
idempotentRecordMapper.insert(newRecord);
returntrue;
} catch (Exception e) {
returnfalse;
}
}
}
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
publicclass OrderTransactionService {
privatefinal OrderMapper orderMapper;
privatefinal 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
publicclass 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
publicclass OrderConsumerService {
privatefinal StockService stockService;
privatefinal 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());
};
}
}
分布式架构的核心优势是可扩展性,评审的核心是验证系统能否支撑业务流量增长,能否通过水平扩展提升性能。

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
publicclass CacheService {
privatefinal RedisTemplate<String, Object> redisTemplate;
privatefinal RedissonClient redissonClient;
privatefinal String NULL_VALUE = "NULL";
privatefinallong NULL_EXPIRE_TIME = 60;
privatefinallong LOCK_WAIT_TIME = 3;
privatefinallong 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)) {
returnnull;
}
return value;
}
Object dbValue = dbFallback.get();
if (dbValue == null) {
redisTemplate.opsForValue().set(key, NULL_VALUE, NULL_EXPIRE_TIME, TimeUnit.SECONDS);
returnnull;
}
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();
returnnull;
} 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);
}
}
分布式系统链路长,出问题能否快速定位、能否便捷运维,是这个维度的评审核心。

安全性是架构的底线,一旦出现问题,可能引发重大安全事故甚至触犯法律,评审必须零容忍。
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
publicclass JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
privatelong 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);
returntrue;
} catch (JwtException | IllegalArgumentException e) {
returnfalse;
}
}
}
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
publicclass JwtAuthenticationFilter extends OncePerRequestFilter {
privatefinal JwtUtil jwtUtil;
privatefinal 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
publicclass SecurityConfig {
privatefinal JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
returnnew 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();
}
}
架构不是一成不变的,需要跟随业务发展持续演进,评审的核心是验证系统能否长期维护,能否平滑升级。
架构风险评估不能依赖主观判断,需采用权威的FMEA(失效模式与影响分析)方法,该方法源自ISO 9000体系,广泛应用于各行业的风险管控。
对每个可能的失效模式,从三个维度进行1-10分的量化评分:
通过风险优先数RPN = S × O × D,对风险进行分级管控:
失效模式 | 业务影响 | 严重度S | 发生频度O | 可探测度D | RPN | 风险等级 | 整改措施 |
|---|---|---|---|---|---|---|---|
订单接口无幂等性设计,重复请求导致重复下单 | 用户重复支付,出现资损与大量客诉 | 10 | 4 | 4 | 160 | P0 | 新增唯一请求号+幂等表的幂等性设计 |
库存服务故障时,订单服务持续调用导致线程池打满,下单链路不可用 | 用户无法下单,业务收入受损 | 9 | 5 | 3 | 135 | P2 | 新增熔断机制,失败率达到50%时熔断打开,直接返回降级结果 |
缓存无随机过期时间,大量key同时过期导致数据库被打垮 | 数据库响应缓慢,下单链路超时 | 8 | 6 | 5 | 240 | P1 | 缓存过期时间增加随机值,新增互斥锁防止缓存击穿 |
架构评审不是一次性活动,需融入整个研发流程,形成全流程的质量保障闭环。
分布式架构评审,不是一次性的检查动作,而是持续的架构治理过程,是保障分布式系统稳定、高效、安全运行的核心手段。评审的核心不是挑错,而是提前识别风险,从源头解决问题,避免线上故障的发生。
一个优秀的分布式架构,从来不是用了多少前沿技术,而是最匹配业务需求的架构——能在不可靠的分布式环境中,提供稳定可靠的服务能力,同时能跟随业务发展平滑演进。架构评审的最终目标,是让架构真正成为业务发展的驱动力,而非绊脚石。