Spring Cloud 微服务实战(二):改个配置还要重启服务?Nacos 配置中心 + OpenFeign 实战


个人主页:夏天拐跑了西瓜
专栏传送门:《大模型应用开发》、《Spring 生态全家桶体系化实战》
学习方向:Java 后端|AI‑Agent 大模型应用开发爱好者
⭐人生格言:路虽远,行则将至


封面图

本文是「Spring Cloud 微服务实战」系列的第二篇。在上一篇搭建好订单、库存服务的基础上,本文继续深入两个核心能力:一是 Nacos 配置中心,实现配置文件的集中管理与动态刷新;二是 OpenFeign,用声明式接口优雅地简化跨服务 HTTP 调用,并覆盖超时配置与底层客户端选型。

本文覆盖:Nacos 配置中心接入全流程(依赖引入 → 控制台创建配置 → bootstrap.yml 配置 → 动态刷新)、OpenFeign 声明式调用(依赖 → 接口定义 → 超时配置 → 底层客户端选型)、常见踩坑与版本差异说明。

适合读者:已搭建基础 Spring Cloud 项目、希望实现配置集中管理与服务间调用的开发者。

环境版本:Spring Boot 2.6.11、Spring Cloud 2021.0.4、Spring Cloud Alibaba 2021.0.4.0、Nacos 2.5.1、OpenFeign 3.x

系列文章导航

  • 《Spring Cloud 微服务实战(一):还在硬编码服务地址?Nacos 注册中心从零搭建》
  • 《Spring Cloud 微服务实战(二):改个配置还要重启服务?Nacos 配置中心 + OpenFeign 实战》(本篇)
  • 《Spring Cloud 微服务实战(三):一个服务挂了,凭什么拖垮整个系统?Sentinel 限流熔断实战》
  • 《Spring Cloud 微服务实战(四):每个请求都直连服务?你的集群缺一个 Gateway 门卫》

在这里插入图片描述

一、Nacos配置中心

1.0 前置条件

阅读本文前,请确保已完成系列第一篇的内容,即:

  • 已搭建好 Spring Cloud 父工程(Spring Boot 2.6.11 + Spring Cloud 2021.0.4 + Spring Cloud Alibaba 2021.0.4.0)
  • 已创建 order(订单)和 stock(库存)两个子服务
  • 两个服务已注册到 Nacos 注册中心
  • 已实现基于 Nacos 服务名的远程调用

如果你还没有完成上述步骤,建议先阅读《Spring Cloud 微服务实战(一):还在硬编码服务地址?Nacos 注册中心从零搭建》。

1.1 配置中心

在上一篇中,所有配置信息都存放在各服务自己的 application.yml 里。随着服务数量增多,这种方式会暴露出几个明显的问题,而配置中心正是针对这些问题设计的:

  • 集中管理:服务集群化部署后,配置文件分散在不同服务器上,缺乏统一的管理入口;
  • 环境隔离:从开发、测试、预发布到生产,需要维护多套环境的配置文件;
  • 动态刷新:本地配置文件修改后必须重启服务才能生效,配置中心可以做到修改后立即生效;
  • 权限控制:本地文本文件任何人都可以查看和修改,权限难以控制,配置中心提供了完善的权限体系;
  • 版本记录:配置中心会记录每次变更的历史版本,便于追溯与回滚。

1.2 Nacos初体验(配置中心)

本节的目标:将 order 服务中 server.port=80 的配置迁移到 Nacos 中统一管理,使 order 服务启动时从 Nacos 读取该配置,最终以 80 端口正常启动。

实现步骤如下:

1、导入 Nacos 配置中心依赖

<!-- Nacos 配置中心依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2、在 Nacos 控制台创建配置文件

image.png

3、在 order 的配置文件中指定 Nacos 地址及要加载的配置文件

spring:
  application:
    # 服务名,用于拼接 DataId
    name: order
  profiles:
    # 环境名,用于拼接 DataId
    active: study
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        # nacos 地址
        server-addr: 127.0.0.1:8848
        # 配置文件后缀,决定 DataId 格式
        file-extension: yml
#  DataId 拼接规则:${spring.application.name}-${spring.profiles.active}.${file-extension}
#  本例对应:order-study.yml

Nacos 配置中心默认按 ${spring.application.name}-${spring.profiles.active}.${file-extension} 的规则拼接 DataId,因此上面对应的配置文件名是 order-study.yml。

4、将 application.yml 重命名为 bootstrap.yml

配置中心的加载发生在应用上下文初始化之前,必须使用 bootstrap.yml 才能生效。

image.png

5、因版本变化,需补充 bootstrap 支持依赖

Spring Cloud 2020.x 之后,bootstrap 上下文默认关闭,需要显式引入:

<!-- bootstrap 上下文支持依赖(Spring Cloud 2020.x 后必须引入) -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

6、启动测试,观察日志确认配置加载成功

image.png

1.3 命名空间和分组(了解)

配置中心对命名空间与分组的使用方式与注册中心完全一致,此处不再展开原理,直接演示效果。

先在 dev 命名空间下创建 DEV_GROUP 分组的配置文件:

image.png

然后在 bootstrap.yml 中指定命名空间 ID 与分组:

spring:
  application:
    # 服务名
    name: order
  profiles:
    # 环境名
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        # nacos 地址
        server-addr: 127.0.0.1:8848
        # 配置文件后缀
        file-extension: yml
        # 命名空间 ID(注意是 UUID,不是名称)
        namespace: 06173ada-fb39-47b3-8551-7265a5177770
        # 分组名称
        group: DEV_GROUP

注意:命名空间配置填写的是命名空间 ID(一串 UUID),而不是命名空间名称。

1.4 配置动态刷新

动态刷新的目标:当 Nacos 中的配置发生变更时,服务无需重启即可读取到最新值。

先准备一个测试接口,读取 Nacos 配置中的 info 属性并返回:

@RestController
public class OrderController {

    // 从 Nacos 配置中心读取 info 属性,默认值为 empty
    @Value("${info:empty}")
    private String info;

    @GetMapping("/order/info")
    public String info() {
        return info;
    }
}

此时修改 Nacos 中的配置后再次访问接口,返回值不会变化:

image.png

image.png


要开启动态刷新,只需在使用该配置的类上追加一个注解:

// 添加 @RefreshScope 注解,开启配置动态刷新
@RestController
@RefreshScope
public class OrderController {

    @Value("${info:empty}")
    private String info;

    @GetMapping("/order/info")
    public String info() {
        return info;
    }
}

添加 @RefreshScope 注解后,再次测试,Nacos 中修改配置即可实时生效:

image.png

二、OpenFeign

2.1 OpenFeign介绍

OpenFeign 是 Spring Cloud 提供的声明式 RPC 调用框架(注意它属于 Spring Cloud,而非 Alibaba 组件)。Alibaba 生态中与之对应的是 Dubbo,两者定位相同,区别在于通信协议:

  • Dubbo 使用自研的 Dubbo 协议;
  • OpenFeign 基于 HTTP 协议。

OpenFeign 的核心价值是简化 Java 中 HTTP 请求的编写过程,让远程调用代码具备更好的可维护性。它与 Sentinel、Nacos、Hystrix、LoadBalancer 等组件都可以低成本整合。

实现原理上,OpenFeign 基于接口(interface)封装,运行时生成代理对象完成实际调用——编写方式与单体项目中调用本地接口完全一致,这也是它最大的优势。

2.2 OpenFeign的初体验

按官方文档的步骤接入,共四步:

1、导入依赖

<!-- OpenFeign 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2、启动类追加注解

// 启动类添加 @EnableFeignClients 注解,开启 Feign 客户端扫描
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

3、编写 OpenFeign Client 接口,与目标服务的接口定义保持映射

package com.study.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

// @FeignClient 指定目标服务名(Nacos 中注册的服务名)
@FeignClient("stock")
public interface StockClient {

    // 路径和方法签名应与目标服务的 Controller 保持一致
    // 建议直接从 stock 服务的 Controller 中复制
    @GetMapping("/stock/test")
    String test();

}

建议:接口上的路径与方法签名直接从目标服务的 Controller 中复制,避免手写产生偏差。

4、在 order 服务中编写测试接口验证

@RestController
public class OrderController {

    // 注入 Feign 客户端,像调用本地方法一样发起远程调用
    @Autowired
    private StockClient stockClient;

    // 基于 OpenFeign 调用 stock 服务
    @GetMapping("/order/feign")
    public String feign() {
        String result = stockClient.test();
        return result;
    }
}

注入 StockClient 后像调用本地方法一样发起远程调用,访问 /order/feign 验证效果:

image.png

image.png

2.3 OpenFeign配置

2.3.1 OpenFeign的超时配置

官方文档中,OpenFeign 的超时配置只有两项:

  • connectTimeout prevents blocking the caller due to the long server processing time.
  • readTimeout is applied from the time of connection establishment and is triggered when returning the response takes too long.

两者的定位不同:

  • connectTimeout:客户端与服务端建立 TCP 连接的超时时间,一般保持默认即可;
  • readTimeout:从请求发出到收到响应的超时时间,是实际调优中最常配置的项。

验证方式:让 stock 服务的接口休眠一段时间,观察达到超时阈值后的表现。order 服务配置如下:

feign:
  client:
    config:
      stock:  # 针对 stock 服务的配置
        readTimeout: 1000    # 响应超时时间 1s,超过则直接报错

超时后抛出的异常:

image.png

注意版本差异:早期版本中 OpenFeign 的默认超时时间是 1 秒,而当前版本的默认值已放宽到 5 秒以上。另外首次请求可能因客户端初始化而偏慢,属于正常现象。

除按服务配置外,OpenFeign 还支持全局默认配置,两者同时存在时按服务配置的优先级更高:

feign:
  client:
    config:
      default:  # 全局默认配置,优先级低
        read-timeout: 2000
      stock:  # 针对 stock 服务的细粒度配置,优先级高
        readTimeout: 6000

2.3.2 OpenFeign的底层技术

当前版本的 OpenFeign 默认使用 HttpClient4 作为底层 HTTP 客户端,也可以通过配置切换为 OkHttp 或 HC5(HttpClient5),三选一。

三种客户端的对比如下:

对比维度HttpClient4OkHttpHC5
协议支持基础 HTTP/1.1HTTP/2 支持更完善HTTP/2 支持完善
性能良好略优略优
维护方ApacheSquareApache
异步支持✓✓✓
推荐度默认,够用即可推荐,社区活跃推荐,Apache 新一代

配置方式:

默认使用 HttpClient4:

feign:
  httpclient:
    enabled: true   # 默认使用 HttpClient4

切换为 HC5:

feign:
  httpclient:
    hc5:
      enabled: true   # 启用 HC5 作为底层客户端

切换为 OkHttp:

feign:
  okhttp:
    enabled: true   # 启用 OkHttp 作为底层客户端

三、常见问题与踩坑总结

在实际开发中,以下是容易踩的坑,建议提前了解:

1. 配置修改后未生效

  • 原因:忘记在使用配置的类上添加 @RefreshScope 注解
  • 解决:给需要动态刷新配置的 Controller 或 Bean 加上注解

2. bootstrap.yml 不加载

  • 原因:Spring Cloud 2020.x 之后默认关闭 bootstrap 上下文
  • 解决:显式引入 spring-cloud-starter-bootstrap 依赖

3. 命名空间配置不生效

  • 原因:在 namespace 字段填了命名空间名称,而非 ID(UUID)
  • 解决:去 Nacos 控制台复制命名空间的 ID(一串 UUID)

4. OpenFeign 首次调用慢

  • 原因:客户端首次调用时需要初始化连接池、加载配置,属于正常现象
  • 解决:无需处理,后续调用会恢复正常

5. DataId 拼接规则混淆

  • 规则:${spring.application.name}-${spring.profiles.active}.${file-extension}
  • 示例:order-study.yml
  • 注意:如果 spring.profiles.active 未配置,则拼接为 order.yml

6. 超时配置不生效

  • 原因:同时配置了 default 和按服务名配置,按服务名优先级更高
  • 解决:确认配置层级,按服务名配置会覆盖全局默认

四、总结

本文围绕微服务的配置管理与远程调用两大主题,核心收获如下:

  • Nacos 配置中心实现配置文件集中管理,支持命名空间与分组做多环境隔离。
  • 配合 @RefreshScope 注解实现配置动态刷新,修改配置无需重启服务。
  • OpenFeign 通过 @FeignClient 声明式接口简化跨服务 HTTP 调用,代码结构更清晰。
  • 掌握 Feign 超时的全局与按服务配置,以及 HttpClient4、HC5、OkHttp 三种底层客户端的切换。

下一篇预告:Sentinel 限流、熔断与降级,解决微服务架构中的服务雪崩问题。

感兴趣的同学记得 点赞 + 关注,第一时间获取更新通知!

更多推荐