首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >TL-Java 架构第七期实战:从微服务拆分到全链路压测的架构演进之路

TL-Java 架构第七期实战:从微服务拆分到全链路压测的架构演进之路

原创
作者头像
搜weiranit
发布2026-07-27 17:54:57
发布2026-07-27 17:54:57
20
举报

一、引言:架构师思维的三个层次

在带领团队完成多个千万级 DAU 项目后,我深刻体会到架构设计并非一蹴而就,而是持续演进的过程:

  • 层次一(生存):系统能跑,功能正确。
  • 层次二(韧性):部分节点宕机,系统依然可用。
  • 层次三(弹性):流量突增 10 倍,系统自动扩容,平稳降级。

TL-Java 架构第七期的课程正是围绕这三个层次展开,将理论(领域驱动设计、CAP 定理、最终一致性)与实战(Spring Cloud Alibaba、Seata、Sentinel、Micrometer)深度结合。本文截取其中最具代表性的五个实战模块,用代码还原架构师的设计决策。


二、模块一:微服务拆分与领域驱动设计(DDD)落地

很多团队拆分微服务时,习惯按“技术层”拆分(Controller-Service-DAO 各成一个服务),导致服务内聚性差、改动扩散。正确的做法是按业务能力(Bounded Context)拆分

2.1 订单领域的聚合根设计

以电商订单为例,我们将“订单创建”和“库存扣减”拆分为两个独立服务,通过领域事件解耦。以下是一个典型的订单聚合根代码(使用 Spring Boot + JPA):

代码语言:javascript
复制
// Order.java - 聚合根
package com.tl.architecture.order.domain;

import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String orderNo;
    private Long userId;
    private BigDecimal totalAmount;
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    private LocalDateTime createdTime;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "order_id")
    private List<OrderItem> items;

    // 领域行为:创建订单(工厂方法)
    public static Order create(Long userId, List<OrderItem> items) {
        Order order = new Order();
        order.orderNo = "ORD" + System.currentTimeMillis();
        order.userId = userId;
        order.items = items;
        order.totalAmount = items.stream()
                .map(OrderItem::getSubtotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        order.status = OrderStatus.CREATED;
        order.createdTime = LocalDateTime.now();
        return order;
    }

    // 领域行为:取消订单
    public void cancel() {
        if (this.status == OrderStatus.PAID) {
            throw new IllegalStateException("已支付订单无法取消");
        }
        this.status = OrderStatus.CANCELLED;
    }
}

// OrderStatus 枚举
enum OrderStatus {
    CREATED, PAID, SHIPPED, COMPLETED, CANCELLED
}

2.2 领域服务与仓储模式

使用 Repository 模式隔离数据访问,领域服务编排跨聚合的操作:

代码语言:javascript
复制
// OrderRepository.java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    Optional<Order> findByOrderNo(String orderNo);
}

// OrderDomainService.java - 领域服务
@Service
@Transactional
public class OrderDomainService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private DomainEventPublisher eventPublisher;

    public Order createOrder(Long userId, List<OrderItem> items) {
        Order order = Order.create(userId, items);
        Order saved = orderRepository.save(order);
        // 发布订单创建事件,由库存服务订阅
        eventPublisher.publish(new OrderCreatedEvent(saved.getId(), saved.getUserId(), items));
        return saved;
    }
}

架构决策点:订单服务不直接调用库存服务,而是通过 消息队列(RocketMQ) 发送事件,实现最终一致性。这避免了分布式事务的刚性开销。


三、模块二:分布式事务 —— Seata TCC 模式实战

在涉及资金、库存等强一致性场景时,事件驱动的最终一致性可能不够。此时采用 TCC(Try-Confirm-Cancel) 模式,Seata 框架提供了完善的支持。

3.1 TCC 接口定义(库存服务)

代码语言:javascript
复制
// InventoryTccAction.java
@LocalTCC
public interface InventoryTccAction {
    @TwoPhaseBusinessAction(name = "deductInventory", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryDeductInventory(
            @BusinessActionContextParameter(paramName = "productId") Long productId,
            @BusinessActionContextParameter(paramName = "quantity") Integer quantity,
            BusinessActionContext context
    );

    boolean confirm(BusinessActionContext context);
    boolean cancel(BusinessActionContext context);
}

3.2 TCC 实现类

代码语言:javascript
复制
@Service
public class InventoryTccActionImpl implements InventoryTccAction {
    @Autowired
    private InventoryRepository inventoryRepo;

    @Override
    public boolean tryDeductInventory(Long productId, Integer quantity, BusinessActionContext context) {
        // Try阶段:预扣库存,将库存状态置为“冻结”
        Inventory inventory = inventoryRepo.findByProductId(productId);
        if (inventory.getAvailableQty() < quantity) {
            return false; // 冻结失败,触发Cancel
        }
        inventory.setFrozenQty(inventory.getFrozenQty() + quantity);
        inventory.setAvailableQty(inventory.getAvailableQty() - quantity);
        inventoryRepo.save(inventory);
        // 将业务数据存入上下文,供Confirm/Cancel使用
        context.setContext("productId", String.valueOf(productId));
        context.setContext("quantity", String.valueOf(quantity));
        return true;
    }

    @Override
    public boolean confirm(BusinessActionContext context) {
        Long productId = Long.valueOf(context.getContext("productId"));
        Integer quantity = Integer.valueOf(context.getContext("quantity"));
        // Confirm阶段:扣减冻结库存,即真正减少库存
        Inventory inventory = inventoryRepo.findByProductId(productId);
        inventory.setFrozenQty(inventory.getFrozenQty() - quantity);
        // 实际可用库存已在Try阶段扣除,此处无需再减
        inventoryRepo.save(inventory);
        return true;
    }

    @Override
    public boolean cancel(BusinessActionContext context) {
        Long productId = Long.valueOf(context.getContext("productId"));
        Integer quantity = Integer.valueOf(context.getContext("quantity"));
        // Cancel阶段:释放冻结库存
        Inventory inventory = inventoryRepo.findByProductId(productId);
        inventory.setFrozenQty(inventory.getFrozenQty() - quantity);
        inventory.setAvailableQty(inventory.getAvailableQty() + quantity);
        inventoryRepo.save(inventory);
        return true;
    }
}

3.3 业务调用方(订单服务)

代码语言:javascript
复制
@Service
public class OrderTccService {
    @Autowired
    private InventoryTccAction inventoryTcc;

    @GlobalTransactional // Seata 全局事务注解
    public void createOrderWithTcc(OrderDTO dto) {
        // 1. 创建订单(本地事务)
        Order order = orderService.createLocalOrder(dto);
        // 2. 调用库存服务的TCC接口
        boolean result = inventoryTcc.tryDeductInventory(dto.getProductId(), dto.getQuantity(), null);
        if (!result) {
            throw new RuntimeException("库存不足");
        }
        // 3. 其他业务...
        // 如果这里抛出异常,Seata会自动触发所有TCC的Cancel
    }
}

架构决策点:TCC 适用于短事务、高一致性要求场景,但需注意空回滚悬挂问题,Seata 已内置防悬挂机制。对于长事务,考虑 SAGA 模式。


四、模块三:高性能网关 —— Spring Cloud Gateway 自定义过滤器

网关是流量的入口,除了路由转发,还需承载认证、限流、日志等横切关注点。我们基于 Spring Cloud Gateway 实现一个灰度发布过滤器,根据请求头中的 version 动态路由到不同版本的服务。

代码语言:javascript
复制
// GrayReleaseFilter.java
@Component
public class GrayReleaseFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String version = request.getHeaders().getFirst("X-version");
        if (StringUtils.hasText(version)) {
            // 将版本号存入路由属性,供RoutePredicate使用
            exchange.getAttributes().put("version", version);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1; // 高优先级
    }
}

// 自定义路由断言工厂(仅部分代码)
@Component
public class VersionRoutePredicateFactory extends AbstractRoutePredicateFactory<VersionRoutePredicateFactory.Config> {
    public VersionRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String version = exchange.getAttribute("version");
            if (version == null) return config.getDefaultVersion().equals("v1");
            return version.equals(config.getVersion());
        };
    }

    public static class Config {
        private String version;
        private String defaultVersion = "v1";
        // getter/setter 省略
    }
}

application.yml 中配置两条路由:

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
        - id: order-service-v1
          uri: lb://order-service-v1
          predicates:
            - Path=/order/**
            - Version= v1
        - id: order-service-v2
          uri: lb://order-service-v2
          predicates:
            - Path=/order/**
            - Version= v2

架构决策点:灰度发布是微服务架构中降低上线风险的重要手段,结合注册中心的元数据(如 eureka.instance.metadata-map.version),可以实现平滑切流。


五、模块四:全链路可观测性 —— 自定义 Span 与业务埋点

排查线上故障时,只依赖日志远远不够。我们引入 Micrometer Tracing + Zipkin,实现请求的全链路追踪,并在关键节点添加业务自定义 Span,以记录订单支付、库存扣减等关键步骤的耗时与状态。

5.1 依赖配置(Maven)

代码语言:javascript
复制
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
</dependency>

5.2 自定义 Span 埋点 AOP

代码语言:javascript
复制
// TraceAnnotation.java - 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceAnnotation {
    String value() default "";
}

// TracingAspect.java
@Aspect
@Component
public class TracingAspect {
    @Autowired
    private Tracer tracer; // Brave Tracer

    @Around("@annotation(trace)")
    public Object traceMethod(ProceedingJoinPoint pjp, TraceAnnotation trace) throws Throwable {
        String spanName = trace.value().isEmpty() ? pjp.getSignature().getName() : trace.value();
        Span span = tracer.nextSpan().name(spanName).start();
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            // 添加业务标签
            span.tag("class", pjp.getTarget().getClass().getSimpleName());
            Object result = pjp.proceed();
            span.tag("result", "success");
            return result;
        } catch (Throwable t) {
            span.tag("error", t.getMessage());
            span.error(t);
            throw t;
        } finally {
            span.finish();
        }
    }
}

// 业务代码中使用
@Service
public class PaymentService {
    @TraceAnnotation("支付流程")
    public void processPayment(PaymentRequest request) {
        // 业务逻辑
    }
}

架构决策点:可观测性不仅是 APM 工具,更是业务监控的基础。通过自定义 Span,我们能够将业务指标(如支付成功率、平均响应时间)与系统指标(GC、CPU)关联分析,快速定位到底是代码慢还是资源争抢。


六、模块五:全链路压测与容量规划 —— 从 JMeter 到 Sentinel 降级

压测是架构演进的重要验证手段。我们通过 JMeter 模拟混合流量,结合 Prometheus + Grafana 监控系统瓶颈,并根据压测结果配置 Sentinel 降级规则

6.1 JMeter 压测脚本片段(JSR223 前置处理器)

代码语言:javascript
复制
// 在 JMeter 中模拟用户登录 token
import org.apache.commons.codec.digest.DigestUtils;

String userId = vars.get("userId");
String timestamp = String.valueOf(System.currentTimeMillis());
String sign = DigestUtils.md5Hex(userId + timestamp + "secret");
vars.put("sign", sign);
vars.put("timestamp", timestamp);

6.2 压测结果驱动的 Sentinel 降级配置

假设压测发现,当 /pay 接口 QPS 超过 5000 时,响应时间从 50ms 飙升到 2000ms,且 CPU 达到 80%。我们据此在 Sentinel 控制台配置慢调用比例降级

代码语言:javascript
复制
// 代码方式配置(也可在控制台动态配置)
@Configuration
public class SentinelConfig {
    @PostConstruct
    public void initFlowRules() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource("pay");
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule.setCount(200); // 响应时间阈值 200ms
        rule.setTimeWindow(10); // 降级持续时间 10s
        rule.setMinRequestAmount(20);
        rule.setStatIntervalMs(1000);
        rule.setSlowRatioThreshold(0.3); // 30% 请求超过阈值则触发降级
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }
}

同时,在业务代码中添加熔断后的降级处理

代码语言:javascript
复制
@Service
public class PayService {
    @SentinelResource(value = "pay", fallback = "payFallback")
    public PayResult pay(PayRequest req) {
        // 实际支付逻辑
    }

    public PayResult payFallback(PayRequest req, Throwable ex) {
        // 降级返回快速失败
        return PayResult.fail("系统繁忙,请稍后重试");
    }
}

架构决策点:压测不是一次性的,而是伴随架构演进的常态化活动。每一次大版本上线前,都应执行容量评估,并根据评估结果调整弹性伸缩策略(如 HPA 阈值)。


七、总结与展望

TL-Java 架构第七期的课程精华,在于将架构理论工业级代码紧密结合。本文的五个模块分别对应了:

  • DDD + 事件驱动:解决微服务拆分边界问题。
  • Seata TCC:解决分布式事务强一致性难题。
  • Gateway 灰度:实现平滑发布,降低变更风险。
  • 自定义链路追踪:提升故障定位效率。
  • 压测 + 降级:保障大促期间的稳定性。

架构师的价值,正是通过“预判”和“设计”,将这些关键技术点有机串联,形成一套可演进、可运维的系统。如果本文的代码让你有所收获,不妨在自己的项目中尝试落地,并持续关注性能数据和业务反馈——架构没有终点,只有持续优化。


免责声明:本文涉及 Seata、Sentinel 等开源组件,版本可能迭代,请以官方最新文档为准。生产环境使用前务必进行充分测试。

关于作者:TL-Java 架构课程讲师,前互联网大厂架构师,专注于分布式系统与云原生技术。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言:架构师思维的三个层次
  • 二、模块一:微服务拆分与领域驱动设计(DDD)落地
    • 2.1 订单领域的聚合根设计
    • 2.2 领域服务与仓储模式
  • 三、模块二:分布式事务 —— Seata TCC 模式实战
    • 3.1 TCC 接口定义(库存服务)
    • 3.2 TCC 实现类
    • 3.3 业务调用方(订单服务)
  • 四、模块三:高性能网关 —— Spring Cloud Gateway 自定义过滤器
  • 五、模块四:全链路可观测性 —— 自定义 Span 与业务埋点
    • 5.1 依赖配置(Maven)
    • 5.2 自定义 Span 埋点 AOP
  • 六、模块五:全链路压测与容量规划 —— 从 JMeter 到 Sentinel 降级
    • 6.1 JMeter 压测脚本片段(JSR223 前置处理器)
    • 6.2 压测结果驱动的 Sentinel 降级配置
  • 七、总结与展望
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档