Spring Cloud微服务架构搭建电商系统:从单体到S2B2C的实战演进

发布时间:2026/8/28 18:10:10
Spring Cloud微服务架构搭建电商系统:从单体到S2B2C的实战演进 Spring Cloud微服务架构搭建电商系统从单体到S2B2C的实战演进前言做过电商系统的人都知道业务从小做到大的过程中最怕的不是流量问题而是架构问题。单体架构在业务初期够用但当你的平台从自营B2C扩展到多商家入驻的B2B2C再演进到供应链赋能的S2B2C模式时单体架构就会变成一场灾难——每次发布像拆炸弹一个小改动可能牵连整个系统。本文围绕一个S2B2C电商系统的真实落地案例分享如何用Spring Cloud从单体架构演进到微服务架构的实战经验。不是理论教程是踩坑之后的真实验证。以小羊云商为参考案例覆盖了多租户隔离、供应链分销等S2B2C特有场景的架构决策。一、为什么要从单体走向微服务1.1 单体架构的瓶颈该平台最初是一个标准的B2C商城单体应用所有功能打包在一个WAR包里商品管理、订单管理、支付、会员、营销、物流……前两年没太大问题但业务扩展到B2B2C多商家入驻后问题集中爆发发布耦合商品模块改一个接口整个系统要重新部署所有功能都被影响技术栈受限搜索模块想用Elasticsearch推荐模块想用Python做AI算法但在单体里很难混用技术栈扩展困难大促时只有订单和支付模块需要扩容但单体架构只能整体扩容资源浪费严重团队协作冲突多个团队改同一个代码库代码合并冲突频繁开发效率直线下降1.2 为什么选Spring Cloud选型时我们对比了Dubbo和Spring Cloud两条路线维度DubboSpring Cloud通信协议RPCTCPHTTP/REST生态完整度偏服务治理全套微服务解决方案社区活跃度阿里维护Spring社区 Netflix生态与Spring Boot集成需额外适配原生集成学习曲线相对陡峭对Spring开发者友好最终选Spring Cloud的核心原因我们整个技术栈就是Spring Boot体系Spring Cloud的集成成本最低且Netflix Eureka Feign Zuul/Spring Cloud Gateway Config Server这套组合已经非常成熟。二、微服务拆分策略2.1 按业务域拆分这是最重要的决策。拆分粒度太粗等于没拆太细则服务数量爆炸运维成本飙升。我们的拆分原则是按业务域Domain拆不按技术层拆。最终拆分出以下核心服务服务名职责技术选型mall-gatewayAPI网关路由、限流、鉴权Spring Cloud Gatewaymall-auth认证授权中心OAuth2 JWTSpring Securitymall-product商品中心SPU/SKU管理、类目Spring Boot MyBatis-Plusmall-trade交易中心订单、购物车、结算Spring Boot Seata分布式事务mall-user用户中心会员、商家、权限Spring Boot Redis缓存mall-pay支付中心聚合支付Spring Boot 对接微信/支付宝mall-promotion营销中心优惠券、满减、拼团Spring Boot 规则引擎mall-search搜索中心商品搜索Spring Boot Elasticsearchmall-config配置中心Spring Cloud Config Nacosmall-monitor监控中心Spring Boot Admin Prometheus2.2 S2B2C场景的特殊考量S2B2C模式比普通B2B2C多了供应链赋能的概念拆分时需要额外考虑多租户隔离不同商家B端的数据必须隔离我们在mall-product和mall-trade中引入了租户ID维度所有查询自动带租户过滤供应链服务新增了mall-supply-chain服务管理S端到B端的供货关系、分销层级、佣金结算分销裂变mall-promotion中独立了分销模块支持多级分销链路计算三、核心组件实战3.1 服务注册与发现Nacos替代Eureka早期我们用的Eureka后来切换到Nacos原因很简单——Nacos同时支持注册中心和配置中心不需要单独部署Spring Cloud Config Server。# application.ymlspring:cloud:nacos:discovery:server-addr:127.0.0.1:8848namespace:mall-prodconfig:server-addr:127.0.0.1:8848file-extension:yamlgroup:MALL_GROUP3.2 服务间通信OpenFeign Sentinel服务间调用我们用OpenFeign声明式接口配合Sentinel做熔断降级FeignClient(namemall-product,fallbackProductFallback.class)publicinterfaceProductFeignClient{GetMapping(/api/product/spu/{id})ResultProductVOgetSpuById(PathVariable(id)Longid);GetMapping(/api/product/list)ResultListProductVOlistProducts(RequestParam(tenantId)LongtenantId,RequestParam(page)intpage,RequestParam(size)intsize);}Sentinel的降级策略配置ComponentpublicclassProductFallbackimplementsProductFeignClient{OverridepublicResultProductVOgetSpuById(Longid){// 返回兜底数据而非直接报错returnResult.fail(商品服务暂时不可用请稍后重试);}OverridepublicResultListProductVOlistProducts(LongtenantId,intpage,intsize){returnResult.ok(Collections.emptyList());}}3.3 API网关Spring Cloud Gateway网关层做了几件关键事情spring:cloud:gateway:routes:-id:mall-product-routeuri:lb://mall-productpredicates:-Path/api/product/**filters:-name:RequestRateLimiterargs:redis-rate-limiter.replenishRate:100redis-rate-limiter.burstCapacity:200-name:Retryargs:retries:3statuses:BAD_GATEWAY,GATEWAY_TIMEOUT鉴权过滤器网关层统一校验JWT Token下游服务无需重复鉴权ComponentpublicclassAuthFilterimplementsGlobalFilter,Ordered{AutowiredprivateRedisTemplateString,StringredisTemplate;OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringtokenexchange.getRequest().getHeaders().getFirst(Authorization);if(StringUtils.isBlank(token)){returnunauthorized(exchange,缺少认证信息);}// 校验Token有效性StringuserIdredisTemplate.opsForValue().get(token:token);if(userIdnull){returnunauthorized(exchange,Token已失效);}// 透传用户信息到下游服务ServerHttpRequestrequestexchange.getRequest().mutate().header(X-User-Id,userId).build();returnchain.filter(exchange.mutate().request(request).build());}OverridepublicintgetOrder(){return-100;}}3.4 分布式事务Seata电商系统最头疼的就是分布式事务。订单创建涉及mall-trade创建订单、mall-product扣减库存、mall-promotion核销优惠券、mall-user扣减积分四个服务。我们用Seata的AT模式处理GlobalTransactional(timeoutMills60000,namecreateOrder)publicOrderVOcreateOrder(OrderDTOorderDTO){// 1. 创建订单本地事务OrderorderorderService.save(orderDTO);// 2. 远程扣减库存ResultresultproductFeignClient.deductStock(orderDTO.getSkuId(),orderDTO.getQuantity());if(!result.isSuccess()){thrownewBusinessException(库存扣减失败);}// 3. 远程核销优惠券ResultcouponResultpromotionFeignClient.useCoupon(orderDTO.getCouponId());if(!couponResult.isSuccess()){thrownewBusinessException(优惠券核销失败);}// 4. 远程扣减积分userFeignClient.deductPoints(orderDTO.getUserId(),orderDTO.getPointsUsed());returnconvertToVO(order);}Seata通过undo_log自动回滚比TCC模式开发量小很多。但注意AT模式有全局锁高并发场景下性能不如TCC。我们在大促期间对核心链路改用TCC模式。四、踩过的坑4.1 Feign调用丢失Request头问题网关鉴权后透传了X-User-Id头但Feign调用下游服务时头信息丢失了。解决加Feign拦截器ConfigurationpublicclassFeignConfigimplementsRequestInterceptor{Overridepublicvoidapply(RequestTemplatetemplate){ServletRequestAttributesattributes(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if(attributes!null){HttpServletRequestrequestattributes.getRequest();template.header(X-User-Id,request.getHeader(X-User-Id));template.header(Authorization,request.getHeader(Authorization));}}}4.2 Seata与MyBatis-Plus的冲突MyBatis-Plus的乐观锁插件和Seata的AT模式会冲突——两个都在SQL里加version条件。解决方案是Seata模式下关闭MyBatis-Plus的乐观锁由Seata的全局锁保证并发安全。4.3 Nacos配置刷新不生效配置类必须加RefreshScope注解且不能用Value直接注入到构造函数中。正确做法RestControllerRefreshScopepublicclassProductController{Value(${mall.product.default-page-size:20})privateintdefaultPageSize;}五、架构演进总结阶段架构时间核心问题V1.0单体应用2019-2020发布耦合、扩展困难V2.0Spring Cloud微服务Eureka2020-2022服务间通信、配置管理V3.0Spring Cloud AlibabaNacosSentinelSeata2022至今高可用、分布式事务从单体到微服务不是技术问题是业务驱动的问题。如果你的业务还没到那个体量不要为了微服务而微服务——单体够用就先用单体等到痛点出现再拆也不迟。该平台目前的微服务架构支撑了多商家入驻、供应链分销、多租户隔离等S2B2C核心场景日常QPS在5000左右大促峰值可达3万。这套架构不是一步到位设计的是业务一步步推着演进出来的。