
在微服务架构的演进过程中,将单体应用拆分为高内聚、低耦合的微服务是架构师面临的首要挑战。优惠券系统作为电商平台中流量高频、逻辑复杂的典型业务,是演练 Spring Cloud 企业级实战的最佳场景。它不仅涉及商品的精准营销,还直接关联订单金额和资金流转,对系统的可用性和一致性要求极高。
在设计优惠券系统的微服务架构时,我们不能简单地按照 DAO 层或 Controller 层拆分,而应该依据业务领域(DDD)进行划分。基于此,我们可以将系统核心拆分为三个关键服务:
优惠券服务:负责优惠券模板的创建、发放、核销规则的配置以及优惠券的生命周期管理。 用户服务:专注于用户账户信息、等级以及用户持有的优惠券资产(库存管理)。 营销活动服务:负责大促活动的规则配置(如满减、折扣)与活动范围控制。 这种拆分方式使得优惠券服务可以专注于规则引擎的计算,而用户服务则专注于资产的扣减,职责清晰。在实际落地中,服务拆分后紧接着要解决的就是分布式调用和网关路由问题。
为了演示这一架构设计,我们将使用 Spring Cloud 的核心组件构建一个简化的实战案例。假设用户需要通过网关查询可用的优惠券列表,并使用一张优惠券。
首先,我们需要一个API 网关作为系统的统一入口,负责路由转发和鉴权。这里使用 Spring Cloud Gateway:
application.yml (Gateway 服务配置)
server: port: 9000 spring: application: name: coupon-gateway cloud: gateway: routes: # 将请求 /coupon/** 转发到 coupon-service - id: coupon-service uri: lb://coupon-service predicates: - Path=/coupon/** # 将请求 /user/** 转发到 user-service - id: user-service uri: lb://user-service predicates: - Path=/user/** eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ 接下来是优惠券服务的核心逻辑。在 Spring Cloud 环境中,服务间调用通常使用 OpenFeign。优惠券服务需要验证优惠券是否存在,但这通常由优惠券自身逻辑处理。这里我们模拟一个更复杂的场景:用户服务调用优惠券服务来“校验并锁定”一张优惠券。
优惠券服务暴露接口:
// 优惠券服务 Controller @RestController @RequestMapping("/coupon") public class CouponController {
@Autowired
private CouponService couponService;
// 模拟的优惠券数据存储
private static final Map<Long, Coupon> COUPON_DB = new HashMap<>();
static {
// 初始化一张测试优惠券
COUPON_DB.put(1L, new Coupon(1L, "100减10", 1000, 10));
}
/**
* 校验优惠券并尝试锁定(预扣除)
* 使用 Feign 暴露给其他服务调用
*/
@PostMapping("/lock")
public Boolean lockCoupon(@RequestParam Long userId, @RequestParam Long couponId) {
Coupon coupon = COUPON_DB.get(couponId);
if (coupon != null && coupon.getStock() > 0) {
coupon.setStock(coupon.getStock() - 1);
System.out.printf("优惠券 %s 库存锁定成功,剩余库存: %d%n", coupon.getName(), coupon.getStock());
return true;
}
System.out.println("优惠券库存不足或不存在");
return false;
}
}
// 简单的实体类 @Data @AllArgsConstructor @NoArgsConstructor class Coupon { private Long id; private String name; private Integer stock; private Integer discountAmount; } 然后是用户服务通过 Feign 调用优惠券服务:
// 用户服务中的 Feign 客户端接口 @FeignClient(name = "coupon-service") public interface CouponClient { @PostMapping("/coupon/lock") Boolean lockCoupon(@RequestParam("userId") Long userId, @RequestParam("couponId") Long couponId); }
// 用户服务 Controller,模拟业务编排 @RestController @RequestMapping("/user") public class UserController {
@Autowired
private CouponClient couponClient;
@PostMapping("/order/use-coupon")
public String createOrderWithCoupon(@RequestParam Long userId, @RequestParam Long couponId) {
// 1. 调用优惠券服务锁定库存
boolean lockResult = couponClient.lockCoupon(userId, couponId);
if (!lockResult) {
return "下单失败:优惠券不可用";
}
// 2. 创建订单逻辑(省略)...
System.out.println("订单创建成功,扣减优惠券库存");
return "下单成功";
}
} 在这个实战模型中,我们通过 Spring Cloud Gateway 实现了流量的统一分发,通过 Spring Cloud Eureka(假设已启动)实现了服务注册与发现,通过 OpenFeign 实现了服务间的声明式调用。这正是 Spring Cloud 企业级开发的标准化流程。
此外,真实的优惠券系统还需要考虑分布式事务的问题。例如,如果订单创建成功但优惠券库存回滚失败,将导致资损。因此,在生产级代码中,我们需要引入 Seata 或 RocketMQ 事务消息来保证 UserController 中的远程调用与本地操作的一致性。
通过这套拆分方案,我们将复杂的业务逻辑下沉到了不同的微服务中,不仅让代码结构更清晰,也使得各服务可以独立扩展(如优惠券服务在大促期间单独增加节点),从而真正发挥了微服务架构的优势。












评论(0)