SpringCloud学习笔记(三)Nacos配置管理与热更新、Feign远程调用替代RestTemplate

文章目录

  • 前言
  • [6 Nacos配置管理](#6 Nacos配置管理)
    • [6.1 在Nacos中添加配置文件](#6.1 在Nacos中添加配置文件)
    • [6.2 微服务拉取配置](#6.2 微服务拉取配置)
    • [6.3 配置热更新](#6.3 配置热更新)
      • [6.3.1 方式一:`@RefreshScope`注解](#6.3.1 方式一:@RefreshScope注解)
      • [6.3.2 方式二:`@ConfigurationProperties`注解](#6.3.2 方式二:@ConfigurationProperties注解)
    • [6.4 配置共享](#6.4 配置共享)
      • [6.4.1 添加一个环境共享配置](#6.4.1 添加一个环境共享配置)
      • [6.4.2 实现读取共享配置](#6.4.2 实现读取共享配置)
    • [6.5 配置共享的优先级](#6.5 配置共享的优先级)
  • [7 Feign远程调用](#7 Feign远程调用)
    • [7.1 使用Feign替代RestTemplate](#7.1 使用Feign替代RestTemplate)
    • [7.2 自定义配置](#7.2 自定义配置)
      • [7.2.1 配置文件方式](#7.2.1 配置文件方式)
      • [7.2.2 Java代码方式](#7.2.2 Java代码方式)

前言

SpringCloud学习笔记系列文章:

SpringCloud学习笔记(一)微服务介绍、服务拆分和RestTemplate远程调用、Eureka注册中心
SpringCloud学习笔记(二)Ribbon负载均衡、Nacos注册中心、Nacos与Eureka的区别

6 Nacos配置管理

Nacos除了可以做注册中心,还可以做配置管理。

当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会变得非常麻烦,而且很容易出错。因此,这就需要一个统一配置管理方案,可以集中管理所有实例的配置。

Nacos一方面可以将配置集中管理,另一方面可以在配置变更时,及时通知微服务,实现配置的热更新。如图:

6.1 在Nacos中添加配置文件

在Nacos管理页面,进入"配置管理"-"配置列表"页面,点击"创建配置":

在"新建配置"页面填写配置信息:

填写完成后点击"发布",即可新增一条配置:

要注意的是,一般是需要热更新的配置才有放到Nacos管理的必要,基本不会变更的配置还是保存在微服务本地比较好。

6.2 微服务拉取配置

微服务要拉取Nacos中管理的配置文件,并且与本地的application.yml配置文件合并,才能完成项目启动。但Nacos地址是配置在本地的application.yml文件中的,启动前服务尚未读取本地的application.yml,又如何得知Nacos地址呢?

为此,Spring引入了一种新的配置文件:bootstrap.yaml文件。它会在application.yml之前被读取,其流程如下:

  • 1)引入nacos-config依赖

在user-service工程中,引入nacos-config依赖:

xml 复制代码
<!--sc_demo\user-service\pom.xml-->

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  • 2)添加bootstrap.yaml

在user-service工程的resources目录下,添加bootstrap.yaml文件,其内容如下:

yaml 复制代码
# sc_demo\user-service\src\main\resources\bootstrap.yaml

spring:
  application:
    # 服务名称
    name: user-service
  profiles:
    #开发环境
    active: dev
  cloud:
    nacos:
      # Nacos地址
      server-addr: localhost:8848 
      config:
        # 文件后缀名
        file-extension: yaml

通过该配置,可以根据spring.cloud.nacos.server-addr获取Nacos地址,再根据${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id,去Nacos读取配置。在本例中,就是去读取user-service-dev.yaml

  • 3)读取Nacos配置

下面在UserController类中编写一个测试方法来读取Nacos配置:

java 复制代码
// com.star.user.web.UserController

@Value("${dir.upload}")
private String uploadDir;
    
@GetMapping("/now")
public String now() {
    System.out.println("读取Nacos配置:dir.upload = " + uploadDir);
    return uploadDir;
}

调用http://127.0.0.1:8081/user/now接口,控制台打印信息如下:

读取Nacos配置:dir.upload = /usr/local/upload

可见,Nacos中的配置已被成功读取。

6.3 配置热更新

使用Nacos管理配置文件的目的,是修改Nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新

要实现配置热更新,有两种方式:

6.3.1 方式一:@RefreshScope注解

在使用@Value注解注入变量的类上添加@RefreshScope注解,例如UserController类:

java 复制代码
// com.star.user.web.UserController

@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {

    @Value("${dir.upload}")
    private String uploadDir;
    
    // ...
}

重启user-service(8081)服务后,修改Nacos中的配置:

再次调用http://127.0.0.1:8081/user/now接口,控制台打印信息如下:

可见,user-service(8081)服务发现了配置的修改,并自动更新。

6.3.2 方式二:@ConfigurationProperties注解

在user-service工程中,添加一个ConfigProperties配置类用于读取配置:

java 复制代码
// com.star.user.config.ConfigProperties

@Component
@Data
@ConfigurationProperties(prefix = "dir")
public class ConfigProperties {
    private String upload;
}

然后在UserController类中直接注入ConfigProperties类,以替代@Value注解:

java 复制代码
// com.star.user.web.UserController

// @Value("${dir.upload}")
// private String uploadDir;

@Autowired
private ConfigProperties configProperties;

@GetMapping("/now")
public String now() {
    // System.out.println("读取Nacos配置:dir.upload = " + uploadDir);
    // return uploadDir;
    System.out.println("通过ConfigProperties读取Nacos配置:dir.upload = " + configProperties.getUpload());
    return configProperties.getUpload();
}

重启user-service(8081)服务后,修改Nacos中的配置:

user-service(8081)服务检测到了配置的修改,在控制台打印修改信息:

再次调用http://127.0.0.1:8081/user/now接口,控制台打印信息如下:

可见,user-service(8081)服务发现了配置的修改,并自动更新。

6.4 配置共享

微服务在启动时,会去Nacos读取多个配置文件,包括:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:user-service-dev.yaml
  • [spring.application.name].yaml,例如:user-service.yaml

第一种即上文读取的配置文件,而第二种[spring.application.name].yaml不包含环境信息,因此可以被多个环境共享。

6.4.1 添加一个环境共享配置

6.4.2 实现读取共享配置

在user-service工程中,修改ConfigProperties类,添加要读取的共享属性:

java 复制代码
// com.star.user.config.ConfigProperties

@Component
@Data
@ConfigurationProperties(prefix = "dir")
public class ConfigProperties {
    private String upload;
    private String envSharedValue;
}

修改UserController类,添加一个读取共享属性方法:

java 复制代码
// com.star.user.web.UserController

@Autowired
private ConfigProperties configProperties;

@GetMapping("/share")
public ConfigProperties share() {
    return configProperties;
}

配置user-service(8081)服务的profile为dev,user-service(8082)服务的profile为test,启动这两个服务:

调用http://127.0.0.1:8081/user/share接口,返回结果如下:

再调用http://127.0.0.1:8082/user/share接口,返回结果如下:

可见,不同的服务实例都读取到了user-service.yaml文件的配置,但只有配置了profile为dev的实例才能读取user-service-dev.yaml文件的配置。

6.5 配置共享的优先级

当Nacos、服务本地同时出现相同配置属性时,优先级有高低之分,如图:

7 Feign远程调用

目前,项目中是利用RestTemplate发起远程调用,但这种编码方式存在一些问题:

  • 存在硬编码,代码可读性差
  • 参数复杂时,URL难以维护

为了解决以上问题,推荐使用Feign来代替RestTemplate,更加优雅地实现http请求的发送。

Feign是一个声明式的http客户端,官网地址:https://github.com/OpenFeign/feign

7.1 使用Feign替代RestTemplate

  • 1)引入Feign依赖

在order-service工程的pom.xml文件中引入Feign依赖:

xml 复制代码
<!--sc_demo\order-service\pom.xml-->

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 2)添加@EnableFeignClients注解

在order-service工程的启动类OrderApplication中添加@EnableFeignClients注解,开启Feign功能:

java 复制代码
// com.star.order.ShopApplication

@EnableFeignClients
@MapperScan("com.star.order.mapper")
@SpringBootApplication
public class OrderApplication {
    ...
}
  • 3)编写并使用Feign客户端

在order-service中新建一个接口,标注@FeignClient注解,表示该接口是一个Feign客户端,其完整内容如下:

java 复制代码
// com.star.shop.feign.UserClient

@FeignClient("user-service")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable Long id);
}

Feign客户端主要是基于SpringMVC的注解来声明远程调用的信息,如上例中:

  • 服务名称:user-service,从Nacos中获取具体的ip和端口
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 返回值类型:User

然后修改OrderService类的queryOrderById()方法,使用Feign客户端来发起http请求:

java 复制代码
// com.star.order.service.OrderService

@Autowired
private UserClient userClient;

public Order queryOrderById(Long orderId) {
    // 1.查询订单
    Order order = orderMapper.findById(orderId);
    
    // 2.远程查询用户信息
    // String url = "http://127.0.0.1:8081/user/" + order.getUserId();
    // 2.1 使用服务名代替ip和端口
    // String url = "http://user-service/user/" + order.getUserId();
    // User user = restTemplate.getForObject(url, User.class);

    // 2.2 使用Feign客户端来发起http请求
    User user = userClient.findById(order.getUserId());

    order.setUser(user);
    // 3.返回
    return order;
}
  • 4)功能测试

重启order-service(8080)服务,调用http://127.0.0.1:8080/order/101接口:

可见,用户信息被成功查询,Feign客户端正常工作。

7.2 自定义配置

Feign支持很多自定义配置,如下表所示:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign. Contract 支持的注解格式 默认是SpringMVC的注解
feign. Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

一般情况下,默认值就能满足使用。如果需要自定义,只需要通过配置文件继续配置或创建自定义的@Bean覆盖默认Bean即可。

下面以修改日志级别为例来说明如何使用自定义配置:

7.2.1 配置文件方式

基于配置文件修改Feign日志级别可以针对单个服务,也可以针对所有服务::

yaml 复制代码
# sc_demo\order-service\src\main\resources\application.yml

feign:
  client:
    config:
      user-service: # 针对单个微服务的配置,填写 服务名
      # default: # 针对所有微服务的配置,填写 default
        loggerLevel: FULL #  日志级别

Feign日志级别分为四种:

  • NONE:不记录任何日志信息,这是默认值
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间。
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息。
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

重启order-service(8080)服务,调用http://127.0.0.1:8080/order/101接口,控制台打印Feign调用http请求的信息:

7.2.2 Java代码方式

先注释掉上面配置文件中的Feign日志级别配置,然后在order-service工程中新建一个配置类:

java 复制代码
// com.star.order.feign.DefaultFeignConfiguration

@Configuration
public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level feignLogLevel() {
        // 日志级别为BASIC
        return Logger.Level.BASIC;
    }
}

如果要全局生效 ,则将该类配置到启动类的@EnableFeignClients注解中:

java 复制代码
// com.star.order.OrderApplication

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
@MapperScan("com.star.order.mapper")
@SpringBootApplication
public class OrderApplication {
    ...
}

如果要局部生效 ,则将该类配置到对应Feign客户端的@FeignClient注解中:

java 复制代码
// com.star.order.feign.UserClient

@FeignClient(value = "user-service", configuration = DefaultFeignConfiguration.class)
public interface UserClient {
    ...
}

重启order-service(8080)服务,调用http://127.0.0.1:8080/order/101接口,控制台打印Feign调用http请求的信息:

可见,日志只记录了请求的方法、URL以及响应状态码和执行时间等,符合BASIC日志级别。

...

本节完,更多内容请查阅分类专栏:SpringCloud学习笔记

本文涉及代码下载地址:https://gitee.com/weidag/springcloud_learning.git

感兴趣的读者还可以查阅我的另外几个专栏:

相关推荐
一元咖啡28 分钟前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
儿时可乖了34 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
ruleslol36 分钟前
java基础概念37:正则表达式2-爬虫
java
xmh-sxh-13141 小时前
jdk各个版本介绍
java
天天扭码1 小时前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺1 小时前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue