前面已经讲了:微服务整合Sentinel持久化到nacos。
这里我们说下网关整合sentinel,网关用的gateway。sentinel官网这里也介绍了网关流控的一些内容。
在前面讲微服务整合sentinel持久化到nacos时,我们把测试代码中sentinel和nacos交互的类拷贝到了rule模块中。
在网关gateway中也需要添加类似的代码。
首先我们看一下官网的介绍:
什么意思?
-
路由维度:也就是我们在gateway网关中配置的路由,如果触发sentinel能够识别到,并支持添加规则。
gateway路由配置
这个路由配置可以在sentinel中发现。
sentinel-gateway
路由链路 -
自定义api维度:我们可以像官方给出的那样,提前自定义api组,也可以被sentinel识别,然后添加规则。我们更多的会用这个,但是我们一般不会提前写在代码中,而是在web页面自定义api组。官网例子:
api组
说这么多,主要是想告诉大家,gateway整合sentinel的时候,不像在微服务中那样(在微服务中,sentinel可以直接识别到url粒度的资源,然后在这些url资源上添加规则,nacos只需要处理规则),有2个地方需要自己改动:
- 自定义的api资源组,需要和nacos交互
- 针对以上资源做的规则(也包括路由维度的资源),需要和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
包下:
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:
1.4.1 GatewayApiController
-
注入我们自己的实现类
注入实现类
-
-
保存的时候有一个中间的小方法,我们重写一下
重写小保存方法
-
// 重写保存规则的方法,通过新加的实现类保存到nacos
private void publishApis(String app) {
List<ApiDefinitionEntity> apis = repository.findAllByApp(app);
try {
rulePublisher.publish(app, apis);
} catch (Exception e) {
logger.error("推送apis到nacos异常");
}
}
-
修改查
apis查
-
-
修改增
apis增
-
-
修改删和改 2处
和增一样,找到地方改
apis删查
-
1.4.2 GatewayFlowRuleController
-
注入我们自己的实现类
注入实现类
-
-
重写小方法
重写小方法
-
// 重写保存规则的方法,通过新加的实现类保存到nacos
private void publishApis(String app) {
List<GatewayFlowRuleEntity> apis = repository.findAllByApp(app);
try {
rulePublisher.publish(app, apis);
} catch (Exception e) {
logger.error("推送流控规则到nacos异常");
}
}
-
修改增删改查
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 请求测试
postget
修改完规则重启或者等一分钟,避免不必要的麻烦
参考文章:https://blog.csdn.net/admin_15082037343/article/details/130530211
网友评论