最近帮同事搭一套新的微服务开发环境,顺手把整个流程记录了下来:在 IntelliJ IDEA 里面,用 JDK17 跑 Spring Boot 3,再配合 Nacos 做服务注册和配置中心。整套环境从零到两个服务互相调用、配置动态刷新全部跑通,花了一天时间。中间踩了不少坑,有些问题百度上搜出来的答案已经过时了,专门整理成这篇图文教程,给准备从 Spring Boot 2.x 升级到 3.x、或者第一次接触 Nacos 的朋友做个参考。
下面的内容会覆盖环境准备、Nacos 安装与安全配置、Spring Boot 工程创建、注册中心接入、配置中心动态刷新,最后是一份宝贵的问题排查记录。跟着做不仅能跑通,还能理解每一步为什么要这么配,以后换个版本组合也知道怎么查问题。
1. 搭建前的环境准备与版本选型
1.1 JDK17 与 Spring Boot 版本怎么搭配
先说版本选型,这是很多人第一步就栽跟头的地方。JDK17 是 Java 17 LTS 版本,2021 年 9 月发布,现在很多公司的生产环境已经从 JDK8 切到 JDK17 了。Spring Boot 这边,如果你打算用 JDK17,最推荐的是Spring Boot 3.x,因为 Spring Boot 3.0 开始官方就把 JDK17 作为基础版本,整个框架是在 Jakarta EE 9 之上重构的(注意javax.*变成了jakarta.*)。
有人会问:Spring Boot 2.7 也支持 JDK17,能不能用?能用,但我不建议新项目这么干。Spring Boot 2.7 虽然能跑在 JDK17 上,但它骨子里还是为 JDK8 设计的,很多第三方库的兼容性问题会在后面的开发中慢慢冒出来。既然是新环境,直接上 Spring Boot 3 + JDK17 是最省心的。
Spring Cloud 和 Spring Cloud Alibaba 的版本也要对应好。我这次实测下来的稳定组合是:
| 组件 | 版本 |
|---|---|
| JDK | 17(Oracle JDK 或 Adoptium/Temurin 均可) |
| Spring Boot | 3.2.x |
| Spring Cloud | 2023.0.x |
| Spring Cloud Alibaba | 2023.0.1.x |
| Nacos Server | 2.3.x 稳定版 |
| IDEA | 2023.2 及以上(社区版也可以) |
| Maven | 3.8.x 或 3.9.x |
这个组合是经过实际验证的,Spring Cloud Alibaba 2023.0.1.x 对应 Spring Cloud 2023.0.x,底层内置的 nacos-client 版本是 2.3.x,跟 Nacos Server 2.3.x 配合最默契。如果你用了 Nacos Server 2.2.x 或者更老的 1.x,客户端接口会有差异,后面服务注册可能会报各种奇怪的错。
JDK17 下载安装本身不难,Windows 用户直接去 Adoptium 官网(或者 Oracle 官网)下载安装包,安装完成后设置JAVA_HOME环境变量为安装路径(比如C:\Program Files\Microsoft\jdk-17.x.x),然后把%JAVA_HOME%\bin加到Path里。这里有个小细节:Windows 上安装 Oracle JDK 有时候会自动把C:\Program Files\Common Files\Oracle\Java\javapath排在环境变量前面,导致终端里java -version显示的版本跟你装的 JDK17 不一样。出现这种情况,把 Path 里的javapath删掉,或者把你的%JAVA_HOME%\bin调整到最前面即可。
1.2 IDEA 与 Maven 的基础配置
IDEA 版本方面,社区版(Community Edition)完全够用。之前有些教程说社区版不能创建 Spring Boot 项目,那是老黄历了。IDEA 2023.1 之后的社区版已经内置了 Spring Initializr 支持,新建项目可以直接选 Spring Boot 版本生成工程。如果你用的版本比较旧,也可以到 start.spring.io 网站上生成项目压缩包再导入 IDEA,效果一样。
IDEA 里用 JDK17 有三个地方要检查,漏一个都会出问题:
- Project Structure 里的 Project SDK:
File -> Project Structure -> Project -> SDK,选择 17。 - Project Structure 里的 Language Level:同样在 Project 菜单里,Language Level 选择 17。如果选低了,代码里用不了 JDK17 新语法。
- Settings 里的 Maven 配置:
File -> Settings -> Build, Execution, Deployment -> Build Tools -> Maven,这里有个Runner -> JRE选项,也要选 17。
Maven 这边,建议先配置好阿里云镜像,不然 Spring Boot 3 的依赖下载会让你怀疑人生。在 Maven 安装目录conf/settings.xml里添加以下 mirror:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>配置完镜像后,IDEA 的 Maven 设置里要把User settings file指向这个settings.xml,然后点Reload All Maven Projects让设置生效。这里容易踩的坑是:IDEA 默认会用内置的 Maven,而不是你下载的 Maven,所以你在命令里用的 Maven 和 IDE 里用的一致不一致要看清楚,建议用 IDEA 内置的 Maven 就行,只需要把settings.xml指对。
2. Nacos 安装、启动与安全配置
2.1 下载与单机启动
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的服务发现和配置管理平台,在 Java 微服务生态里用得很多。它的作用可以用一句话概括:让服务互相能找到对方,并且能让配置在不重启服务的情况下动态更新。
去 Nacos 官网 GitHub Releases 页面下载最新稳定版,我用的是 2.3.2。下载nacos-server-2.3.2.zip或者.tar.gz即可。Windows 用户解压后进入bin目录,注意 Nacos 默认是集群模式启动,单机开发必须加-m standalone参数:
startup.cmd -m standaloneMac / Linux 用户用:
sh startup.sh -m standalone启动成功后,浏览器访问http://localhost:8848/nacos,默认用户名和密码都是nacos/nacos。注意路径里的/nacos前缀,很多新手直接访问 8848 端口发现是白屏,就是因为少了这个上下文路径。
Nacos 默认会使用内嵌的 Derby 数据库存储配置信息,这个对开发来说够用,重启数据也不会丢太多。但如果你打算一直用下去,建议提前切换到 MySQL。在conf/application.properties里把数据源改成 MySQL,再执行conf/mysql-schema.sql初始化数据库表。我这次在开发环境就是用的 Derby,就不展开 MySQL 切换了,不过建议正式环境务必切 MySQL,不然后面配置多了、集群节点一多,Derby 的并发和一致性都会成问题。
2.2 开启鉴权并修改默认密码
Nacos 默认没有开启鉴权,这意味着任何能访问你 8848 端口的人,都能看到你所有服务的注册信息、改你的配置,这是很大的安全隐患。社区里常说的“Nacos namespaces 未授权访问漏洞”“Nacos 配置泄露”,绝大多数就是因为默认没开鉴权。
开启鉴权的方法:在conf/application.properties里添加以下配置:
nacos.core.auth.enabled=true nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMDEyMzQ1Njc4OQ== nacos.core.auth.server.identity.key=serverIdentity nacos.core.auth.server.identity.value=exampleServerIdentitytoken.secret.key这个配置容易踩坑:Nacos 2.x 要求这个值必须是 Base64 编码的字符串,且原文长度不低于 32 字节,否则启动时会报错或者鉴权不生效。上面的示例值是可以用,生产环境建议自己生成一个随机值,随便找个在线工具把一段 32 字符以上的随机字符串做 Base64 编码即可。
配置完成后重启 Nacos,再用浏览器登录,会发现登录接口多了鉴权检查。启用鉴权后,你的 Spring Boot 服务在连接 Nacos 时也需要带上用户名密码,这个在后面的客户端配置里会讲到。
另外,登录后第一件事就是把默认密码改掉。在 Nacos 控制台右上角“修改密码”里操作即可。社区里有个高频报错:“nacos 页面修改密码报错 request error, please try again later!”,这个我遇到过,大多是 Nacos 客户端版本和 Server 版本不一致导致的,或者是在鉴权开启之前就登录了老会话。遇到这个报错,先确认 Server 和 nacos-client 版本一致,再试试清除浏览器缓存、重新登录。
2.3 Docker Compose 方式部署 Nacos(可选)
如果你用的是 Mac 或者 Linux,本地装了 Docker,也可以直接用 Docker Compose 起一个 Nacos,干净又方便。这次我在一台 Ubuntu 环境测试时用的就是 Docker 方式,写个docker-compose.yml:
services: nacos: image: nacos/nacos-server:v2.3.2 container_name: nacos environment: - MODE=standalone - NACOS_AUTH_ENABLE=true - NACOS_AUTH_TOKEN=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMDEyMzQ1Njc4OQ== - NACOS_AUTH_IDENTITY_KEY=serverIdentity - NACOS_AUTH_IDENTITY_VALUE=exampleServerIdentity ports: - "8848:8848" - "9848:9848"然后执行:
docker compose up -d这里有个 Nacos 2.x 的端口说明:Nacos 2.x 客户端默认会使用9848端口做 gRPC 通信,如果只映射了 8848 没映射 9848,服务注册和发现都会失败。这个报错在日志里往往很模糊(比如 timeout 或者 connection reset),非常容易被忽略。建议在搭建环境前就把这个端口映射加上。
启动完成后一样的地址登录控制台。Docker 方式的好处是以后不想用了docker compose down就清理干净了,不会像本机解压部署那样留一堆残留进程。
3. 创建 Spring Boot 工程并接入 Nacos
3.1 Spring Boot 工程的创建
打开 IDEA,File -> New -> Project,选择Spring Initializr。这里注意:
- Name 填
user-service(后面做服务提供方)。 - Type 选 Maven。
- Language 选 Java。
- Java 版本选 17。
依赖这里只需要加一个Spring Web,剩下的 Nacos 相关依赖我们后面手动加,因为 Spring Initializr 里的 Alibaba 依赖列表不一定跟你的 Spring Boot 版本匹配,手动加更可控。
创建完工程后,确认pom.xml里 Spring Boot 版本是不是 3.2.x。然后手动添加以下依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> </parent> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2023.0.1.0</version> </dependency> </dependencies>注意:Spring Boot 3 和 Spring Cloud 的依赖管理不用再像 Spring Boot 2 那样引入spring-cloud-dependencies的import类型依赖了?不对,其实还是需要的。Spring Boot 3 的 parent 里并不会帮你管理 Spring Cloud 的版本,所以要再加两个 dependencyManagement:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.1</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2023.0.1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>这样依赖版本就由 BOM 统一管住了,你的业务依赖里不用写版本号。如果你不想手动管理版本,直接复制这段就行。
3.2 引入 Nacos 注册中心配置
创建一个 Spring Boot 服务接入 Nacos,核心配置在application.yml里。我这次用的配置如下:
server: port: 8080 spring: application: name: user-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 username: nacos password: nacos namespace: public config: server-addr: 127.0.0.1:8848 username: nacos password: nacos file-extension: yml关键点展开讲:
spring.application.name必须设置。Nacos 控制台里显示的服务名,就是从这里取的。如果没有这个配置,注册到 Nacos 的服务会显示为unknown。username和password:如果你按前面教程开启了鉴权,这里必须带上账号密码,否则服务启动时会报 403 或者无法注册。namespace:有两个选择,public(默认命名空间)或者具体命名空间 ID。如果你没特意创建命名空间,用public就行。如果填了不存在的命名空间 ID,服务注册界面可能什么都看不到。- Nacos 地址的配置有两个位置,一个是
discovery(注册中心),一个是config(配置中心),两者可以指向同一个 Nacos Server,也可以分开。
这里提醒一个容易踩的坑:Spring Boot 3 里,Nacos config 相关功能只依赖spring-cloud-starter-alibaba-nacos-config,而注册中心依赖是spring-cloud-starter-alibaba-nacos-discovery。如果你没有引入 config 依赖,spring.cloud.nacos.config配置会被忽略(不会报错,但不会生效)。我在第一次测试的时候只引入了 discovery,结果动态刷新一直不生效,排查了半天。
3.3 注册中心接入验证
加入依赖和配置后,启动user-service。启动成功后再写一个最简单的接口用于验证:
@RestController public class UserController { @GetMapping("/user/hello") public String hello() { return "Hello from user-service"; } }启动完成后,打开 Nacos 控制台http://localhost:8848/nacos,进入“服务管理 -> 服务列表”,如果能看到user-service的实例,并且状态为“健康”,说明注册中心已经打通了。
如果服务列表里没有东西,优先看日志。Nacos 客户端启动时会输出类似这样一行日志:
Nacos registry, DEFAULT_GROUP user-service 127.0.0.1:8080 register finished如果看到这一行,说明客户端已经注册成功了,控制台没显示通常是命名空间选错了,检查一下控制台左上角选的是不是public。如果没有这行日志,就要看前面有没有异常,常见的是 9848 端口不通、鉴权失败(403)、或者 Nacos 版本不兼容。
4. 配置中心接入与动态刷新
4.1 Nacos 配置中心的数据组织方式
Nacos 作为配置中心,数据是分层的:命名空间(Namespace) -> 分组(Group) -> 配置(Data ID)。这个概念理解了,后面配置管理就顺了。
- Namespace:用来做环境隔离,比如开发、测试、生产各一个命名空间,相互之间配置不干扰。默认是
public。 - Group:默认是
DEFAULT_GROUP。可以在同一个命名空间下按业务域拆分组。 - Data ID:具体某份配置。默认的格式是
${spring.application.name}.${file-extension}。比如user-service.yml。
对于单个服务,最常用的是直接在 Nacos 里创建一条 Data ID 为user-service.yml的配置,这个 Data ID 会被 Spring Boot 服务自动加载。这是 Nacos 约定优于配置的体现,因为spring.application.name加上文件后缀正好组成默认的 Data ID,不需要额外指定。
4.2 动态刷新配置实战
在user-service的pom.xml里加配置中心依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2023.0.1.0</version> </dependency>注意:Spring Boot 3 默认不会主动加载bootstrap.yml文件,需要额外的 bootstrap 依赖。简单点说,我们在配置中心加载配置用得最多的方式是spring.config.import,即在application.yml里加一行:
spring: config: import: optional:nacos:user-service.yml它的意思是:应用启动时把 Nacos 里 Data ID 为user-service.yml的配置导入进来。optional:前缀表示哪怕 Nacos 连不上,应用也能启动,不会因为配置中心暂时不可用就直接挂掉。
然后咱们在 Nacos 控制台新增一条配置:
- Data ID:
user-service.yml - Group:
DEFAULT_GROUP - 配置格式:YAML
配置内容加一条测试配置:
user: nickname: zhangsan age: 25在代码里写一个验证用的 Controller:
@RestController @RefreshScope public class ConfigController { @Value("${user.nickname:default}") private String nickname; @Value("${user.age:0}") private int age; @GetMapping("/config") public String config() { return "nickname=" + nickname + ", age=" + age; } }关键点是@RefreshScope,不加这个注解的话,配置虽然能从 Nacos 拉下来,但修改后不会自动刷新到 Bean 里。加上之后,当你修改 Nacos 里的配置并发布,Spring Cloud 会通过事件机制刷新这个 Bean,再次请求/config接口就能拿到新值了。
这里有个容易犯的错:@ConfigurationProperties和@Value的刷新机制不一样。@Value配合@RefreshScope可以刷新,@ConfigurationProperties如果类本身没有被@RefreshScope标注,即使被注入到其他 Bean 里,刷新也不会生效。建议在项目里优先用@ConfigurationProperties,把一组配置封装成类,然后在类上加上@RefreshScope,代码更整洁:
@Component @ConfigurationProperties(prefix = "user") @RefreshScope public class UserProperties { private String nickname; private int age; // getter and setter 省略 }启动user-service,访问/config,如果返回nickname=zhangsan, age=25,说明配置中心加载成功。然后回 Nacos 控制台把nickname改成lisi,点发布,再刷新请求,几秒内就能看到新值。
我实测时大概有 3-5 秒的延迟(因为客户端默认配置了长轮询),如果你的要求是秒级甚至毫秒级,可以调小spring.cloud.nacos.config.timeout之类参数,但是没必要,开发环境默认就很好用。
5. 服务发现与调用实战
5.1 再创建一个消费者服务
光有服务注册、没有服务调用,等于只搭了一半。咱们再来一个order-service,让它通过 Nacos 发现user-service并调用接口。
创建方式和前面一样:Spring Initializr 新建项目,order-service,端口设成 8081。pom.xml里引入跟前面一样的 discovery 依赖。
代码里关键的配置是消费端的服务发现和负载均衡。Spring Cloud 2023.0.x 默认使用的是 LoadBalancer 组件,不用再单独引入 Ribbon(Ribbon 已经进入维护状态了)。咱们在启动类里声明一个带负载均衡的RestTemplate:
@SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }@LoadBalanced是点睛之笔。它会让 RestTemplate 发请求时自动把“服务名”解析成真实的 IP:Port,然后从 Nacos 拿到user-service的实例列表做负载均衡。如果忘了加这个注解,直接写http://user-service/user/hello,运行时会报“UnknownHostException”。
写一个测试接口:
@RestController public class OrderController { @Resource private RestTemplate restTemplate; @GetMapping("/order/call") public String call() { String url = "http://user-service/user/hello"; return restTemplate.getForObject(url, String.class); } }启动order-service,访问http://localhost:8081/order/call,返回Hello from user-service就说明服务发现和调用全部打通了。
5.2 验证负载均衡与自我保护
为了直观看到负载均衡,可以同时启动两个user-service实例,一个 8080 端口,一个 8082 端口(启动时用--server.port=8082参数覆盖)。两个实例都注册到 Nacos 后,控制台的服务列表里user-service会显示两个实例。
连续访问/order/call多次,你会看到请求在两个实例之间轮询分发,日志里两个服务各自收到部分请求。这个负载均衡能力是客户端侧做的,不需要额外的网关或者代理,生产环境做多实例部署时这一点很关键。
再提一个 Nacos 的自我保护机制:当某个服务实例心跳消失(比如被强杀),默认会保留 15 秒左右才把它从健康实例列表移除。所以如果你测出“服务明明停了,但接口还能调通”的现象,别慌,等十几秒再试。这个时间可以在服务端的配置里调整,但开发环境用默认值就够了。
6. 常见问题与排查技巧实录
6.1 版本不匹配类问题
这一部分是全流程里踩坑最多的地方,也是社区里问得最多的。我做了一个速查表,基本覆盖了常见报错:
| 报错现象 | 原因 | 解决办法 |
|---|---|---|
启动报NoSuchMethodError或者ClassNotFoundException | 一般是 nacos-client 版本和 Spring Cloud Alibaba 版本不匹配 | 用 BOM 统一管理版本,别手动指定不兼容的 nacos-client |
| 服务注册到 Nacos 控制台看不到 | 命名空间不对 / 8848 与 9848 端口未都通 | 检查控制台命名空间、确认 9848 端口已映射 |
| 连接 Nacos 报 403 / Access denied | 服务端开了鉴权,客户端没配账号密码 | 在application.yml的 discovery/config 里配置username和password |
@RefreshScope注解加了还是不刷新 | 配置中心依赖没引入 / Data ID 不对 / 没走 spring.config.import | 检查spring-cloud-starter-alibaba-nacos-config依赖,确认import的 Data ID |
启动时说找不到nacos config配置 | Spring Boot 3 没识别 bootstrap.yml | 用spring.config.import或者在打包时引入 bootstrap 相关依赖 |
这里特别强调一个容易混淆的地方:Nacos 服务端版本、nacos-client 客户端版本、Spring Cloud Alibaba 版本,这三者是三个独立的东西。网上很多教程直接给一个 nacos-client 的坐标让你引入,但 Spring Cloud Alibaba 自己内置了客户端版本,你手动覆盖它极容易引发接口不兼容。尽量别手动引入 nacos-client,让 Spring Cloud Alibaba BOM 来管理。
6.2 连接与启动类问题
9848 端口不通是最隐蔽的问题。Nacos 2.x 从客户端到服务端有一半通信走 gRPC,端口是 8848+1000=9848。本机测试通常没问题,但如果你以后把服务部署到远程服务器,防火墙只开了 8848,服务注册就会时好时坏,日志里出现:
Failed to request to server: /127.0.0.1:8848, code: 501, message: Not Implemented看到501 Not Implemented大概率就是这个原因。解决办法就是安全组或者防火墙同时放行 8848 和 9848。
启动顺序问题:Nacos Server 最好先启动,再启动 Spring Boot 服务。虽然客户端配置了optional:后连不上 Nacos 也能启动,但服务注册时机错过之后要等心跳重连,往往过了 30 秒到 1 分钟才在控制台出现,容易让人误以为配置有问题。
IDEA 社区版创建 Spring Boot 工程的问题:社区版确实能用,但如果你用的版本较早(2022.x 之前),创建项目时没有 Spring Initializr 选项,可以去 start.spring.io 生成项目,然后 File -> Open 导入即可。公司如果有安全要求不能访问外网,也可以用镜像站点,比如阿里云的https://start.aliyun.com,生成的工程结构和官方的一致,但注意这个站点的 Spring Boot 版本列表偏老,可能没有 3.2.x,生成后手动改 parent 版本即可。
6.3 配置与安全类问题
关于 Nacos 页面那个“修改密码报错 request error, please try again later!”,我这次也碰到一次。原因是 Nacos Server 是 2.2.0,而浏览器缓存的登录 Token 是开启鉴权之前的旧 Token,导致请求鉴权不过。解决办法很简单:清掉浏览器缓存,重新登录,再修改密码。
如果你在生产环境用 Nacos,强烈建议把以下几件事做了:
- 开启鉴权,修改默认密码。
- 配置访问白名单,限制 8848 端口的来源 IP。
- 定期备份 Nacos 的数据库(如果用 MySQL 的话)。
- 重要配置拆成多个 Data ID,避免超大配置块。
另外,很多人问 Nacos 的替代方案。如果是中小项目,不想引入额外组件,直接用 Spring Cloud Config + Git 也可以,但 Nacos 胜在“注册中心 + 配置中心”二合一,控制台界面也直观,运维成本低。如果是 K8s 环境,还可以考虑使用 K8s 原生的 ConfigMap + 服务发现,不过那套体系跟 Spring Cloud 生态融合程度不如 Nacos。
最后再分享一个小技巧:Nacos 控制台左侧菜单里有一个“监听查询”,可以看当前配置被哪些服务订阅了。当你在纠结“配置到底有没有被加载”的时候,去监听查询里搜 Data ID,如果能看到对应的客户端 IP,说明配置已经分发下去了,问题一定出在应用侧的刷新逻辑上,而不是配置通道。这个功能知道的人不多,排查问题却非常实用,建议先记下来。
这套环境搭完之后,后面还可以继续扩展:加一个 Spring Cloud Gateway 做统一入口,或者把配置中心的配置按环境拆分到不同命名空间,再或者接一套 Nacos 集群保证高可用。不过对于刚上手的人来说,先把注册中心和配置中心这两个核心能力玩熟,比什么都重要。