
SpringCloud入门最让人头疼的不是概念而是从哪下手。搜了一圈资料要么直接丢一堆理论术语要么上来就给一个几百个文件的微服务项目新手看三分钟就关了。这篇我换个思路只做一件事从零手把手搭一个能跑起来的SpringCloud demo。服务注册、服务发现、远程调用这些核心流程全部走通代码量控制在最小范围全程用最主流的组件不整花活。这套demo是给谁准备的刚接触SpringCloud的Java后端开发或者在公司里被分到微服务改造任务但还没上手过的朋友。看完之后你能得到一个本地可运行的完整工程骨架并且理解每一个配置和每一行关键代码在干什么而不是复制粘贴完跑通就完事。1. SpringCloud到底是什么先搞清我们要搭的这套东西很多人在第一步就被绕晕因为SpringCloud不是一个独立的技术而是一整套微服务解决方案的集合。咱们不背官方定义用大白话拆一下。1.1 一个服务拆成多个之后真正棘手的问题是什么原来写单体应用所有接口都在一个项目里一个类调用另一个类的方法直接new或者Spring注入就行。一旦拆成多个独立部署的服务问题立刻出现调用方怎么知道被调用的服务部署在哪台机器、哪个端口一个服务起了多个实例调用方该请求哪个万一挂了一个怎么办服务之间的接口地址变了难道要改代码重新发布吗这些问题的答案就是SpringCloud里面那一堆组件各自要干的活。说白了注册中心解决服务在哪负载均衡解决请求发给谁远程调用组件解决怎么方便地调别人的接口网关解决所有请求统一从哪里进。1.2 本教程用到的核心组件和最小集合网上动不动就列SpringCloud十个八个组件什么配置中心、消息总线、链路追踪全上阵对入门demo来说完全没必要。搭最小可用闭环只需要三个东西Nacos既当注册中心又当配置中心。用注册中心功能就够了。服务提供者真实提供业务接口的服务本文里就叫order-service。服务消费者调用别人接口的服务本文里叫consumer-service。网关、熔断、分布式事务这些等把这条主链路彻底跑懂再学事半功倍。这就好比学开车先学会启动、换挡、刹车你非要把漂移技巧一起学反而什么都学不会。需要说明的是本文用的服务间调用方式是OpenFeign它是当前SpringCloud生态最主流的声明式HTTP客户端。后面细讲。2. 环境准备和版本选型入门最大的坑其实在这里SpringCloud版本兼容问题是个老大难我见过太多人demo跑不起来最后发现是Spring Boot版本和Spring Cloud版本对不上或者Spring Cloud Alibaba和Spring Cloud版本不匹配。先把版本搞定后面全顺畅。2.1 一套经过验证的稳定版本组合SpringCloud的版本命名用的是伦敦地铁站名比如Hoxton、2020.0、2021.0这种。Spring Cloud Alibaba则单独维护了一套版本它负责把Nacos等阿里中间件集成到SpringCloud体系里。我本地实测可用的组合如下组件版本JDK1.8预算充足上17也行但1.8最稳Spring Boot2.7.12Spring Cloud2021.0.5Spring Cloud Alibaba2021.0.5.0Nacos Server2.2.3这套组合经过大量生产项目验证网上资料也最多出了问题容易搜到答案。记住一个关键点Spring Cloud Alibaba和Spring Boot之间不是随便配的官方有对应的版本关系表。2021.0.5.0这个版本适配Spring Boot 2.7.x别乱升级。2.2 Maven依赖都配了哪些东西三个服务的父工程用的都是同一个依赖管理核心就两个依赖。第一个是Spring Cloud的BOM第二个是Spring Cloud Alibaba的BOM。dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version2021.0.5/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.0.5.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagementBOM的作用是统一管理一堆子依赖的版本号我们在子模块里引入依赖时就不需要再写version了有效避免版本冲突。这一点和Maven的dependencyManagement机制直接相关不熟悉的可以单独查一下。2.3 Nacos Server的下载和启动Nacos Server本身是个独立运行的Java程序不是嵌在SpringBoot里的。先去GitHub下载对应的release包下载后解压目录结构大概是这样nacos-server-2.2.3/ ├── bin/ # 启动脚本 ├── conf/ # 配置文件 ├── data/ # 运行数据首次启动后生成 └── logs/ # 日志目录Windows系统直接在bin目录下双击startup.cmdLinux/macOS执行startup.sh。但有个坑Nacos 2.x版本默认以集群方式启动单机运行必须加参数。# Linux/macOS sh startup.sh -m standalone # Windows startup.cmd -m standalone启动成功之后浏览器访问 http://localhost:8848/nacos 默认账号密码都是nacos就能看到控制台。看到那个登录页面整个demo最基础的一步就算完成了。3. 用Nacos做注册中心整个demo的地基为什么要搞注册中心用个现实场景比喻一下。以前没有外卖平台的时候你得挨个记饭店的电话号码有了平台之后饭店入驻用户直接在平台上找饭店就行。注册中心就是这个平台每个服务启动后都去平台上报到记录自己叫什么名字、在哪台机器哪个端口。3.1 服务提供者的基础配置新建一个Spring Boot工程模块名叫order-service端口设置为8081。只引入一个和注册相关的依赖dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency这个starter会自动把服务注册到Nacos并且内置了心跳机制服务挂了Nacos能感知到。配置文件application.yml如下server: port: 8081 spring: application: name: order-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848这三项配置缺一不可。spring.application.name决定了服务在注册中心显示的名字后续远程调用就是通过这个名字来找服务的。server-addr指定Nacos Server的地址。现在写一个最简单的接口验证服务是活的RestController RequestMapping(/order) public class OrderController { GetMapping(/info) public String info() { return order-service: 订单服务返回的结果; } }3.2 从Nacos控制台看服务是否注册成功启动order-service然后打开Nacos控制台在服务管理-服务列表这一页就能看到order-service已经在列表里了。点进去可以看到这个服务的实例详情包括IP、端口、健康状态。这一步是对上面所有配置的第一轮验证。如果控制台里看不到服务排查顺序是order-service有没有引入nacos-discovery依赖spring.application.name是不是没配或者写错Nacos Server启动的时候是不是单机模式有没有网络不通或者端口被防火墙拦截的问题4. 第一个服务提供者order-service到底做了什么现在进一步把order-service做成稍微真实一点的样子不能只是一个空壳。虽然是个demo但至少要把接口返回数据这件事做完整因为后面消费者要拿这些数据。4.1 加一个简单的service层和mapper虽然SpringCloud的重点不在业务代码但工程结构要有个雏形。我加了一张订单表一个实体类一个Mapper接口一个Service类一个Controller。表数据直接初始化两条CREATE TABLE order_info ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(64) DEFAULT NULL, amount decimal(10,2) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;Controller返回订单列表GetMapping(/list) public ListOrderInfo list() { return orderService.listAll(); }这部分的代码和平时的单体开发没有任何区别。微服务的拆分是逻辑上的不是技术上另起炉灶。每个微服务内部就是一个小型单体项目。4.2 为什么demo里用MySQL而不是H2很多教程为了省事直接用H2内存数据库跑完即弃。我的建议是直接用MySQL因为MySQL才符合国内公司的真实环境。你现在用H2跑通了到了公司里对接MySQL可能又得踩一遍连接配置的坑。数据库连接池我用的是Druid配置如下spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/cloud_demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的密码提示如果数据库连不上排查耗时最多的问题往往是密码错误和时区配置不对。serverTimezone这一项尤其容易漏建议直接用Asia/Shanghai避免默认UTC导致的8小时偏差。5. 服务消费者用OpenFeign调用别人的接口服务提供者搭好了现在要建第二个服务consumer-service端口8082。这个服务的工作是提供一个HTTP接口给前端然后通过远程调用order-service的数据组装后返回。5.1 引入OpenFeign并启用远程调用能力在consumer-service的pom.xml中引入dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId /dependency第二个依赖很容易被忽略。Spring Cloud 2021版本之后OpenFeign不再默认集成Ribbon做负载均衡必须手动引入spring-cloud-starter-loadbalancer否则启动后调用服务会直接报503错误。启动类上别忘了加注解SpringBootApplication EnableFeignClients public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }EnableFeignClients的作用是扫描所有FeignClient标记的接口并生成代理对象注入Spring容器。5.2 用声明式接口代替手写HTTP请求OpenFeign最爽的地方在于调用远程服务就像调用本地方法一样。定义一个接口FeignClient(name order-service) public interface OrderFeignClient { GetMapping(/order/list) String listOrders(); }关键是FeignClient(name order-service)这里的name对应的是order-service在Nacos里注册的服务名。OpenFeign会拿着这个名字去注册中心找到真实的服务地址然后发起HTTP请求。调用的时候RestController RequestMapping(/consumer) public class ConsumerController { Resource private OrderFeignClient orderFeignClient; GetMapping(/orders) public String getOrders() { return 消费者收到数据: orderFeignClient.listOrders(); } }以前用RestTemplate或HttpClient手动拼接URL、设置headers、处理连接的步骤全部省掉了声明一个接口就完事这也是OpenFeign在国内SpringCloud项目中普及率这么高的原因。6. 启动三个服务验证整个调用链真的通了现在到了检验成果的时候。需要启动的东西一共有三个Nacos Server、order-service、consumer-service。启动顺序有个讲究先把Nacos启动了再启动两个业务服务。6.1 一步一步看整个调用链的通信流程我用实际测试过程给你走一遍看到哪一步卡住就在哪里排查第一步确认Nacos控制台 http://localhost:8848/nacos 正常访问第二步启动order-service等1~2秒让心跳注册成功在Nacos服务列表看到order-service实例第三步启动consumer-service同样在服务列表看到consumer-service实例第四步在浏览器访问 http://localhost:8082/consumer/orders如果一切正常第4步返回的响应大概是消费者收到数据: [{id:1,orderNo:ORD-20240601-001,amount:199.00},{id:2,orderNo:ORD-20240601-002,amount:299.00}]这说明consumer-service通过服务发现找到了order-service的真实地址然后通过OpenFeign完成了一次真实的HTTP调用数据从order-service的数据库取出来经过服务间传输最后返回到了浏览器。一条完整的微服务调用链路就这样通了。6.2 再多加一个实例看负载均衡是怎么工作的上面的链路虽然通了但只有一个order-service实例体现不出微服务的价值。你可以把order-service再启动一个实例端口设置成8083运行方式可以这么来先用jar包方式启动第一个实例java -jar order-service-0.0.1-SNAPSHOT.jar --server.port8081再启动第二个实例java -jar order-service-0.0.1-SNAPSHOT.jar --server.port8083这时候去Nacos控制台看order-service会发现有两个实例。连续刷新几次consumer-service的接口可以看到信息交替来自两个实例的日志。这就是负载均衡干的事同一个服务有多个可用实例时把请求分摊到不同实例上避免单个服务压力过大。7. 快速搭建中最容易踩的坑和排查套路写这篇教程之前我在本地把从零搭建的过程完整复现了一遍就是为了把最常见的坑提前踩一遍。以下这几个问题基本覆盖了初学者90%以上的报错场景。7.1 OpenFeign直接报503多半是缺了loadbalancer依赖这是2021版本之后最大的变化。很多老教程都是基于Hoxton之前版本写的那时候Ribbon是默认自带的OpenFeign可以直接做负载均衡。到了2021.0.x之后你如果不手动引入spring-cloud-starter-loadbalancer服务启动不报错但一旦调用就会报503 Service Unavailable。排查方法很简单看报错日志里有没有Load balancer does not contain an instance for the service这句话有的话就是缺依赖加上就完事。7.2 Nacos控制台看到一个服务下面出现两个名字相同但端口不同的实例这其实不是问题是服务发现正常工作。但有一种情况要注意如果同一个服务在不同机器上注册IP不同你要确认是同一套代码、同一套配置是分布式部署的多个实例而不是不同的服务误用了相同的spring.application.name。7.3 服务注册不上控制台看不到服务怎么办优先检查三条Nacos是单机模式启动的吗2.x默认集群模式不加-m standalone会报错或者启动完不工作spring.cloud.nacos.discovery.server-addr配置的IP和端口对不对本地用127.0.0.1本地8848端口有没有被其他进程占用被占了Nacos可能从8849端口启动服务自然就注册不上7.4 调用接口时返回JSON格式不正确或乱码Spring Boot 2.7默认返回的JSON编码是UTF-8基本不会乱码。如果自己手动拼了String字符串注意在注解里加producesGetMapping(value /list, produces application/json;charsetUTF-8)数据库连接串里的characterEncodingutf8也要检查一遍这两处是最容易产生中文乱码的源头。7.5 数据库连接失败的排查顺序数据库报错通常是最后踩到的坑因为要走到Consumer调Order才触发。从下面三个方向排查基本上一找一个准MySQL服务有没有启动、账号密码和权限对不对、数据库cloud_demo有没有建表。用Navicat先手动连一次能连上再启动服务。7.6 依赖下载慢或下载失败怎么处理SpringCloud和SpringCloud Alibaba的依赖一般都在中央仓库和阿里云仓库里本地Maven仓库如果之前没有缓存过首次下载会比较慢。建议在Maven的settings.xml里配置阿里云镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror配好之后IDEA里重新reimport一下Maven工程问题基本解决。7.7 端口冲突端口被占用怎么快速找到进程Windows下用netstat命令Linux用lsof或netstat。Windows示例netstat -ano | findstr 8081看到占用进程的PID后再查是哪个进程tasklist | findstr PID号是系统进程或者不认识的程序的直接杀taskkill /F /PID PID号7.8 Nacos 2.x版本还有一点特殊额外占用9848端口这一点非常隐蔽很多人踩了坑都不知道原因。Nacos 2.x除了8848之外还会额外使用9848端口用于gRPC通信。这个端口是主端口1000算出来的。如果服务器部署在阿里云、腾讯云上安全组里只开放了8848服务注册时会一直失败或者时好时坏报错信息里通常能看到Connection refused。要同时把9848端口也在防火墙和安全组里放开。7.9 启动时报错找不到主类或者缺少依赖多半是子模块没有正确引入父工程的依赖或者Maven打包时没有先把公共模块install到本地仓库。最简单粗暴的解决办法在根目录执行mvn clean install -DskipTests把所有模块先构建一遍再重新启动。这个方法能解决80%的IDE里能跑命令行跑不了问题。7.10 一个容易漏的点consumer-service的SpringBootApplication和EnableFeignClients扫描范围如果启动类放在了和FeignClient接口不同的包路径下就会出现找不到FeignClient的情况。EnableFeignClients默认扫描启动类所在包及子包。所以最省事的做法就是所有代码都放在com.example.consumer这个包下面不要乱建平行的包路径。微服务这套体系拆开看每一个技术其实都是解决某个具体问题的工具。Nacos管服务发现OpenFeign管服务调用LoadBalancer管请求分配。你把这个demo跑通之后再去看网关、配置中心、熔断限流这些内容思路会清晰得多。我最早学SpringCloud的时候也差点被一大堆概念劝退后来发现最好的方式就是先把这个最小闭环跑通从能跑到能说明白每一步在干什么这个进阶路径既平滑也不容易半途而废。最后再分享一个小技巧把order-service的日志级别调成DEBUG或者直接在Controller里加一条日志输出这样每次远程调用都能在消费者和提供者两个服务的控制台看到日志对应关系对整个调用链的理解会加深很多。动手实验永远是学框架最快的路。