微服务二、SpringCloudAlibaba(2021版本)+Nacos+Sentinel流量安全控制+OpenFeign+Gateway以及问题解决

  • Post author:
  • Post category:其他



目录


上一篇:


继续集成-Sentinel 流量安全控制


接入Sentinel 控制台


Sentinel 集成 openfeign遇到的问题


重新贴下整理后的各文件配置


Sentinel + openfeign熔断降级


OpenFeign (服务调用与客户端负载均衡)


测试负载


负载均衡策略


1、轮循


2、随机负载


nacos控制台上下线失效


Gateway(网关)


网关路由


创建子模块cloud-nacos-gateway


pom


bootstrap.yml


网关路由测试


动态路由


项目修改


nacos控制台动态路由配置


Filter


动态路由测试


下一篇:微服务三、SpringCloudAlibaba(2021版本) Seata分布式事务、RocketMQ分布式消息


其他


相关文档


Sentinel持久化配置


sentinel持久化支持的类型


sentinel结合Nacos持久化配置


Nacos持久化



上一篇:


微服务一、SpringCloudAlibaba(2021版本)+Nacos-从零开始、安装、配置、使用和父子项目搭建

本篇内容是接着上一篇的,本篇主要内容:

Sentinel 流量安全控制



OpenFeign 负载均衡



Gateway网关路由和动态路由



sentinel结合Nacos持久化配置、Nacos持久化

继续集成-Sentinel 流量安全控制


github spring-cloud-alibaba Sentinel文档

Alibaba Sentinel 是一款高性能且轻量级的流量控制、熔断降级解决方案。是面向分布式服务架构的高可用流量控制组件。分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

接入Sentinel 控制台


官网文档



release 页面

下载最新版本的控制台 jar 包。

启动命令如下,本文使用的是目前最新 1.8.1 版本:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar

访问:

http://localhost:8080/

输入默认账号密码sentinel


两个子工程pom增加

        <!-- spring cloud alibaba sentinel 依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>


两个子工程yml增加

spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

# feign 开启 sentinel 支持
feign:
  sentinel:
    enabled: true

sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包。

即:访问一次客户端,Sentinel 即可完成客户端初始化操作

老规矩,启动项目,多访问几次:

http://localhost:8081/test


定义资源

资源 是 Sentinel 中的核心概念之一。可以是任何东西,服务,服务里的方法,甚至是一段代码。最常用的资源是我们代码中的 Java 方法。Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

注解(注解方式埋点不支持 private 方法)

文档

Sentinel 集成 openfeign遇到的问题

该版本的说明实在太少,最终也没在网上找到对应的问题和解决方法,倔强的我也不愿意自降版本,所以,断断续续的尝试了好一段时间,这里记录下我最终的解决方案

项目启动找不到

@FeignClient

注解标注的类

因为开启feign对sentinel的支持 feign.sentinel.enabled: true 项目启动报错,找不到

@FeignClient

注解标注的类,最后在启动类@EnableFeignClients注解加上扫描制定包@EnableFeignClients(basePackages = “com.demo.rpc.*”),启动成功但是又出现了下面得错误

远程调用不生效一直进入fallback

可能版本得原因吧最后删掉了上方配置和feign.sentinel.enabled: true,远程调用生效,但是这时又发现了以下问题

生产者关闭fallback不生效

最后查看feign的属性时发现了circuitbreaker,描述是:If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.   (如果为true,OpenFeign客户端将被一个Spring云断路器包装。)加上后成功解决!

feign:
  circuitbreaker:
    enabled: true

后面继续。

重新贴下整理后的各文件配置


父pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>cloud-nacos-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-nacos-demo</name>
    <description>Demo project for Spring Cloud Nacos</description>
    <packaging>pom</packaging>

    <modules>
        <!--
        <module>cloud-nacos-common-entity</module>
        <module>cloud-nacos-common-redis</module>
        <module>cloud-nacos-common-utils</module>
        -->
        <module>cloud-nacos-console-center</module>
        <module>cloud-nacos-customer-center</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.4.7</spring-boot.version>
        <spring-cloud.version>2020.0.0</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- spring boot 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--  SpringCloud 微服务 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- SpringCloud Alibaba 微服务 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- bootstrap 启动器 使bootstrap配置文件生效 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
    </dependencies>
</project>


子pom依赖


        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- SpringCloud Alibaba Nacos 注册中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

        <!-- 远程调用 SpringCloud Openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

        <!-- SpringCloud Alibaba Sentinel 熔断降级,外部降级 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>


yml

删除了application.yml使用bootstrap.yml文件

# Tomcat
server:
  port: 8081

# Spring
spring:
  application:
    name: customer-center
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        group: DEV_GROUP
        #namespace: xxx-xxx-xxx
        #service: ${spring.application.name}
        #register-enabled: true # 是否开启注册服务(默认true)
      config:
        # 配置中心地址
        server-addr: localhost:8848
        prefix: config  # 默认为 spring.application.name 的值
        # 配置文件格式
        file-extension: yml
        #namespace: xxx-xxx-xxx
        #refresh-enabled: true # 是否开启动态刷新(默认true)
        # 共享配置
        # shared-configs:
        # - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
    sentinel:
      # 取消控制台懒加载
      eager: true
      transport:
        port: 8719  # 在应用对应的机器上启动一个 Http Server,与 Sentinel 控制台做交互
        dashboard: localhost:8080 # 控制台地址


feign:
  circuitbreaker:
    # If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
    enabled: true


启动类

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients // 开启feign远程调用
@EnableDiscoveryClient // 开启nacos
public class CloudNacosCustomerCenterApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudNacosCustomerCenterApplication.class, args);
    }
}

Sentinel + openfeign熔断降级


文档


console-center


TestController

package com.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {

        return "我是来自console-center得数据";
    }
}

修改

customer-center


UserController

package com.demo.controller;

import com.demo.config.ConfigProperties;
import com.demo.rpc.ConsoleApiService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class UserController {

    @Resource
    ConfigProperties properties;

    @Resource
    private ConsoleApiService consoleApiService;

    @GetMapping("/user")
    public String getUser() {
        return "customer-center发起请求,请求内容:" + consoleApiService.test();
    }
}


ConsoleApiService

使用FallbackFactory 可以记录异常

package com.demo.rpc;

import com.demo.rpc.impl.ConsoleApiServiceFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

// value 代表要调用的服务
// 设置configuration属性为  feign的配置 比如日志记录
// fallback 消费端 降级方案
@FeignClient(value = "console-center", fallbackFactory = ConsoleApiServiceFallbackFactory.class)
public interface ConsoleApiService {

    @GetMapping("/test")
    String test();
}


ConsoleApiServiceFallbackFactory

服务熔断降级处理,可返回托底数据,

package com.demo.rpc.impl;

import com.demo.rpc.ConsoleApiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ConsoleApiServiceFallbackFactory implements FallbackFactory<ConsoleApiService> {

    @Override
    public ConsoleApiService create(Throwable cause) {
        return new ConsoleApiService() {
            @Override
            public String test() {
                log.error("test fail :remote call console-center test method fail, 原因:");
                cause.printStackTrace();
                return null;
            }
        };
    }
}

目录


启动项目,多请求几次


http://localhost:8081/user

添加

流控

规则,(默认效果

快速失败

)定义资源访问的 QPS 为 1(每秒能处理查询数目)。

请求远程服务调用

http://localhost:8081/user

,对比单次和快速刷新效果

请求

http://localhost:8082/test

,对比单次和快速刷新效果

OpenFeign (服务调用与客户端负载均衡)

开发微服务,免不了需要服务间调用,服务间调用和调用失败的熔断降级在上文已经配置过了,这里实践下客户端的负载均衡

创建子工程

cloud-nacos-console-center2

(上文已有,此处略过)



cloud-nacos-console-center的内容复制到cloud-nacos-console-center2


修改TestController

(为了区分)

package com.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {
        // int a = 1 / 0;

        return "我是来自console-center2得数据";
    }
}


修改bootstrap.yml

文件

server.port: 8083

启动项目

测试负载

多次请求

http://localhost:8081/user

console-center2和console-center轮询调用,几率平均!

负载均衡策略

1、轮循


ReactiveLoadBalancer

默认使用的实现是

RoundRobinLoadBalancer

轮循,上文配置的测试的负载方式就是轮询

打开nacos管理后台,选择服务实例编辑权重,取值在0~1之间,值越大代表实例被调用的几率越大,权重改为0不会调用

console-center2的权重更改

实测发现除了改为0,其他的任何值都默认为轮询,查了下资料说是spring cloud-ribbon默认是没有权重的轮询算法的. 另外这个nacos的权重属性不一定在springcloud 有用到, 可以使用元信息配置的权重+自定义ribbon策略

2、随机负载



官方文档

官方切换成


随机负载


均衡示例

package com.demo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

@Slf4j
public class NacosWeightRule {

    /**
     * 切换为随机负载均衡(官方写法)
     */
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}

作为@LoadBalancerClient或@LoadBalancerClient配置参数传递的类,不应使用@configuration进行注释,也不应在组件扫描范围之外,启动类添加

@LoadBalancerClients(defaultConfiguration = {NacosWeightRule.class})

再次测试,两个服务调用概率确实改为了随机

nacos控制台上下线失效

而上下线功能也是,虽然

nacos

实现了秒级的实例上下线,但是由于在

Spring Cloud

中,负载组件

rabbion

的实例信息更新是采用了定时任务的形式,有可能这个任务上一秒刚刚执行完,下一秒你就执行实例上下线操作,那么

rabbion

要感知这个变化,就必须要等待

refreshIntervalMs

秒后才可以感知到。



解决:


默认

ttl

设置为 35 秒, 可以设置spring.cloud.loadbalancer.cache.enabled: false禁用缓存或者更改

ttl

的值

spring:
  application:
    name: customer-center
  profiles:
    active: dev
  cloud:
    loadbalancer:
      cache:
#        enabled: false
        ttl: 2s

Gateway(网关)


文档


API网关的作用

  • API网关可以把后端的多个服务进行整合,提供唯一的业务接口(请求转发),客户端只需要调用这个接口即可完成数据获取与展示。
  • 针对所有的请求进行统一鉴权,限流,熔断,日志。
  • 协议转化。针对后端不同的协议,在网关层统一处理后以http对外提供服务。
  • 统一错误码处理
  • 请求转发,并且可以基于网关实现内、外网隔离。

网关路由


创建子模块cloud-nacos-gateway

经历了一天的各种问题后终于尝试成功了(如何创建子模块可参考前面的创建父子项目)

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.demo</groupId>
        <artifactId>cloud-nacos-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>com.demo</groupId>
    <artifactId>cloud-nacos-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-nacos-gateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- SpringCloud Alibaba Nacos 注册中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

        <!-- SpringCloud Alibaba Sentinel 熔断降级,外部降级 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>


        <!-- 远程调用 SpringCloud Openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

        <!-- SpringCloud Alibaba Sentinel Gateway 网关 和 Sentinel Starter 配合使用  -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <!-- SpringCloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!-- 单独使用 -->
        <!-- sentinel gateway adapter 依赖 -->
        <!--<dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>-->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

bootstrap.yml

# Tomcat
server:
  port: 10000
  
# Spring
spring:
  application:
    name: gateway-center
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        group: DEV_GROUP
      config:
        # 配置中心地址
        server-addr: localhost:8848
        prefix: config  # 默认为 spring.application.name 的值
        # 配置文件格式
        file-extension: yml
    # 网关配置
    gateway:
      discovery:
        locator:
          # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        - id: customer-center # 路由 ID,唯一 没有固定规则,建议配合服务名
          # 支持普通url和lb://注册中心服务名称
          uri: lb://customer-center
          predicates:  # 路由条件,根据匹配结果进行路由
            - Path=/customer/**  # 路径相匹配的进行路由,会添加到uri后
            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
            #- Cookie=username,yys
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
          filters:
            # 自动去掉一节路径, 转发到uri后自动去掉customer, 1代表截取路径的个数
            - StripPrefix=1
        - id: console-center
          uri: lb://console-center
          predicates:
            - Path=/console/**
          filters:
            - StripPrefix=1


启动类注解

@SpringBootApplication
@EnableDiscoveryClient

启动项目

如报错:Consider defining a bean of type ‘org.springframework.http.codec.ServerCodecConfigurer’ in your configuration.


要排除依赖spring-boot-starter-web,因为会与spring cloud gateway的webflux冲突。

网关路由测试

访问

http://localhost:10000/customer/user

访问

http://localhost:10000/console/test

动态路由

Spring Cloud Gateway本身不支持直接从Nacos动态加载路由配置表,需要自己编写监听器监听配置变化并刷新路由表。

项目修改

package com.demo.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
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.stereotype.Component;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {

    //设置路由配置的Nacos dataid
    private String dataId = "gateway.properties";
    //设置路由配置的nacos group
    @Value("${spring.cloud.nacos.config.group:DEFAULT_GROUP}")
    private String group;
    //Nacos 配置中心地址
    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> ROUTE_LIST = new ArrayList<>();


    @PostConstruct //服务器加载Servlet的时候运行,并且只会被服务器执行一次
    public void dynamicRouteByNacosListener() {
        try {
            //创建nacos服务
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            //根据条件获取nacos配置
            configService.getConfig(dataId, group, 5000);
            //为nacos配置添加监听
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    //清空路由信息
                    clearRoute();
                    try {
                        //路由信息JSON转为RouteDefinition对象
                        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
                        //添加路由信息
                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                            addRoute(routeDefinition);
                        }
                        //推送路由信息
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

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


删除路由信息

# Tomcat
server:
  port: 10000

# Spring
spring:
  application:
    name: gateway-center
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        group: DEV_GROUP
      config:
        # 配置中心地址
        server-addr: localhost:8848
        prefix: config  # 默认为 spring.application.name 的值
        # 配置文件格式
        file-extension: yml
    # 网关配置
    gateway:
      discovery:
        locator:
          # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
#        - id: customer-center # 路由 ID,唯一 没有固定规则,建议配合服务名
#          # 支持普通url和lb://注册中心服务名称
#          uri: lb://customer-center
#          predicates:  # 路由条件,根据匹配结果进行路由
#            - Path=/customer/**  # 路径相匹配的进行路由,会添加到uri后
#            #- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#            #- Cookie=username,yys
#            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
#          filters:
#            # 自动去掉一节路径, 转发到uri后自动去掉customer
#            - StripPrefix=1
#        - id: console-center
#          uri: lb://console-center
#          predicates:
#            - Path=/console/**
#          filters:
#            - StripPrefix=1

nacos控制台动态路由配置

[
    {
        "id":"customer-center",
        "order": 0,
        "uri":"lb://customer-center",
        "predicates":[
            {
                "name":"Path",
                "args":{
                    "pattern":"/customer/**"
                }
            }
        ],
        "filters":[
            {
                "name":"StripPrefix",
                "args": {
                    "parts": "1"
                }
            }
        ]
    },
    {
        "id":"console-center",
        "order": 0,
        "uri":"lb://console-center",
        "predicates":[
            {
                "name":"Path",
                "args":{
                    "pattern":"/console/**"
                }
            }
        ],
        "filters":[
            {
                "name":"StripPrefix",
                "args": {
                    "parts": "1"
                }
            }
        ]
    }
]

Filter

  • StripPrefix Filter    请求路径截取的功能(截掉前面的)
  • PrefixPath Filter 的作用和 StripPrefix 正相反,是在 URL 路径前面添加一部分的前缀

动态路由测试

nacos配置前访问不到,配置后访问正常

下一篇:微服务三、SpringCloudAlibaba(2021版本) Seata分布式事务、RocketMQ分布式消息


微服务三、SpringCloudAlibaba(2021版本) Seata分布式事务、RocketMQ分布式消息


还有分布式事务、分布式消息等,下一篇再撸吧

其他

相关文档


Sentinel持久化配置

一旦项目重启,Sentinel规则将消失,生产环境需要将配置规则进行持久化


pom

        <!-- Sentinel Datasource Nacos 持久化-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

sentinel持久化支持的类型

com.alibaba.cloud.sentinel.datasource.config.DataSourcePropertiesConfiguration

  • private FileDataSourceProperties

    file

    ;
  • private NacosDataSourceProperties

    nacos

    ;
  • private ZookeeperDataSourceProperties

    zk

    ;
  • private ApolloDataSourceProperties

    apollo

    ;
  • private RedisDataSourceProperties

    redis

    ;
  • private ConsulDataSourceProperties

    consul

    ;


sentinel结合Nacos持久化配置

修改cloud-nacos-customer-center bootstrap.yml,添加

spring:
  cloud:
    sentinel:
      # 取消控制台懒加载
      eager: true
      transport:
        port: 8719  # 在应用对应的机器上启动一个 Http Server,与 Sentinel 控制台做交互
        dashboard: localhost:8080 # 控制台地址
      filter:
        # 关闭官方默认收敛所有context
        # 若在网关流控控制台上看到了 URL 资源,就是此配置项没有置为 false
        enabled: false
      datasource:
#        # 降级规则
        ds1:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: ${spring.application.name}-flow-rules
            groupId: DEFAULT_GROUP
            data-type: json
            # authority(授权规则)、degrade(降级规则)、flow(流控规则)、param(热点规则)、system(系统规则)五种规则持久化到Nacos中。 另外还增加网关的两个(api分组,限流)
            rule-type: flow


nacos添加配置

[
    {
        "resource": "console-center_test",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
  • resource:资源名称
  • limitApp:来源应用
  • grade:阀值类型,0-线程数,1-qps
  • count:单机阀值
  • strategy:流控模式,0-直接,1-关联,2-链路
  • controlBehavior:流控效果,0-快速失败,1-warm up,2-排队等待
  • clusterMode:是否集群


Sentinel持久化配置如果自己写的话,可能会写错,可以用浏览器捕获数据


Sentinel上的流控规则删除,发布nacos上的配置,测试可达到一样的效果


Nacos持久化

默认情况下Nacos会将配置信息存储在内嵌存储器中,但通常我们希望在数据库中存储或管理这些配置信息,那么就需要进行持久化修改,目前Nacos仅支持MYSQL5.6以上数据库。


初始化数据库表


在/userpath/nacos/conf下找到文件nacos-mysql.sql,在数据库执行该文件内容,完成库表初始化工作。


修改Nacos配置


在/userpath/nacos/conf下找到文件application.properties,定位到 Config Module Related Configurations



将原本注释的部分放开并修改为自己的数据库连接信息。

### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123456



版权声明:本文为qq_44695727原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。