美文网首页
十四、网关Gateway整合Sentinel持久化到nacos

十四、网关Gateway整合Sentinel持久化到nacos

作者: 轻轻敲醒沉睡的心灵 | 来源:发表于2023-06-15 15:33 被阅读0次

前面已经讲了:微服务整合Sentinel持久化到nacos
这里我们说下网关整合sentinel,网关用的gateway。sentinel官网这里也介绍了网关流控的一些内容。
在前面讲微服务整合sentinel持久化到nacos时,我们把测试代码中sentinel和nacos交互的类拷贝到了rule模块中。
在网关gateway中也需要添加类似的代码。
首先我们看一下官网的介绍:

资源维度
什么意思?
  1. 路由维度:也就是我们在gateway网关中配置的路由,如果触发sentinel能够识别到,并支持添加规则。


    gateway路由配置

    这个路由配置可以在sentinel中发现。


    sentinel-gateway
    路由链路
  2. 自定义api维度:我们可以像官方给出的那样,提前自定义api组,也可以被sentinel识别,然后添加规则。我们更多的会用这个,但是我们一般不会提前写在代码中,而是在web页面自定义api组。官网例子:


    api组

说这么多,主要是想告诉大家,gateway整合sentinel的时候,不像在微服务中那样(在微服务中,sentinel可以直接识别到url粒度的资源,然后在这些url资源上添加规则,nacos只需要处理规则),有2个地方需要自己改动:

    1. 自定义的api资源组,需要和nacos交互
    1. 针对以上资源做的规则(也包括路由维度的资源),需要和nacos交互

1. sentinel-board后台源码修改

注意:这次修改,是在上次微服务整合sentinel修改完成之后的,所以是包含上次的修改的。
参考上次sentinel修改的代码:微服务整合Sentinel持久化到nacos

1.1 添加一些常量,用于在nacos中规则名字的后缀、组别:

// 添加 gateway 后缀
public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api-group";
public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";

上次移过来的nacos的类中:com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil

常量

1.2 nacos的连接中添加bean

上次可以看出,nacos中保存的是json字符串,是由一些entity直接转的。上次移过来的,里面只处理了微服务规则实体和json字符串互转。我们现在要添加的是gateway的api组实体、流控规则实体和json互转。

    /** 新增gateway使用的entity和json串互转  **/
    @Bean
    public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
    }

    @Bean
    public Converter<List<ApiDefinitionEntity>, String> gatewayApiEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<ApiDefinitionEntity>> gatewayApiEntityDecoder() {
        return s -> JSON.parseArray(s, ApiDefinitionEntity.class);
    }
json串转换

1.3 添加规则和api组对接nacos的实现类

这个就是参考上次的微服务对接nacos的类写的,entity换一下就行,4个,com.alibaba.csp.sentinel.dashboard.rule.gateway包下:

新加对接nacos的类
GatewayApiNacosProvider.png
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;

@Component("gatewayApiNacosProvider")
public class GatewayApiNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {
    
    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<ApiDefinitionEntity>> converter;

    @Override
    public List<ApiDefinitionEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX, 
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }

}
GatewayApiNacosPublisher.png
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;

@Component("gatewayApiNacosPublisher")
public class GatewayApiNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<ApiDefinitionEntity>, String> converter;

    @Override
    public void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app+ NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }

}
GatewayFlowRuleNacosProvider.png
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;

@Component("gatewayFlowRuleNacosProvider")
public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {
    
    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<String, List<GatewayFlowRuleEntity>> converter;

    @Override
    public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {
        String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }

}
GatewayFlowRuleNacosPublisher.png
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;

@Component("gatewayFlowRuleNacosPublisher")
public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {
    
    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<List<GatewayFlowRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }

}

GatewayApiNacosProvider:从nacos中读取已保存的api分组
GatewayApiNacosPublisher:保存gateway中api分组到nacos
GatewayFlowRuleNacosProvider:读取nacos中保存的流控规则的
GatewayFlowRuleNacosPublisher:保存gateway中流控规则到nacos

1.4 修改conrtoller,调用上一步添加的实现类

要修改的内容在com.alibaba.csp.sentinel.dashboard.controller.gateway包下面,2个controller:

controller
1.4.1 GatewayApiController
    1. 注入我们自己的实现类


      注入实现类
    1. 保存的时候有一个中间的小方法,我们重写一下


      重写小保存方法
    // 重写保存规则的方法,通过新加的实现类保存到nacos
    private void publishApis(String app) {
        List<ApiDefinitionEntity> apis = repository.findAllByApp(app);
        try {
            rulePublisher.publish(app, apis);
        } catch (Exception e) {
            logger.error("推送apis到nacos异常");
        }
    }
    1. 修改查


      apis查
    1. 修改增


      apis增
    1. 修改删和改 2处
      和增一样,找到地方改


      apis删查
1.4.2 GatewayFlowRuleController
    1. 注入我们自己的实现类


      注入实现类
    1. 重写小方法


      重写小方法
    // 重写保存规则的方法,通过新加的实现类保存到nacos
    private void publishApis(String app) {
        List<GatewayFlowRuleEntity> apis = repository.findAllByApp(app);
        try {
            rulePublisher.publish(app, apis);
        } catch (Exception e) {
            logger.error("推送流控规则到nacos异常");
        }
    }
    1. 修改增删改查


      1-查
      增删改

2. gateway网关服务

2.1 引入jar包

gateway-sentinel依赖
pom.xml文件:
<?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.zrb.test</groupId>
        <artifactId>sentinel-test</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>gateway-sentinel-server</artifactId>
    <version>1.0.0</version>
    <name>gateway-sentinel-server</name>
    <description>gateway网关</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!-- openfein -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- SpringCloud在Hoxton.M2版本之后不再使用Ribbon而是使用spring-cloud-loadbalancer,需要自己引入 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
        
        <!--nacos客户端 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        
        <!--sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        
    </dependencies>
    

    <build>
        <finalName>gateway-sentinel-server</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.2 配置文件

配置文件
application.yml文件:
# 1.server
server:
  port: 8000 
# 2.log
logging:
  level:
    '[org.springframework.cloud.gateway]': info
    '[org.springframework.jdbc.core]': debug
# 3.spring
spring: 
  # 服务名称必须带上,不然nacos服务列表中没有,也不会有注册成功的信息
  application: 
    name: gateway-server
  # 统一设置返回的时间格式
  jackson: 
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    default-property-inclusion: NON_NULL
  cloud: 
    nacos: 
      config: 
        import-check:
          enabled: false
#        server-addr: 192.168.50.89:8848
#        file-extension: yaml
      discovery: 
        server-addr: 192.168.50.89:8848
    sentinel:
       transport:
         dashboard: 192.168.50.89:8080
         port: 8723
       datasource:
        gw-flow:
          nacos:
            server-addr: 192.168.50.89:8848
            namespace: 
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-gateway-flow-rules
            dataType: json
            rule-type: gw-flow
        gw-api-group:
          nacos:
            server-addr: 192.168.50.89:8848
            namespace: 
            groupId: SENTINEL_GROUP
            dataId: ${spring.application.name}-gateway-api-group
            dataType: json
            rule-type: gw-api-group
    # gateway
    gateway:
      discovery:
        locator:
          enabled: false
          lowerCaseServiceId: true
      routes:
          # 自定义路由 ID
        - id: user_route
          # 采用 LoadBalanceClient 方式请求,以 lb:// 开头,后面的是注册在 Nacos 上的服务名
          uri: lb://user-demo
          # Predicate 断言,主要作用是匹配用户的请求路径,有很多种用法
          predicates:
            # 路径匹配,一般是指要走网关时要写的路径地址
            - Path=/api/**
          filters:
            # 指路由到其他服务时去掉path中几层路径,如值为1,则去掉1层,即去掉/api
            - StripPrefix=1

2.3 加上官方文档中给的config类

这个可以不用加了,加了会冲突。顺着提示找了一下,com.alibaba.cloud.sentinel.gateway.scg.SentinelSCGAutoConfiguration中已经有了,而且默认就是开启了:

默认开启
所以这类不用加了
config类
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;

@Configuration
public class GatewayConfig {
    
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

}

2.4 启动语句添加配置

网关类型的配置:-Dcsp.sentinel.app.type=1

java -Dcsp.sentinel.app.type=1 -jar *.jar

3. nacos中添加配置文件

分别添加api组和流控规则的配置文件


nacos配置

4. 测试

将sentinel-dashboard和gateway以及user服务打包启动。


监控
路由

4.1 添加api组

开始
添加2个
nacos中有

4.2 添加流控规则

规则
10s
5s
nacos中

4.3 请求测试

post
get
修改完规则重启或者等一分钟,避免不必要的麻烦

参考文章:https://blog.csdn.net/admin_15082037343/article/details/130530211

相关文章

网友评论

      本文标题:十四、网关Gateway整合Sentinel持久化到nacos

      本文链接:https://www.haomeiwen.com/subject/lfnhydtx.html