目录
前言
Sentinel控制台规则管理和推送主要有3种方式:原始模式、Pull模式和Push模式,如果不做任何修改,默认使用的是原始模式。
所谓原始模式就是Sentinel控制台通过相关API直接将规则推送至客户端并直接更新到内存中,下面是三种模式的对比。
原始模式规则配置由于是在内存中,重启就会消失。
因此在生产环境中Sentinel
官方推荐我们使用Push模式实现动态规则配置和持久化,我们一般会依赖一些第三方中间件,如Nacos
、Apollo
、Zookeeper
、Redis
等来实现。
一、Push模式原理
Push模式的推送流程是:Sentinel控制台新增/修改配置 → 持久化至配置中心(Nacos) → Sentinel数据源监听配置中心最新配置 → Sentinel客户端从数据源中读取配置。
整体流程图如下:
更多内容请参考:在生产环境中使用Sentinel。
二、Sentinel控制台改造支持配置持久化至Nacos
由于Sentinel
控制台本身并不支持在前端界面修改规则配置后推送至第三方配置中心,因此我们需要对Sentinel Dashboard
的代码进行修改。
控制台代码修改的思路参考:Sentinel控制台集群流控管理。
1、拉取最新代码
直接从Github上拉取sentinel-dashboard的代码即可,我拉取的版本是1.8.1。
2、开始搬运代码
拉下代码后,可以看到在test目录下,有关于Nacos规则推送的类,我们可以直接搬过去。
这里我自己其实做了一些代码优化和小调整,调整后的文件目录如下:
(1) 调整点之修改NacosConfig类
@Configuration
@EnableConfigurationProperties(NacosConfigProperties.class)
public class NacosConfig {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
// 这里在创建ConfigService实例时加了Nacos实例地址和命名空间两个属性。
properties.put(PropertyKeyConst.SERVER_ADDR, nacosConfigProperties.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, nacosConfigProperties.getNamespace());
return ConfigFactory.createConfigService(properties);
}
}
(2) 调整点之新增NacosConfigProperties类
@ConfigurationProperties(prefix = "sentinel.config.nacos")
public class NacosConfigProperties {
/**
* nacos服务器地址
*/
private String serverAddr;
/**
* nacos命名空间
*/
private String namespace;
/**
* 分组名
*/
private String groupId = NacosConfigUtil.GROUP_ID;
/**
* 限流规则dataId后缀
*/
private String flowRuleDataIdSuffix = NacosConfigUtil.FLOW_DATA_ID_POSTFIX;
/**
* 降级规则dataId后缀
*/
private String degradeRuleDataIdSuffix = NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX;
public String getServerAddr() {
return serverAddr;
}
public void setServerAddr(String serverAddr) {
this.serverAddr = serverAddr;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getFlowRuleDataIdSuffix() {
return flowRuleDataIdSuffix;
}
public void setFlowRuleDataIdSuffix(String flowRuleDataIdSuffix) {
this.flowRuleDataIdSuffix = flowRuleDataIdSuffix;
}
public String getDegradeRuleDataIdSuffix() {
return degradeRuleDataIdSuffix;
}
public void setDegradeRuleDataIdSuffix(String degradeRuleDataIdSuffix) {
this.degradeRuleDataIdSuffix = degradeRuleDataIdSuffix;
}
}
(3) 调整点之修改NacosConfigUtil类
public final class NacosConfigUtil {
public static final String GROUP_ID = "SENTINEL_GROUP";
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
public static final String SYSTEM_FULE_DATA_ID_POSTFIX = "-system-rules";
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
/**
* cc for `cluster-client`
*/
public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
/**
* cs for `cluster-server`
*/
public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";
private NacosConfigUtil() {
}
}
(4) DynamicRuleProvider和DynamicRulePublisher实现优化
这里我用模板方法模式优化了一下代码,新增了AbstractNacosProvider
和AbstractNacosPublisher
两个类。
1) AbstractNacosProvider类
public abstract class AbstractNacosProvider<T> implements DynamicRuleProvider<List<T>> {
private static final long timeoutInMills = 3000;
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<T>> converter;
@Override
public List<T> getRules(String appName) throws Exception {
String dataId = appName + getDataIdSuffix();
String groupId = nacosConfigProperties.getGroupId();
String rules = configService.getConfig(dataId, groupId, timeoutInMills);
return StringUtil.isEmpty(rules) ? Collections.emptyList() : converter.convert(rules);
}
protected NacosConfigProperties getNacosConfigProperties() {
return nacosConfigProperties;
}
protected abstract String getDataIdSuffix();
}
2) AbstractNacosPublisher类
public abstract class AbstractNacosPublisher<T> implements DynamicRulePublisher<List<T>> {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<T>, String> converter;
@Override
public void publish(String app, List<T> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (CollectionUtils.isEmpty(rules)) {
return;
}
String dataId = app + getDataIdSuffix();
String groupId = nacosConfigProperties.getGroupId();
configService.publishConfig(dataId, groupId, converter.convert(rules));
}
protected NacosConfigProperties getNacosConfigProperties() {
return nacosConfigProperties;
}
protected abstract String getDataIdSuffix();
}
3) FlowRuleNacosProvider类
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider extends AbstractNacosProvider<FlowRuleEntity> {
@Override
protected String getDataIdSuffix() {
return super.getNacosConfigProperties().getFlowRuleDataIdSuffix();
}
}
4) FlowRuleNacosPublisher类
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher extends AbstractNacosPublisher<FlowRuleEntity> {
@Override
protected String getDataIdSuffix() {
return super.getNacosConfigProperties().getFlowRuleDataIdSuffix();
}
}
备注:降级规则相关实现类参考上面的FlowRuleNacosProvider和FlowRuleNacosPublisher即可。
3、修改Controller
(1) FlowControllerV2类
需要注意的是,FlowControllerV2
类主要用于集群流控相关的规则配置读取和发布,而FlowControllerV1
类用于单个应用维度流控的规则配置和发布。
这个类这里我们把动态规则获取和发布相关实现类改成了我们自己实现的基于Nacos的实现,如下:
备注:其它修改处,如从Nacos中读取流控规则配置,发布流控规则配置到Nacos的代码示例,请参考代码:FlowControllerV2。
(2) DegradeControllerV2类
降级规则配置读取和发布控制器的修改和FlowControllerV2
基本差不多,只不过降级是针对单个应用。
备注:这里我新复制了一个
DegradeControllerV2
类,注意把DegradeController
类上的@RestController
注解给干掉。其它细节修改请参考:DegradeControllerV2。
4、修改前端路由配置文件sidebar.html
之前这里的流控规则的路由是:dashboard.flowV1,也就是路由到FlowController
。现在我们把修改为dashboard.flow,也就是路由到FlowControllerV2
。
备注:到这里sentinel控制台的代码修改就差不多了,至于其它规则Controller的修改,如:热点规则、系统规则、授权规则等,可以参考上面的
DegradeControllerV2
类。
5、Sentinel控制台application.properties
新增Nacos数据源相关的自定义配置,我们在NacosConfigProperties
类中有定义过。
# nacos server address
sentinel.config.nacos.server-addr=localhost:8848
# nacos namespace to use
sentinel.config.nacos.namespace=d3a61a32-1c74-4fc6-a9f2-1cf18675c4ee
6、启动Sentinel控制台
备注:用户名和密码都是sentinel。
三、Sentinel客户端接入Nacos数据源
Nacos
和Sentinel
都是Spring Cloud Alibaba的开源组件,这里我们就选用Nacos
作为配置中心。
1、Maven依赖
<dependencies>
<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>
<version>${sentinel-datasouce-nacos.version}</version>
</dependency>
</dependencies>
备注:
Spring Cloud Alibaba
的版本我用的是2.2.6.RELEASE,sentinel-datasource-nacos
的版本是1.8.1。
2、bootstrap.yaml
server:
port: 9010
spring:
application:
name: sentinel-client
3、application.yml
这里我们流控和降级相关的规则都是从Nacos中读取,如下:
spring:
cloud:
sentinel:
datasource:
flow-ds:
nacos:
server-addr: localhost:8848
namespace: d3a61a32-1c74-4fc6-a9f2-1cf18675c4ee
data-id: ${spring.application.name}-flow-rules
group-id: SENTINEL_GROUP
data-type: json
rule-type: flow
degrade-ds:
nacos:
server-addr: localhost:8848
namespace: d3a61a32-1c74-4fc6-a9f2-1cf18675c4ee
data-id: ${spring.application.name}-degrade-rules
group-id: SENTINEL_GROUP
data-type: json
rule-type: degrade
transport:
port: 8719 #与sentinel dashboard的通信端口,会启动一个http server,供dashboard调用
dashboard: localhost:9999 #dashboard地址和端口
更多内容请参考:Sentinel集成Spring Cloud Alibaba动态数据源支持。
四、Sentinel客户端测试用例
1、SentinelController
@RestController
public class SentinelController {
@GetMapping("/order")
@SentinelResource(value = ResourceDefConst.ORDER_INFO, blockHandler = "exceptionHandler", fallback = "fallback")
public String queryOrderInfo(long orderId) {
Random random = new Random();
int timeout = random.nextInt(150);
LockSupport.parkNanos(timeout * 1000 * 1000);
System.out.println(String.format("调用时间为:%d", timeout));
return String.format("订单信息:%d", orderId);
}
/**
* 若blockHandler和fallback都进行了配置,则被限流降级时抛出BlockException时只会进入blockHandler逻辑
* @param orderId
* @param e
* @return
*/
public String exceptionHandler(long orderId, BlockException e) {
e.printStackTrace();
return "服务降级啦!";
}
public String fallback(long orderId, Throwable t) {
t.printStackTrace();
return "服务异常啦!";
}
}
备注:启动Sentinel客户端,当有相应的资源请求到来时,Sentinel控制台会展示Sentinel客户端应用的资源调用信息。
2、登录Sentinel控制台修改流控规则
这里我们修改了资源名为order_info
的流控规则为基于QPS的限流且阈值为2。
3、登录Nacos控制台查看流控规则
在上一步我们已经在Sentinel控制台修改了集群流控相关的规则,我们登录Nacos控制台看看配置是否已经持久化到了Nacos上。
备注:可以看到Sentinel控制台的修改确实同步到了Nacos上。
4、请求示例
当我们访问http://localhost:9010/order?orderId=1
,可以看到当QPS超过2时会触发FlowException,说明我们的流控规则修改是生效的。
备注:关于降级规则的测试同样可以在Sentinel控制台上进行操作。