文章目录一、任务与拆分单体到底哪里疼二、环境准备先把 Nacos 跑起来三、服务注册两个工程都把自己报上去四、OpenFeign把 HTTP 调用写成「本地方法」4.1 引依赖、开注解4.2 三个必踩的坑提前绕开五、Gateway给所有服务装一个门卫六、熔断降级别让一次库存抖动拖垮整个商城七、落地清单上线前把这张表过一遍聊微服务架构的文章大多停在画框图。这篇换个路子先给一个真实任务——用户下单后要扣库存从一个最普通的单体写起沿着痛点一步步拆成微服务每一步拆分都对应 Spring Cloud 里一个具体的组件。最后你会得到一套能跑的代码Nacos 注册中心 OpenFeign 服务调用 Gateway 网关Spring Boot 3.2 Spring Cloud 2023 版本线附每个组件最容易踩的坑。版本前置说明Spring Boot 3.x 要求 JDK 17。版本搭配按官方对照表走别自由组合——这是微服务环境问题的一大来源。组件版本JDK17Spring Boot3.2.xSpring Cloud2023.0.xSpring Cloud Alibaba2023.0.1.xNacos Server2.3.x一、任务与拆分单体到底哪里疼先写单体版。一个 Spring Boot 工程两个 ServiceServicepublicclassOrderService{AutowiredprivateStockServicestockService;publicStringcreateOrder(Stringsku,intcount){stockService.deduct(sku,count);// 进程内直接调用orderMapper.insert(buildOrder(sku,count));returnok;}}跑起来没问题。疼在后面发布耦合。库存逻辑改一行订单模块也得跟着重新部署。大促前只想扩容库存服务的机器做不到整个应用一起扩。故障扩散。库存模块一个死循环把线程占满订单接口跟着超时——它们共享同一个进程的线程池。技术栈锁死。想给库存模块换 Go、给报表模块换个存储不可能全在一个进程里。拆分方案按业务能力拆成 order-service 和 stock-service 两个独立进程订单服务通过 HTTP/RPC 调库存服务。拆完立刻冒出四个新问题每个问题对应一个 Spring Cloud 组件新问题组件stock-service 部署了 3 台order 怎么知道调哪台Nacos注册中心 负载均衡跨进程 HTTP 调用代码怎么写得像本地方法一样OpenFeign声明式调用外部请求从哪进来、怎么转发、谁做统一鉴权Gateway网关服务挂了/变慢了怎么不让故障顺着调用链扩散熔断降级Sentinel / Resilience4j下面按这个顺序逐个落地。二、环境准备先把 Nacos 跑起来Nacos 承担注册中心和配置中心两个角色是整个体系的「通讯录」。下载安装官网下载 zip 即可要求已装 JDK 17# WindowsGit Bash / CMDstartup.cmd-mstandalone# macOS / Linuxshstartup.sh-mstandalone-m standalone是单机模式本地开发够用。启动后访问http://localhost:8848/nacos默认账号密码 nacos/nacos生产环境第一件事改密码。生产部署提醒一句单机模式数据默认存在内嵌 Derby 里重启不丢但不好迁移生产切 MySQL 存储conf/application.properties里配spring.datasource.platformmysql并至少跑主备。三、服务注册两个工程都把自己报上去两个工程的 pom 依赖结构相同以 stock-service 为例parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.2.5/version/parentpropertiesspring-cloud.version2023.0.1/spring-cloud.versionspring-cloud-alibaba.version2023.0.1.0/spring-cloud-alibaba.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependency/dependenciesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion${spring-cloud-alibaba.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagement启动类加EnableDiscoveryClient新版其实可以不加显式写出来更清楚SpringBootApplicationEnableDiscoveryClientpublicclassStockApplication{publicstaticvoidmain(String[]args){SpringApplication.run(StockApplication.class,args);}}application.ymlserver:port:9001# order-service 用 9002spring:application:name:stock-service# 注册到 Nacos 的服务名调用方就靠这个名字找你cloud:nacos:discovery:server-addr:127.0.0.1:8848stock-service 提供接口RestControllerRequestMapping(/stock)publicclassStockController{// 内存模拟库存演示用真实场景在数据库里用乐观锁扣减privatefinalMapString,IntegerstockMapnewConcurrentHashMap(Map.of(iphone,100,macbook,50));PostMapping(/deduct)publicStringdeduct(RequestParamStringsku,RequestParamintcount){stockMap.compute(sku,(k,v)-{if(vnull||vcount){thrownewIllegalStateException(库存不足: sku);}returnv-count;});returndeducted sku xcount;}}两个工程先后启动到 Nacos 控制台「服务列表」里能看到order-service和stock-service各一条记录注册这步就算通了。验证自愈能力把 stock-service 再起一个实例IDEA 里 Copy Configuration 改端口为 9011或--server.port9011启动服务列表里 stock-service 变成 2 个实例。杀掉一个约 15~30 秒后 Nacos 摘除该实例——调用方无感。这就是注册中心买来的第一笔保险。四、OpenFeign把 HTTP 调用写成「本地方法」4.1 引依赖、开注解order-service 加依赖dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-loadbalancer/artifactId/dependency启动类上EnableFeignClients。然后定义「接口式客户端」// name 要和对方注册的服务名完全一致FeignClient(namestock-service,path/stock)publicinterfaceStockClient{PostMapping(/deduct)Stringdeduct(RequestParam(sku)Stringsku,RequestParam(count)intcount);}调用侧就跟用本地 Service 一样ServicepublicclassOrderService{AutowiredprivateStockClientstockClient;publicStringcreateOrder(Stringsku,intcount){StringresultstockClient.deduct(sku,count);// 实际发起了 HTTP 调用orderMapper.insert(buildOrder(sku,count));returnresult;}}Feign 帮你做了三件事按服务名从 Nacos 拿可用实例列表 → LoadBalancer 挑一台 → 组装 HTTP 请求。服务扩容缩容调用方一行代码不用动。4.2 三个必踩的坑提前绕开坑一RequestParam必须写 value。Feign 接口里RequestParam String sku不写sku编译后参数名丢失没开-parameters请求发出去就是skunull对面直接 400。接口里所有参数注解都显式写名字一劳永逸Stringdeduct(RequestParam(sku)Stringsku,RequestParam(count)intcount);坑二超时没配默认太短。Feign 底层默认连接 10s / 读 60s随版本有差异库存接口一旦变慢订单线程会被拖着等。显式配置spring:cloud:openfeign:client:config:default:connect-timeout:2000# 毫秒read-timeout:5000logger-level:basic# 调试期开 basic生产改 none坑三把 Feign 当本地方法忘了它有网络。createOrder里如果stockClient.deduct抛超时异常订单记录就不能写——扣库存失败必须中断下单流程这个 try/catch 的语义要想清楚不能无脑吞异常返回下单成功。五、Gateway给所有服务装一个门卫微服务拆多了之后前端不该知道后面有 8 个服务 8 个端口。网关统一收口路由转发、鉴权、限流都在这一层做。新建 gateway 工程dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependencyserver:port:8000spring:application:name:gateway-servicecloud:nacos:discovery:server-addr:127.0.0.1:8848gateway:routes:-id:order-routeuri:lb://order-service# lb:// 表示走负载均衡从注册中心解析predicates:-Path/api/order/**filters:-StripPrefix1# 转发前去掉第一段 /api-id:stock-routeuri:lb://stock-servicepredicates:-Path/api/stock/**filters:-StripPrefix1前端此后只认http://gateway:8000/api/order/**网关按路径拆给对应服务。一个必须澄清的点Gateway 基于 WebFluxNetty不能和spring-boot-starter-web共存同一个 pom 里同时引了直接起不来。网关工程里别顺手引 web 依赖。网关层的鉴权过滤器示意完整 JWT 校验逻辑见下一篇系统文章ComponentpublicclassAuthFilterimplementsGlobalFilter,Ordered{OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringtokenexchange.getRequest().getHeaders().getFirst(Authorization);if(tokennull||token.isBlank()){exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);returnexchange.getResponse().setComplete();}returnchain.filter(exchange);}OverridepublicintgetOrder(){return-100;}// 数值越小越先执行}六、熔断降级别让一次库存抖动拖垮整个商城调用链是 order → stock。某天 stock 变慢比如 DB 锁表order 的线程会排队等它超时线程池迅速占满order 也跟着不可用——故障顺着调用链爬上来了这叫雪崩。解法是熔断统计出某下游错误率过高后短时间内直接走降级逻辑不再发真实请求给下游喘息窗口。用 Resilience4j不额外引 Alibaba 组件依赖更轻dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-circuitbreaker-resilience4j/artifactId/dependencyFeignClient(namestock-service,path/stock,fallbackStockFallback.class)// 声明降级类publicinterfaceStockClient{PostMapping(/deduct)Stringdeduct(RequestParam(sku)Stringsku,RequestParam(count)intcount);}ComponentpublicclassStockFallbackimplementsStockClient{OverridepublicStringdeduct(Stringsku,intcount){// 降级策略按业务定这里选择「下单失败」也可以改成延迟扣减、入队列thrownewBizException(库存服务繁忙请稍后重试);}}降级逻辑一定要是业务决策而不是技术兜底下单场景宁可报「稍后再试」也不能悄悄跳过扣库存——那是要对账到半夜的。七、落地清单上线前把这张表过一遍事项说明版本对齐Boot / Cloud / Alibaba / Nacos 四个版本查官方对照表别自由组合Nacos 生产部署切 MySQL 存储、改默认密码、开鉴权别用单机 Derby 裸奔Feign 超时每个客户端显式设置别吃默认值降级语义每个降级逻辑过一遍业务明确「失败了用户看到什么、数据会不会不一致」幂等跨进程调用会重试扣库存、支付回调必须幂等唯一业务 ID 去重链路追踪至少把 traceId 透传到日志里Micrometer Tracing Zipkin 是当前默认组合最后一句话总结拆分的原则微服务拆的不是代码是发布节奏、扩容策略和故障隔离。服务数量跟着业务边界走两个人三个服务运维成本比收益大——先拆对最疼的那一块比画一张八个服务的大图有用得多。