【SpringCloud Alibaba】Nacos Config配置管理与Gateway 网关

发布时间:2024年01月11日

目录

一、Config 远程配置

1.1 config 介绍

1.2?bootstrap.yml 配置文件

二、Gateway 网关

2.1 gateway 介绍

2.2 gateway 使用

2.2.1 方式一

2.2.2 方式二(动态路由)


一、Config 远程配置

1.1 config 介绍

????????微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大 量的服务。 由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的。

????????Spring Cloud 提供了 ConfigServer来解决这个问题,Spring Cloud Config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服 务应用的所有环境提供了一个中心化的外部配置。

一个使用微服务架构的应用系统可能会包括成百上千个微服务,配置各部相同,需求各不相同:

  • 不同环境不同配置:例如数据源在不同的环境(开发,测试,生产)是不同的
  • 运行期间可以动态调整。例如根据各个微服务的负载状况,动态调整数据源连接池大小或者熔断阀 值,并且调整时不停止微服务(配置修改后可以自动更新)

????????使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外 部属性配置。?

1.2?bootstrap.yml 配置文件

SpringBoot 默认支持propertiesYAML两种格式的配置文件。

  1. bootstrap.yml(bootstrap.properties)用来程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等
  2. application.yml(application.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用 的公共参数等。

bootstrap.yml 先于 application.yml 加载 !!!

1、添加依赖

        <!--config配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

2、添加bootsrap.yml配置

server:
  port: 8080
spring:
  application:
    name: basketball
  cloud:
    nacos:
      server-addr: localhost:8848
      config:
        server-addr: ${spring.cloud.nacos.server-addr}
        #指定nacos配置中心地址
        prefix: ${spring.application.name}
        file-extension: yaml # 使用的 nacos 配置集的 dataId 的文件拓展名,同时也是Nacos 配置集的配置格式,默认为 properties
        group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
        # 共享配置集数组
        shared-configs:
          - data-id: redis.yml
            group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
            refresh: true # 是否自动刷新配置,默认为 fals
          - data-id: basketball.yml
            group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
            refresh: true # 是否自动刷新配置,默认为 false
        # 使用的 nacos 的命名空间(指定项目环境),
        namespace: 363830b6-d72e-4868-a028-6be9c2cfd267

3、对应Nacos也添加配置信息

redis.yml:

basketball.yml:

4、编写两个配置类读取Nacos配置文件属性值

5、编写测试接口类

package com.example.basketball.controller;

import com.example.basketball.config.EmailProperies;
import com.example.basketball.config.RedisProperies;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import pojo.dto.IkunDto;

import java.util.Map;

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

    @Autowired
    private EmailProperies emailProperies;
    @Autowired
    private RedisProperies redisProperies;

    /*编写测试接口读取远程配置信息*/
    @RequestMapping("/test1")
    public Object test01() {
        return emailProperies;
    }

    @RequestMapping("/test2")
    public Object test02() {
        return redisProperies;
    }

}

6、启动类添加刷新配置注解

7、测试

? ? ? ? 这样我们就能将一些基本配置信息添加到Nacot进行远程配置管理,其实这里还有一个缺点:如果我们要个更换环境需要修改指定的nacos命名空间在这我们其实可以不写,把项目导成jar包,直接设置指定命名空间运行项目:

cmd指令示例:

java -jar xxx.jar --spring.cloud.nacos.config.namespace=xxx

二、Gateway 网关

2.1 gateway 介绍

????????Spring Cloud Gateway是Spring官方基于Spring5.0、SpringBoot2.0和Project Reactor等技术开发的网关,旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。

????????Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代Netflix的Zuul,其不仅提供统 一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等等。 它是基于Netty的响应式开发模式。

1??路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL、一组断言predicates)工厂和一 组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

2??断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配 来自http Request中的任何信息,比如请求头和参数等。

3??过滤器(Filter):一个标准的Spring WebFilter,Spring Cloud Gateway中的Filter分为两种类型: Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理

2.2 gateway 使用

创建网关服务:

添加依赖?:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--阿里json解析-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.35</version>
        </dependency>
    </dependencies>

2.2.1 方式一

本地yml配置

server:
  port: 8082
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      routes:
        #路由标识(id:标识,具有唯一性)配合读取类使用
        - id: user-basketball-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb://ikun
          #优先级,越小越优先
          order: 999
          #路由条件(predicates:断言)
          predicates:
            # 路径匹配,
            - Path=/kun/**
          filters:
            #路径前缀删除示例
            - StripPrefix=1

????????这样同时启动gateway和ikun(消费者)服务,通过网关端口8082路径kun就能访问ikun服务的接口方法。但是这样还是不太方便,建议使用方法二动态路由远程配置👇

2.2.2 方式二(动态路由)

如果是跳到该目录的小帅和小美请看上面的服务创建与依赖配置哦!?

1、修改yml配置 :

server:
  port: 8082
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
#自定义读取配置
gateway:
  nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    #namespace: xxx-xx-xx-xx (指定环境命名空降如[dev,test,pro...])
    data-id: dynamic-routing.json
    group: DEFAULT_GROUP

2、添加Nacos远程配置信息

{
  "refreshGatewayRoute": true,
  "routeList": [
    {
      "id": "basketball-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/ball/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://basketball",
      "order": 0
    },
    {
      "id": "ikun-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/kun/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://ikun",
      "order": 0
    }
  ]
}

?

3、添加相关远程配置解析类

GatewayNacosProperties:
package com.example.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ConfigurationProperties(prefix = "gateway.nacos")
@Component
public class GatewayNacosProperties {

    private String serverAddr; //nacos服务地址
    private String dataId; //配置文件名称
    private String namespace; //指定命名空间
    private String group; //分组

}
RouteEntity:
package com.example.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 云村小威
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class RouteEntity {

    //路由id
    private String id;
    //路由断言集合
    private List<PredicateEntity> predicates = new ArrayList<>();
    //路由过滤器集合
    private List<FilterEntity> filters = new ArrayList<>();
    //路由转发的目标uri
    private String uri;
    //路由执行的顺序
    private int order = 0;

}
PredicateEntity:
package com.example.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 云村小威
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class PredicateEntity {

    //断言对应的Name
    private String name;

    //断言规则
    private Map<String, String> args = new LinkedHashMap<>();

}
FilterEntity:
package com.example.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 云村小威
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class FilterEntity {

    //过滤器对应的Name
    private String name;

    //路由规则
    private Map<String, String> args = new LinkedHashMap<>();

}

?

DynamicRoutingConfig :?🌟

package com.example.gateway;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.example.gateway.pojo.FilterEntity;
import com.example.gateway.pojo.GatewayNacosProperties;
import com.example.gateway.pojo.PredicateEntity;
import com.example.gateway.pojo.RouteEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;


/**
 * 此类实现了Spring Cloud Gateway + nacos 的动态路由,
 * 它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
 */
@SuppressWarnings("all")
@Slf4j
@Component
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    @Autowired
    private GatewayNacosProperties gatewayProperties;

    @Autowired
    private ObjectMapper mapper;

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,
     * 再使用ConfigService开启一个监听,
     * 并且在监听的方法中刷新路由信息。
     */
    @Bean
    public void refreshRouting() throws NacosException {
        //创建Properties配置类
        Properties properties = new Properties();
        System.out.println(gatewayProperties);
        //设置nacos的服务器地址,从配置类GatewayProperties中获取
        properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr());
        //设置nacos的命名空间,表示从具体的命名空间中获取配置信息,不填代表默认从public获得
        if (gatewayProperties.getNamespace() != null) {
            properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace());
        }
        //根据Properties配置创建ConfigService类
        ConfigService configService = NacosFactory.createConfigService(properties);
        //获得nacos中已有的路由配置
        String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000);
        this.parseJson(json);

        //添加监听器,监听nacos中的数据修改事件
        configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            /**
             * 用于接收远端nacos中数据修改后的回调方法
             */
            @Override
            public void receiveConfigInfo(String configInfo) {
                log.warn(configInfo);
                //获取nacos中修改的数据并进行转换
                parseJson(configInfo);
            }
        });

    }

    /**
     * 解析从nacos读取的路由配置信息(json格式)
     */
    public void parseJson(String json) {
        log.warn("从Nacos返回的路由配置(JSON格式):" + json);
        boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
        if (refreshGatewayRoute) {
            List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class);
            for (RouteEntity route : list) {
                update(assembleRouteDefinition(route));
            }
        } else {
            log.warn("路由未发生变更");
        }
    }


    /**
     * 路由更新
     */
    public void update(RouteDefinition routeDefinition) {

        try {
            this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
            log.warn("路由删除成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.warn("路由更新成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

    }

    /**
     * 路由定义
     */
    public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {

        RouteDefinition definition = new RouteDefinition();

        // ID
        definition.setId(routeEntity.getId());

        // Predicates
        List<PredicateDefinition> pdList = new ArrayList<>();
        for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(predicateEntity.getArgs());
            predicateDefinition.setName(predicateEntity.getName());
            pdList.add(predicateDefinition);
        }
        definition.setPredicates(pdList);

        // Filters
        List<FilterDefinition> fdList = new ArrayList<>();
        for (FilterEntity filterEntity : routeEntity.getFilters()) {
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setArgs(filterEntity.getArgs());
            filterDefinition.setName(filterEntity.getName());
            fdList.add(filterDefinition);
        }
        definition.setFilters(fdList);

        // URI
        URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
        definition.setUri(uri);

        return definition;
    }

}

?

4、接口调用测试

????????这样就可以通过配置统一管理服务接口, 如下图所示;通过网关端口8082可访问不同服务的接口实现路由转发、处理请求和响应。

?

?

文章来源:https://blog.csdn.net/Justw320/article/details/135442845
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。