
概述微服务拆开之后服务之间怎么找到彼此、怎么调用、配置改一处要动几十个工程——SpringCloud 就是来解决这一串问题的。它不是一门新技术而是把业界已经跑通的组件攒成一套解决方案再用 SpringBoot 的方式装配好给你用。纲要SpringCloud 的定位一站式微服务解决方案而不是某一项具体技术它到底解决哪三类问题服务治理、配置管理、服务保护与网关SpringCloud 与 SpringBoot 的分工一个负责搭单个服务一个负责让多个服务协同组件清单Eureka / Nacos、Ribbon、Feign、Hystrix / Sentinel、Gateway / Zuul、Config / Nacos Config、Bus版本兼容Release Train 与 SpringBoot 的对照关系写错的典型报错父工程pom.xml的真实写法dependencyManagement导入 BOM微服务技术选型三条路线SpringCloud 原生 / Spring Cloud Alibaba / Dubbo 自建实战常见的三个坑SpringCloud 是干什么的先把最容易误解的一点说清楚SpringCloud 不是一门新技术它是微服务架构落地的一整套解决方案。它把其他公司开源出来的成熟组件整合到一起再基于 SpringBoot 做自动装配。你引入一个 starter配几行application.yml功能就能跑起来。里面的东西单拎出来都是独立项目——Eureka 是 Netflix 的Nacos 是阿里的Gateway 是 Spring 自己写的——但被 SpringCloud 统一收编、统一配置方式之后用起来就顺手了。这里其实藏着一个值得想的问题既然那些组件官方原生版本就能用为什么大家不直接用原生的非要绕一层 SpringCloud答案在于 SpringBoot 的自动装配。原生组件用起来配置繁琐、依赖关系要自己捋而 SpringCloud 在中间做了一层适配把引入依赖 写配置 加注解这套 SpringBoot 的肌肉记忆复用到微服务组件上。这份开箱即用的体验才是微服务真正被推广开的原因。原生的太重SpringCloud 把它压到了几乎零成本。它解决的三类问题问题类别具体要解决的问题对应组件服务治理服务实例地址会变调用方怎么知道该调谁一个服务多个实例时怎么分摊流量注册中心Eureka、Nacos、负载均衡Ribbon、Spring Cloud LoadBalancer配置管理几十个服务的配置分散在各工程里改一个开关要挨个发布配置中心Spring Cloud Config、Nacos Config、配置广播Spring Cloud Bus服务保护与网关下游挂了不能把上游拖死外部请求要统一入口、统一鉴权熔断限流Hystrix、Sentinel、网关Gateway、Zuul一句话概括服务治理管找得到、分得匀配置管理管改得快、不重启服务保护与网关管挂不了、进得来。SpringCloud 与 SpringBoot 的分工这是面试里被问得最多的一题也是最容易答糊的一题。两者的边界其实很干净维度SpringBootSpringCloud关注范围单个微服务内部多个微服务之间核心能力自动装配、起步依赖、内嵌容器注册发现、远程调用、负载均衡、配置、网关解决的问题一个服务怎么快速搭起来、怎么打成可执行包服务之间怎么找到彼此、怎么协同工作依赖关系独立存在底层依赖 SpringBoot典型产物一个spring-boot-starter-web工程一整套spring-cloud-starter-*依赖 父工程管理因为 SpringCloud 是基于 SpringBoot 做自动装配的所以它离了 SpringBoot 跑不起来反过来 SpringBoot 可以完全不用 SpringCloud。这个依赖是单向的。组件清单SpringCloud 官网左侧那一长串组件不是摆设每个都对应一个明确的职责。下面这张表把本课程会展开的组件和只做了解的组件分开组件定位解决的问题本课程是否展开Eureka服务注册中心服务实例注册与发现调用方靠它拿到实例列表展开Nacos注册中心 配置中心阿里开源兼具注册发现与配置管理支持分级存储、权重、namespace展开Ribbon客户端负载均衡拿到实例列表后决定这次请求发给哪个实例展开Feign声明式远程调用把 HTTP 调用写成接口方法省掉手写RestTemplate展开Hystrix熔断、降级、线程隔离下游服务异常时快速失败避免线程被拖死引发级联失败了解新版已被替代Sentinel流量控制、熔断降级阿里开源的流控组件替代 Hystrix了解Gateway网关统一入口、路由转发、鉴权、限流展开Zuul网关老版本Gateway 出现前的网关方案基于 Servlet性能不如 Gateway了解Spring Cloud Config配置中心把配置集中到 Git 仓库服务启动时拉取了解Nacos Config配置中心与 Nacos 注册中心共用一套支持配置热更新展开Spring Cloud Bus配置广播配合消息中间件把配置变更推送到所有实例触发刷新了解记住两条线索就不会乱注册中心解决找到谁其余组件解决找到之后怎么打交道。Nacos 之所以在课程里出现两次注册中心 配置中心是因为它一个组件同时占了两个位置。组件协作关系下面这张图是 SpringCloud 微服务集群的大致数据流。客户端请求先进网关网关去注册中心查目标服务实例再转发服务之间通过 Feign 互相调用调用时的实例选择交给 Ribbon所有服务的地址都注册在注册中心配置从配置中心拉取。按服务名转发Feign 声明式调用拉取实例列表注册心跳拉取实例列表启动时拉配置启动时拉配置配置变更广播配置变更广播客户端 / 浏览器Gateway 网关路由 · 鉴权 · 限流order-serviceuser-service注册中心Eureka / Nacos配置中心Nacos ConfigSpring Cloud Bus请求链路和治理链路是分开的实线是业务流量虚线是注册、发现、配置这类治理流量。想清楚这一点调不通的时候就知道该查哪条线——业务 404 看网关路由调用超时看注册中心和负载均衡配置没生效看配置中心与 Bus。版本兼容最容易踩、也最不该踩的坑SpringCloud 和 SpringBoot 之间有严格的版本对应关系因为它们底层是自动装配绑在一起的。SpringCloud 用版本列车Release Train命名每个列车只保证跟特定范围的 SpringBoot 协同工作。SpringCloud 版本Release Train对应 SpringBoot 版本状态Greenwich2.1.x已停止维护Hoxton2.2.x / 2.3.x本课程使用2020.0.x代号 Ilford2.4.x旧2021.0.x代号 Jubilee2.6.x旧2022.0.x3.0.x需要 JDK 17Hoxton 这里有个细节值得记SR5 之前对应 SpringBoot 2.2.xSR5 及之后对应 2.3.x。课程用 Hoxton.SR10SR10 SR5所以配套 SpringBoot 是 2.3.x具体落到2.3.9.RELEASE。版本写错的后果不是编译警告而是启动直接失败。典型表现有两类java.lang.NoSuchMethodError: org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator...*************************** APPLICATION FAILED TO START *************************** Description: An attempt was made to call a method that does not exist. The class hierarchy was loaded from: org.springframework.cloud.client.loadbalancer...看到NoSuchMethodError或NoClassDefFoundError指向 Spring 内部类第一反应就该去核对 SpringCloud 与 SpringBoot 的版本而不是去怀疑自己的代码。父工程 pom.xml 的真实写法单靠子模块各自声明版本很快就会出现user-service 用 2.2、order-service 用 2.3这种版本漂移。正确做法是在父工程用dependencyManagement一次性导入 BOMBill of Materials子模块只写groupId和artifactId版本由 BOM 统一裁决。下面这段是本课程cloud-demo父工程的真实内容?xml version1.0 encodingUTF-8?projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcn.itcast.demo/groupIdartifactIdcloud-demo/artifactIdversion1.0/versionmodulesmoduleuser-service/modulemoduleorder-service/module/modulespackagingpom/packagingparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.9.RELEASE/versionrelativePath//parentpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncodingjava.version1.8/java.versionspring-cloud.versionHoxton.SR10/spring-cloud.versionmysql.version5.1.47/mysql.versionmybatis.version2.1.1/mybatis.version/propertiesdependencyManagementdependencies!-- springCloud --dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependency!-- mysql驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion${mysql.version}/version/dependency!--mybatis--dependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion${mybatis.version}/version/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency/dependencies/project有几个点容易被忽略spring-cloud-dependencies的typepom/type和scopeimport/scope必须成对出现缺一个导入就不生效。子模块继承了父工程之后引spring-cloud-starter-*时不要写版本号写了反而会覆盖 BOM引来版本冲突。父工程继承了spring-boot-starter-parent意味着 SpringBoot 的依赖版本也已经由官方 BOM 管好了lombok、spring-boot-starter-web这种同样不需要写版本。子模块长这样只声明要什么不声明要哪个版本?xml version1.0 encodingUTF-8?projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdcloud-demo/artifactIdgroupIdcn.itcast.demo/groupIdversion1.0/version/parentmodelVersion4.0.0/modelVersionartifactIduser-service/artifactIddependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency!--mybatis--dependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactId/dependency/dependenciesbuildfinalNameapp/finalNamepluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project工程结构cloud-demo是典型的 Maven 聚合工程父工程只管版本子模块各管自己的业务cloud-demo ├── pom.xml # 父工程packagingpom统一 dependencyManagement ├── user-service │ ├── pom.xml # 继承父工程不写版本号 │ └── src/main/java/cn/itcast/user/... ├── order-service │ ├── pom.xml │ └── src/main/java/cn/itcast/order/... └── (后续加入) eureka-server / gateway / feign-api后续章节会在这个骨架上依次挂上注册中心、网关和独立的 Feign 接口模块所以一开始就把父工程搭对后面省很多事。微服务技术选型的三条路线真到项目里选型时通常绕不开这三条路线。这里只给结论和边界详细的横向对比属于同系列的另一篇本篇不展开。路线构成适合的场景备注SpringCloud 原生Eureka Ribbon Feign Gateway Config追求组件中立、贴合 Spring 生态本课程主线Spring Cloud AlibabaNacos Sentinel RocketMQ Seata国内团队需要注册配置一体、中文文档友好本质是 SpringCloud 标准的一种实现Dubbo 自建DubboRPC 自研或第三方注册中心对 RPC 性能要求高、已有治理体系不走 HTTP不是 SpringCloud 标准需要强调的是Spring Cloud Alibaba 本质上是实现了 SpringCloud 标准的一套实现它不是 SpringCloud 的对立方案而是 SpringCloud 标准下的一个具体技术组合。这也是为什么本课程可以以 SpringCloud 原生为主、同时把 Nacos 作为注册中心和配置中心引入——两者不冲突Nacos 本来就在 SpringCloud 的组件体系里。实战中常见的三个坑只引spring-boot-starter-web漏了spring-cloud-starter-*。表现是注解编译不过或运行时找不到EnableEurekaServer、EnableDiscoveryClient、FeignClient这些注解都在对应的 starter 里靠spring-boot-starter-web是引不进来的。缺哪个报哪个看注解全限定名就知道该补哪个 starterjava: 找不到符号 符号: 类 EnableEurekaServer 位置: 程序包 org.springframework.cloud.netflix.eureka.server父工程没有dependencyManagement子模块各写各的版本。一开始能跑等到第三个服务要接注册中心时就出问题不同模块拉进来的 SpringCloud 组件版本不一致NoSuchMethodError突然冒出来。修法不是逐个对齐子模块版本号而是回到父工程补上 BOM 导入再让子模块删掉版本号。SpringBoot 3.x 与 Hoxton 完全不兼容。SpringBoot 3.x 要求 JDK 17、把 Java EE 命名空间整体迁到了 Jakarta EE而 Hoxton 是 2.3.x 时代的产物差了两个大版本。这个组合不是配一配能跑是根本跑不起来。要么 SpringBoot 降到 2.3.x 配 Hoxton要么 SpringCloud 升到 2022.0.x 配 3.0.x两边的版本必须整体对齐不能各升各的。升级 SpringCloud 时顺手把spring-boot-starter-parent一起改是相对安全的做法。API 速览注解 / 依赖所属组件作用EnableEurekaServerspring-cloud-starter-netflix-eureka-server标注启动类把当前应用变成注册中心EnableDiscoveryClientspring-cloud-starter-netflix-eureka-client把当前服务注册到注册中心并允许发现其他服务LoadBalancedspring-cloud-starter-netflix-eureka-client加在RestTemplate上让请求按服务名做负载均衡FeignClientspring-cloud-starter-openfeign声明一个远程调用接口运行时生成代理EnableFeignClientsspring-cloud-starter-openfeign开启 Feign 接口扫描spring-cloud-dependencies父工程 BOM统一管理所有 SpringCloud 组件版本官方文档Spring Cloud 官网Spring Cloud 版本兼容对照表Spring Cloud Alibaba 官网Nacos 官网总结SpringCloud 不是一门新技术是把成熟组件整合起来、并用 SpringBoot 自动装配打包好的一站式微服务解决方案价值在于开箱即用。SpringBoot 管单个服务怎么搭SpringCloud 管多个服务怎么协同依赖是单向的SpringCloud 离不开 SpringBoot反之不成立。组件再多也就两条线索注册中心解决找到谁其余组件解决找到之后怎么打交道。版本必须成套对齐。本课程是 SpringCloudHoxton.SR10 SpringBoot2.3.9.RELEASE父工程用dependencyManagement导入 BOM子模块不写版本号。选型上 Spring Cloud Alibaba 不是竞品它是 SpringCloud 标准的一种实现课程以 SpringCloud 原生为主Nacos 同时担任注册中心与配置中心。