SpringCloud —— 配置管理

一、前言

至此,微服务的基本开发我们就学习完了,接下来学习的是为了简便维护成本和保障服务安全的技术了,这里首先要讲的就是配置管理,配置管理是通过Nacos来实现的,对复用率高的配置进行统一管理共享,所以在微服务的配置文件中就只需要写参数和部分特殊配置了,简便了开发,并且方便了后期的维护。同时,Nacos的配置管理具有热部署的功能,也就是无需重启服务即可更改配置,所以对于服务的日常维护是有很大好处的。

二、共享配置

刚刚提到了共享配置,其实就是说一份配置多个微服务共同使用,比如像数据库和日志的配置,配置的结构完全相同,仅仅是少部分参数略有不同,这个时候就可以直接共享配置了。

比如购物车微服务,先前的配置如下:

bash 复制代码
server:
  port: 8082
spring:
  application:
    name: cart-service
  profiles:
    active: dev
  datasource:
    url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"
knife4j:
  enable: true
  openapi:
    title: 商品服务接口文档
    description: "信息"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.hmall.cart.controller

可以看到是很冗长的,接下来我们拆分配置,将可以值得共享的配置拿出来,创建为一个共享配置:(在Nacos中操作)

1.数据库

bash 复制代码
spring:
  datasource:
    url: jdbc:mysql://${hm.db.host:192.168.242.130}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${hm.db.un:root}
    password: ${hm.db.pw:123}

mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto

2.日志

bash 复制代码
logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"

3.文档

bash 复制代码
knife4j:
  enable: true
  openapi:
    title: ${hm.swagger.title:黑马商城接口文档}
    description: "${hm.swagger.desc:黑马商城接口文档}"
    email: zhanghuyi@itcast.cn
    concat: 印东升
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - ${hm.swagger.package}

最终在nacos中就会变成这样:

4.微服务配置

接下来我们就只需要在微服务的配置对应的参数即可,注意,下面的配置是在bootstrap.yaml中配置的,其中在对nacos的配置中注明了共享配置的id,以便于作为模板使用,这里我们就需要配置刚刚上面三个共享配置的id了(这个id是在nacos中自定义的)。

bash 复制代码
server:
  port: 8082

feign:
  okhttp:
    enabled: true # 开启OKHttp功能

hm:
  db:
    database: hm-cart
  swagger:
    title: "黑马商城购物车服务接口文档"
    package: com.hmall.cart.controller
bash 复制代码
spring:
  application:
    name: cart-service
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.242.130:8848
      config:
        file-extension: yaml
        shared-configs:
          - data-id: shared-jdbc.yaml
          - data-id: shared-log.yaml
          - data-id: shared-swagger.yaml

三、配置热更新

我们通过一个例子来演示,首先我们将以往写死的购物车数量限制更改,改成以配置的形式引入。

java 复制代码
 private void checkCartsFull(Long userId) {
        int count = lambdaQuery().eq(Cart::getUserId, userId).count();
        if (count >= cartProperties.getMaxItems()) {
            throw new BizIllegalException(StrUtil.format("用户购物车数量不能超过{}", cartProperties.getMaxItems()));
        }
    }

自然先得创建一个配置类,用于导入配置文件的配置参数,注意了,这个配置我们是交给nacos的(Nacos 中 Data ID 默认是服务名.yml),所以这里我们要注意按照规范定义共享配置的配置名,这个配置名决定了nacos中的配置应用在哪个微服务中(将配置绑定到对应微服务专属的Spring IoC容器中),比如这里是cart-service,那么其他微服务就不会识别到这个配置了。

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {
    private Integer maxItems;
}

这个时候,我们就可以直接在nacos中更改配置参数了,可以发现,即使微服务不重启,配置的参数也会更改,这就是热更新。

四、动态路由

路由是通过网关来转发的,所以这里的所有配置,都是网关的,首先先用共享配置配置网关,这里只需要一个日志配置就行了,同时,路由配置我们就不要写死了,这里面都是写死的配置,所以我们全部删除了:

bash 复制代码
spring:
  application:
    name: gateway
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.242.130:8848
      config:
        file-extension: yaml
        shared-configs:
          - data-id: shared-log.yaml
bash 复制代码
server:
  port: 8080
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.242.130
hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30d
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi

这个就是动态路由加载器 ,它的原理是用NacosConfigManager 获取nacos中的配置然后更新路由表,注意,这里我们必须使用JSON格式来配置路由 ,因为之前配置文件中的路由底层其实是交给一个类来封装的,也就是说,每一个微服务的路由都是一个类的对象,而这个封装的类就是RouteDefinition所以我们最后交给监听器的是RouteDefinition对象的集合,其中的每个对象是由nacos中的JSON配置转化过来的。

而监听器监听的是什么呢? 其实就是nacos控制台中配置的更改,只要配置一改变,监听器就会执行**updateConfigInfo()**方法,从而更新路由表。

java 复制代码
@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicRouteLoader {
    private final NacosConfigManager nacosConfigManager;

    private final RouteDefinitionWriter writer;

    private final String dataId = "gateway-routes.json";

    private final String group = "DEFAULT_GROUP";

    private final Set<String> routeIds = new HashSet<>();

    @PostConstruct//在Bean初始化后执行
    public void initRouteConfigListener() throws NacosException {
        //1.项目启动时,先拉起一次配置,并且添加配置监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //2.监听到配置变更,需要去更新路由表
                        updateConfigInfo(configInfo);
                    }
                });

        //3.第一次读取到配置,也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo) {
        log.debug("监听到路由配置信息:{}",configInfo);
        //1.解析配置文件,转换为RouteDefinition
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2.删除旧的路由表
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
        //3.更新路由表
        for (RouteDefinition routeDefinition : routeDefinitions) {
            //3.1更新路由表
            writer.save(Mono.just(routeDefinition)).subscribe();
            //3.2记录路由id,便于下一次更新时删除
            routeIds.add(routeDefinition.getId());
        }

    }

}
bash 复制代码
[
    {
        "id": "item",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
        }],
        "filters": [],
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/carts/**"}
        }],
        "filters": [],
        "uri": "lb://cart-service"
    },
    {
        "id": "user",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}
        }],
        "filters": [],
        "uri": "lb://user-service"
    },
    {
        "id": "trade",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/orders/**"}
        }],
        "filters": [],
        "uri": "lb://trade-service"
    },
    {
        "id": "pay",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/pay-orders/**"}
        }],
        "filters": [],
        "uri": "lb://pay-service"
    }
]
相关推荐
乂爻yiyao8 小时前
Java 的云原生困局与破局
java·开发语言·云原生
C182981825758 小时前
traceId 传递-MQ
java
小鸡脚来咯8 小时前
java web后端开发流程
java·开发语言·git
北友舰长8 小时前
基于Springboot+thymeleaf快递管理系统的设计与实现【Java毕业设计·安装调试·代码讲解】
java·spring boot·mysql·校园管理·快递·快递系统
我爱烤冷面8 小时前
kotlin项目实现Java doc的方案:使用Dokka
java·开发语言·kotlin·dokka
jian110588 小时前
android java转kotlin,kotlin转java
android·java·kotlin
长征coder8 小时前
SpringCloud服务优雅下线LoadBalancer 缓存配置方案
java·后端·spring
历程里程碑8 小时前
C++ 4:内存管理
java·c语言·开发语言·数据结构·c++·笔记·算法
没有bug.的程序员8 小时前
微服务的本质:不是拆服务,而是拆复杂度
java·jvm·spring·微服务·云原生·容器·架构