Sentinel实现动态配置的集群流控的方法

2025-05-29 0 82

介绍

为什么要使用集群流控呢?

相对于单机流控而言,我们给每台机器设置单机限流阈值,在理想情况下整个集群的限流阈值为机器数量✖️单机阈值。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。

基于单机流量不均的问题以及如何设置集群整体的QPS的问题,我们需要创建一种集群限流的模式,这时候我们很自然地就想到,可以找一个 server 来专门统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。

原理

集群限流的原理很简单,和单机限流一样,都需要对 qps 等数据进行统计,区别就在于单机版是在每个实例中进行统计,而集群版是有一个专门的实例进行统计。

这个专门的用来统计数据的称为 Sentinel 的 token server,其他的实例作为 Sentinel 的 token client 会向 token server 去请求 token,如果能获取到 token,则说明当前的 qps 还未达到总的阈值,否则就说明已经达到集群的总阈值,当前实例需要被 block,如下图所示:

Sentinel实现动态配置的集群流控的方法

和单机流控相比,集群流控中共有两种身份:

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。

而单机流控中只有一种身份,每个 sentinel 都是一个 token server。

注意,集群限流中的 token server 是单点的,一旦 token server 挂掉,那么集群限流就会退化成单机限流的模式。

Sentinel 集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:

  • 集群总体模式:即限制整个集群内的某个资源的总体 qps 不超过此阈值。
  • 单机均摊模式:单机均摊模式下配置的阈值等同于单机能够承受的限额,token server 会根据连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30),按照计算出的总的阈值来进行限制。这种方式根据当前的连接数实时计算总的阈值,对于机器经常进行变更的环境非常适合。

部署方式

token server 有两种部署方式:

一种是独立部署,就是单独启动一个 token server 服务来处理 token client 的请求,如下图所示:

Sentinel实现动态配置的集群流控的方法

如果独立部署的 token server 服务挂掉的话,那其他的 token client 就会退化成本地流控的模式,也就是单机版的流控,所以这种方式的集群限流需要保证 token server 的高可用性。

一种是嵌入部署,即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,如下图所示:

Sentinel实现动态配置的集群流控的方法

嵌入式部署的模式中,如果 token server 服务挂掉的话,我们可以将另外一个 token client 升级为token server来,当然啦如果我们不想使用当前的 token server 的话,也可以选择另外一个 token client 来承担这个责任,并且将当前 token server 切换为 token client。Sentinel 为我们提供了一个 api 来进行 token server 与 token client 的切换:


  1. http://<ip>:<port>/setClusterMode?mode=<xxx>

其中 mode 为 0 代表 client,1 代表 server,-1 代表关闭。

PS:注意应用端需要引入集群限流客户端或服务端的相应依赖。

集群流控制台

sentinel为用户提供集群流控制台功能,能够通过控制台配置集群的限流规则以及配置集群的Server与Client。

集群限流客户端

要想使用集群限流功能,必须引入集群限流 client 相关依赖:


  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-cluster-client-default</artifactId>
  4. <version>1.8.0</version>
  5. </dependency>

集群限流服务端

要想使用集群限流服务端,必须引入集群限流 server 相关依赖:


  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-cluster-server-default</artifactId>
  4. <version>1.8.0</version>
  5. </dependency>

我们结合server和client实现一个嵌入式模式。在pom中同时引入上面的两个依赖,并配置sentinel控制台地址,实现一个查询订单的接口。

pom


  1. <dependency>
  2. <groupId>com.alibaba.csp</groupId>
  3. <artifactId>sentinel-cluster-server-default</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.alibaba.csp</groupId>
  7. <artifactId>sentinel-cluster-client-default</artifactId>
  8. </dependency>

application.yml


  1. server:
  2. port: 9091
  3. spring:
  4. application:
  5. name: cloudalibabasentinelclusterServer
  6. cloud:
  7. sentinel:
  8. transport:
  9. #配置sentinel dashboard地址
  10. dashboard: localhost:8080
  11. port: 8719 #默认8719端口

OrderController


  1. @RestController
  2. public class OrderController {
  3. /**
  4. * 查询订单
  5. * @return
  6. */
  7. @GetMapping("/order/{id}")
  8. public CommonResult<Order> getOrder(@PathVariable("id") Long id){
  9. Order order = new Order(id, "212121");
  10. return CommonResult.success(order.toString());
  11. }
  12. }

代码示例如cloudalibaba-sentinel-cluster-embedded9091

修改VM options配置,启动三个不同端口的实例,即可。


  1. Dserver.port=9091 Dproject.name=cloudalibabasentinelclusterServer Dcsp.sentinel.log.use.pid=true
  2. Dserver.port=9092 Dproject.name=cloudalibabasentinelclusterServer Dcsp.sentinel.log.use.pid=true
  3. Dserver.port=9093 Dproject.name=cloudalibabasentinelclusterServer Dcsp.sentinel.log.use.pid=true

控制台配置

登录sentinel的控制台,并有访问量后,我们就可以在 Sentinel上面看到集群流控,如下图所示:

点击添加Token Server。

Sentinel实现动态配置的集群流控的方法

从实例列表中选择一个作为Server端,其他作为Client端,并选中到右侧Client列表,配置token sever端的最大允许的QPS,用于对 Token Server 的资源使用进行限制,防止在嵌入模式下影响应用本身。

Sentinel实现动态配置的集群流控的方法

配置完成之后的Token Server列表,如下图所示

Sentinel实现动态配置的集群流控的方法

使用控制台配置token Server、token Client以及限流规则,有很多的缺点:

1、限流规则,不能持久化,应用重启之后,规则丢失。

2、token Server 、token Client配置也会丢失。

官方推荐给集群限流服务端注册动态配置源来动态地进行配置。我们使用nacos作为配置中心,动态配置客户端与服务端属性以及限流规则,实现动态集群限流。

sentinel结合nacos实现集群限流

我们使用Nacos对cloudalibaba-sentinel-cluster-embedded9091进行改造,实现动态配置源来动态进行配置。

配置源注册的相关逻辑可以置于 InitFunc 实现类中,并通过 SPI 注册,在 Sentinel 初始化时即可自动进行配置源加载监听。

嵌入模式部署

添加ClusterInitFunc类


  1. public class ClusterInitFunc implements InitFunc {
  2. //应用名称
  3. private static final String APP_NAME = AppNameUtil.getAppName();
  4. //nacos集群地址
  5. private final String remoteAddress = "localhost:8848";
  6. //nacos配置的分组名称
  7. private final String groupId = "SENTINEL_GROUP";
  8. //配置的dataId
  9. private final String flowDataId = APP_NAME + Constants.FLOW_POSTFIX;
  10. private final String paramDataId = APP_NAME + Constants.PARAM_FLOW_POSTFIX;
  11. private final String configDataId = APP_NAME + Constants.CLIENT_CONFIG_POSTFIX;
  12. private final String clusterMapDataId = APP_NAME + Constants.CLUSTER_MAP_POSTFIX;
  13. private static final String SEPARATOR = "@";
  14. @Override
  15. public void init() {
  16. // Register client dynamic rule data source.
  17. //动态数据源的方式配置sentinel的流量控制和热点参数限流的规则。
  18. initDynamicRuleProperty();
  19. // Register token client related data source.
  20. // Token client common config
  21. // 集群限流客户端的配置属性
  22. initClientConfigProperty();
  23. // Token client assign config (e.g. target token server) retrieved from assign map:
  24. //初始化Token客户端
  25. initClientServerAssignProperty();
  26. // Register token server related data source.
  27. // Register dynamic rule data source supplier for token server:
  28. //集群流控规则,比如限制整个集群流控阀值,启动的时候需要添加-Dproject.name=项目名
  29. registerClusterRuleSupplier();
  30. // Token server transport config extracted from assign map:
  31. //初始化server的端口配置
  32. initServerTransportConfigProperty();
  33. // Init cluster state property for extracting mode from cluster map data source.
  34. //初始化集群中服务是客户端还是服务端
  35. initStateProperty();
  36. }
  37. private void initDynamicRuleProperty() {
  38. //流量控制的DataId分别是APP_NAME + Constants.FLOW_POSTFIX;热点参数限流规则的DataId是APP_NAME + Constants.PARAM_FLOW_POSTFIX;
  39. ReadableDataSource<String, List<FlowRule>> ruleSource = new NacosDataSource<>(remoteAddress, groupId,
  40. flowDataId, source -> jsON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
  41. FlowRuleManager.register2Property(ruleSource.getProperty());
  42. ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(remoteAddress, groupId,
  43. paramDataId, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
  44. ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
  45. }
  46. private void initClientConfigProperty() {
  47. ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(remoteAddress, groupId,
  48. configDataId, source 编程客栈-> JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {}));
  49. ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
  50. }
  51. private void initServerTransportConfigProperty() {
  52. ReadableDataSource<String, ServerTransportConfig> serverTransportDs = new NacosDataSource<>(remoteAddress, groupId,
  53. clusterMapDataId, source -> {
  54. List<ClusterGroupEntity> groupList = new Gson().fromJson(source, new TypeToken<List<ClusterGroupEntity>>(){}.getType());
  55. return Optional.ofNullable(groupList)
  56. www.cppcns.com .flatMap(this::extractServerTransportConfig)
  57. .orElse(null);
  58. });
  59. ClusterServerConfigManager.registerServerTransportProperty(serverTransportDs.getProperty());
  60. }
  61. private void registerClusterRuleSupplier() {
  62. // Register cluster flow rule property supplier which creates data source by namespace.
  63. // Flow rule dataId format: ${namespace}-flow-rules
  64. ClusterFlowRuleManager.setPropertySupplier(namespace -> {
  65. ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
  66. namespace + Constants.FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
  67. return ds.getProperty();
  68. });
  69. // Register cluster parameter flow rule property supplier which creates data source by namespace.
  70. ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
  71. ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
  72. namespace + Constants.PARAM_FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
  73. return ds.getProperty();
  74. });
  75. }
  76. private void initClientServerAssignProperty() {
  77. // Cluster map format:
  78. // [{"clientSet":["112.12.88.66@8729","112.12.88.67@8727"],"ip":"112.12.88.68","serverId":"112.12.88.68@8728","port":11111}]
  79. // serverId: <ip@commandPort>, commandPort for port exposed to Sentinel dashboard (transport module)
  80. ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(remoteAddress, groupId,
  81. clusterMapDataId, source -> {
  82. List<ClusterGroupEntity> groupList = new Gson().fromJson(source, new TypeToken<List<ClusterGroupEntity>>(){}.getType());
  83. return Optional.ofNullable(groupList)
  84. .flatMap(this::extractClientAssignment)
  85. .orElse(null);
  86. });
  87. ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
  88. }
  89. private void initStateProperty() {
  90. // Cluster map format:
  91. // [{"clientSet":["112.12.88.66@8729","112.12.88.67@8727"],"ip":"112.12.88.68","serverId":"112.12.88.68@8728","port":11111}]
  92. // serverId: <ip@commandPort>, commandPort for port exposed to Sentinel dashboard (transport module)
  93. ReadableDataSource<String, Integer> clusterModeDs = new NacosDataSource<>(remoteAddress, groupId,
  94. clusterMapDataId, source -> {
  95. List<ClusterGroupEntity> groupList = new Gson().fromJson(source, new TypeToken<List<ClusterGroupEntity>>(){}.getType());
  96. return Optional.ofNullable(groupList)
  97. .map(this::extractMode)
  98. .orElse(ClusterStateManager.CLUSTER_NOT_STARTED);
  99. });
  100. ClusterStateManager.registerProperty(clusterModeDs.getProperty());
  101. }
  102. private int extractMode(List<ClusterGroupEntity> groupList) {
  103. // If any server group serverId matches current, then it's token server.
  104. if (groupList.stream().anyMatch(this::machineEqual)) {
  105. return ClusterStateManager.CLUSTER_SERVER;
  106. }
  107. // If current machine belongs to any of the token server group, then it's token client.
  108. // Otherwise it's unassigned, should be set to NOT_STARTED.
  109. boolean canBeClient = groupList.stream()
  110. .flatMap(e -> e.getClientSet().stream())
  111. .filter(Objects::nonNull)
  112. .anyMatch(e -> e.equals(getCurrentMachineId()));
  113. return canBeClient ? ClusterStateManager.CLUSTER_CLIENT : ClusterStateManager.CLUSTER_NOT_STARTED;
  114. }
  115. private Optional<ServerTransportConfig> extractServerTransportConfig(List<ClusterGroupEntity> groupList) {
  116. return groupList.stream()
  117. .filter(this::machineEqual)
  118. .findAny()
  119. .map(e -> new ServerTransportConfig().setPort(e.getPort()).setIdleSeconds(600));
  120. }
  121. private Optional<ClusterClientAssignConfig> extractClientAssignment(List<ClusterGroupEntity> groupList) {
  122. if (groupList.stream().anyMatch(this::mwww.cppcns.comachineEqual)) {
  123. return Optional.empty();
  124. }
  125. // Build client assign config from tJTVlRhe client set of target server group.
  126. for (ClusterGroupEntity group : groupList) {
  127. if (group.getClientSet().contains(getCurrentMachineId())) {
  128. String ip = group.getIp();
  129. Integer port = group.getPort();
  130. return Optional.of(new ClusterClientAssignConfig(ip, port));
  131. }
  132. }
  133. return Optional.empty();
  134. }
  135. private boolean machineEqual(/*@Valid*/ ClusterGroupEntity group) {
  136. return getCurrentMachineId().equals(group.getServerId());
  137. }
  138. private String getCurrentMachineId() {
  139. // Note: this may not work well for container-based env.
  140. return HostNameUtil.getIp() + SEPARATOR + TransportConfig.getRuntimePort();
  141. }
  142. }

在resources文件夹下创建META-INF/service,,然后创建一个叫做com.alibaba.csp.sentinel.init.InitFunc的文件,在文件中指名实现InitFunc接口的类全路径,内容如下:


  1. com.liang.springcloud.alibaba.init.ClusterInitFunc

添加配置的解析类:


  1. public class ClusterGroupEntity implements Serializable {
  2. private String serverId;
  3. private String ip;
  4. private Integer port;
  5. private Set<String> clientSet;
  6. public String getServerId() {
  7. return serverId;
  8. }
  9. public void setServerId(String serverId) {
  10. this.serverId = serverId;
  11. }
  12. public String getIp() {
  13. return ip;
  14. }
  15. public void setIp(String ip) {
  16. this.ip = ip;
  17. }
  18. public Integer getPort() {
  19. return port;
  20. }
  21. public void setPort(Integer port) {
  22. this.port = port;
  23. }
  24. public Set<String> getClientSet() {
  25. return clientSet;
  26. }
  27. public void setClientSet(Set<String> clientSet) {
  28. this.clientSet = clientSet;
  29. }
  30. @Override
  31. public String toString() {
  32. return "ClusterGroupEntity{" +
  33. "serverId='" + serverId + '\\'' +
  34. ", ip='" + ip + '\\'' +
  35. ", port=" + port +
  36. ", clientSet=" + clientSet +
  37. '}';
  38. }
  39. }

在Nacos中添加动态规则配置,以及token server与token client的配置:

DataId:cloudalibaba-sentinel-clusterServer-flow-rules Group:SENTINEL_GROUP 配置内容(json格式):


  1. [
  2. {
  3. "resource" : "/order/{id}", // 限流的资源名称
  4. "grade" : 1, // 限流模式为:qps,线程数限流0,qps限流1
  5. "count" : 20, // 阈值为:20
  6. "clusterMode" : true, // 是否是集群模式,集群模式为:true
  7. "clusterConfig" : {
  8. "flowId" : 111, // 全局唯一id
  9. "thresholdType" : 1, // 阈值模式为:全局阈值,0是单机均摊,1是全局阀值
  10. "fallbackToLocalWhenFail" : true // 在 client 连接失败或通信失败时,是否退化到本地的限流模式
  11. }
  12. }
  13. ]

DataId:cloudalibaba-sentinel-clusterServer-cluster-client-config Group:SENTINEL_GROUP 配置内容(json格式):


  1. {
  2. "requestTimeout": 20
  3. }

DataId:cloudalibaba-sentinel-clusterServer-cluster-map Group:SENTINEL_GROUP 配置内容(json格式):


  1. [{
  2. "clientSet": ["10.133.40.30@8721", "10.133.40.30@8722"],
  3. "ip": "10.133.40.30",
  4. "serverId": "10.133.40.30@8720",
  5. "port": 18730 //这个端口是token server通信的端口
  6. }]

重新启动服务,并访问接口,我们可以看到流控规则与集群流控都自动配置完成。我们需要测试,我们集群流控是否已经生效。

不断执行以下命令:


  1. ab n 100 c 50 http://localhost:9091/order/1
  2. ab n 100 c 50 http://localhost:9092/order/3
  3. ab n 100 c 50 http://localhost:9093/order/1

测试效果图:

我们从实时监控图上可以看出,资源名为/order/{id},整个集群的QPS为20,跟我们的配置是一样的。当作为token server的机器挂掉后,集群限流会退化到 local 模式的限流,即在本地按照单机阈值执行限流检查。

Sentinel实现动态配置的集群流控的方法

Token Server 分配配置:

Sentinel实现动态配置的集群流控的方法

上面这张图可以很好帮忙我们解释嵌入模式的具体实现。通过配置信息解析,管理我们的token server与token client。

适用范围:

嵌入模式适合某个应用集群内部的流控。由于隔离性不佳,token server会影响应用本身,需要限制 token server 的总QPS。

独立模式部署

独立模式相对于嵌入模式而言就是将token server与应用隔离,进行独立部署。将嵌入模式中token server和token client分离,分别进行配置。我们只需要将 InitFunc 实现类进行拆分。

token server的nacos配置

server的名称空间配置,(集群的namespace或客户端项目名)如下:

DataId:cluster-server-namespace-set Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. [
  2. "cloudalibaba-sentinel-cluster-client-alone"
  3. ]

server的通信端口配置,如下:

DataId:cluster-server-transport-config Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. {
  2. "idleSecods":600,
  3. "port": 18730
  4. }

Token sever的流控限制配置,如下:

DataId:cluster-server-flow-config Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. {
  2. "exceedCount":1.0,
  3. "maxAllowedQps":20000,
  4. "namespace":"cloudalibaba-sentinel-clust编程客栈er-client-alone"
  5. }

token server的host地址与端口号配置,如下:

DataId: cluster-server-config Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. {
  2. "serverHost": "10.133.40.30",
  3. "serverPort": 18730
  4. }

token server的InitFunc类:


  1. /**
  2. * @PROJECT_NAME: SpringCloud-Learning
  3. * @USER: yuliang
  4. * @DESCRIPTION:
  5. * @DATE: 2021-04-01 10:01
  6. */
  7. public class ClusterServerInitFunc implements InitFunc {
  8. //nacos集群地址
  9. private final String remoteAddress = "localhost:8848";
  10. //配置的分组名称
  11. private final String groupId = "SENTINEL_ALONE_GROUP";
  12. //配置的dataId
  13. private final String namespaceSetDataId = "cluster-server-namespace-set";
  14. private final String serverTransportDataId = "cluster-server-transport-config";
  15. private final String serverFlowDataId = "cluster-server-flow-config";
  16. @Override
  17. public void init() {
  18. //监听特定namespace(集群的namespace或客户端项目名)下的集群限流规则
  19. initPropertySupplier();
  20. // 设置tokenServer管辖的作用域(即管理哪些应用)
  21. initTokenServerNameSpaces();
  22. // Server transport configuration data source.
  23. //Server端配置
  24. initServerTransportConfig();
  25. // 初始化最大qps
  26. initServerFlowConfig();
  27. //初始化服务器状态
  28. initStateProperty();
  29. }
  30. private void initPropertySupplier(){
  31. // Register cluster flow rule property supplier which creates data source by namespace.
  32. // Flow rule dataId format: ${namespace}-flow-rules
  33. ClusterFlowRuleManager.setPropertySupplier(namespace -> {
  34. ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
  35. namespace + Constants.FLOW_POSTFIX,
  36. source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
  37. return ds.getProperty();
  38. });
  39. // Register cluster parameter flow rule property supplier which creates data source by namespace.
  40. ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
  41. ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
  42. namespace + Constants.PARAM_FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
  43. return ds.getProperty();
  44. });
  45. }
  46. private void initTokenServerNameSpaces(){
  47. // Server namespace set (scope) data source.
  48. ReadableDataSource<String, Set<String>> namespaceDs = new NacosDataSource<>(remoteAddress, groupId,
  49. namespaceSetDataId, source -> JSON.parseObject(source, new TypeReference<Set<String>>() {}));
  50. ClusterServerConfigManager.registerNamespaceSetProperty(namespaceDs.getProperty());
  51. }
  52. private void initServerTransportConfig(){
  53. // Server transport configuration data source.
  54. ReadableDataSource<String, ServerTransportConfig> transportConfigDs = new NacosDataSource<>(remoteAddress,
  55. groupId, serverTransportDataId,
  56. source -> JSON.parseObject(source, new TypeReference<ServerTransportConfig>() {}));
  57. ClusterServerConfigManager.registerServerTransportProperty(transportConfigDs.getProperty());
  58. }
  59. private void initServerFlowConfig(){
  60. // Server namespace set (scope) data source.
  61. ReadableDataSource<String, ServerFlowConfig> serverFlowConfig = new NacosDataSource<>(remoteAddress, groupId,
  62. serverFlowDataId, source -> JSON.parseObject(source, new TypeReference<ServerFlowConfig>() {}));
  63. ClusterServerConfigManager.registerGlobalServerFlowProperty(serverFlowConfig.getProperty());
  64. }
  65. private void initStateProperty() {
  66. ClusterStateManager.applyState(ClusterStateManager.CLUSTER_SERVER);
  67. }
  68. }

token client的nacos配置

客户端请求超时配置,如下:

DataId:cluster-client-config Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. {
  2. "requestTimeout": 20
  3. }

流控限流配置,如下:

DataId: cloudalibaba-sentinel-cluster-client-alone-flow-rules Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. [
  2. {
  3. "resource" : "/order/{id}", // 限流的资源名称
  4. "grade" : 1, // 限流模式为:qps
  5. "count" : 30, // 阈值为:30
  6. "clusterMode" : true, // 集群模式为:true
  7. "clusterConfig" : {
  8. "flowId" : 111, // 全局唯一id
  9. "thresholdType" : 1, // 阈值模式为:全局阈值
  10. "fallbackToLocalWhenFail" : true // 在 client 连接失败或通信失败时,是否退化到本地的限流模式
  11. }
  12. }
  13. ]

热点限流配置,如下:

DataId:cloudalibaba-sentinel-cluster-client-alone-param-rules Group:SENTINEL_ALONE_GROUP 配置内容(json格式):


  1. [
  2. {
  3. "resource" : "order", // 限流的资源名称
  4. "paramIdx" : 1, //参数索引
  5. "grade" : 1, // 限流模式为:qps
  6. "count" : 10, // 阈值为:10
  7. "clusterMode" : true, // 集群模式为:true
  8. "clusterConfig" : {
  9. "flowId" : 121, // 全局唯一id
  10. "thresholdType" : 1, // 阈值模式为:全局阈值
  11. "fallbackToLocalWhenFail" : true // 在 client 连接失败或通信失败时,是否退化到本地的限流模式
  12. },
  13. "paramFlowItemList":[ //索引为1的参数值为hot时,接口阈值为50,其他值均为10
  14. {
  15. object: "hot",
  16. count: 50,
  17. classType: "java.lang.String"
  18. }
  19. ]
  20. }
  21. ]

Token client的InitFunc类:


  1. /**
  2. * @PROJECT_NAME: SpringCloud-Learning
  3. * @USER: yuliang
  4. * @DESCRIPTION:
  5. * @DATE: 2021-04-01 17:47
  6. */
  7. public class ClusterClientInitFunc implements InitFunc {
  8. //项目名称
  9. private static final String APP_NAME = AppNameUtil.getAppName();
  10. //nacos集群地址
  11. private final String remoteAddress = "localhost:8848";
  12. //nacos配置的分组名称
  13. private final String groupId = "SENTINEL_ALONE_GROUP";
  14. //项目名称 + Constants的配置名称,组成配置的dataID
  15. private final String flowDataId = APP_NAME + Constants.FLOW_POSTFIX;
  16. private final String paramDataId = APP_NAME + Constants.PARAM_FLOW_POSTFIX;
  17. private final String configDataId = "cluster-client-config";
  18. private final String serverDataId = "cluster-server-config";
  19. @Override
  20. public void init() throws Exception {
  21. // Register client dynamic rule data source.
  22. //客户端,动态数据源的方式配置sentinel的流量控制和热点参数限流的规则。
  23. initDynamicRuleProperty();
  24. // Register token client related data source.
  25. // Token client common config
  26. // 集群限流客户端的配置属性
  27. initClientConfigProperty();
  28. // Token client assign config (e.g. target token server) retrieved from assign map:
  29. //初始化Token客户端
  30. initClientServerAssignProperty();
  31. //初始化客户端状态
  32. initStateProperty();
  33. }
  34. private void initDynamicRuleProperty() {
  35. //流量控制的DataId分别是APP_NAME + Constants.FLOW_POSTFIX;热点参数限流规则的DataId是APP_NAME + Constants.PARAM_FLOW_POSTFIX;
  36. ReadableDataSource<String, List<FlowRule>> ruleSource = new NacosDataSource<>(remoteAddress, groupId,
  37. flowDataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
  38. FlowRuleManager.register2Property(ruleSource.getProperty());
  39. ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(remoteAddress, groupId,
  40. paramDataId, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
  41. ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
  42. }
  43. private void initClientConfigProperty() {
  44. ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(remoteAddress, groupId,
  45. configDataId, source -> JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {}));
  46. ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
  47. }
  48. private void initClientServerAssignProperty() {
  49. ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(remoteAddress, groupId,
  50. serverDataId, source -> JSON.parseObject(source, new TypeReference<ClusterClientAssignConfig>() {}));
  51. ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
  52. }
  53. private void initStateProperty() {
  54. ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
  55. }
  56. }

核心的代码与配置,如上所示,其他代码,可以访问:


  1. <module>cloudalibaba-sentinel-cluster-server-alone9092</module>
  2. <module>cloudalibaba-sentinel-cluster-client-alone9093</module>

测试:

启动cloudalibaba-sentinel-cluster-server-alone9092,我们启动两个实例,模拟集群(可以启动多个):


  1. Dserver.port=9092 Dcsp.sentinel.log.use.pid=true
  2. Dserver.port=9094 Dcsp.sentinel.log.use.pid=true

启动cloudalibaba-sentinel-cluster-client-alone9093,我们启动1个实例,模拟server(实现master选举之后,可以启动多个):


  1. Dserver.port=9093 Dcsp.sentinel.log.use.pid=true

不断执行以下命令,进行接口访问测试:


  1. ab n 100 c 50 http://localhost:9092/order/1
  2. ab n 100 c 50 http://localhost:9094/order/3

我们从实时监控图上可以看出,资源名为/order/{id},整个集群的QPS为30,跟我们的配置是一样的。当作为token server的机器挂掉后,集群限流会退化到 local 模式的限流,即在本地按照单机阈值执行限流检查。

Sentinel实现动态配置的集群流控的方法

热点限流已经为大家实现了,大家可以自行测试,比较简单,不再累述。


  1. ab n 100 c 50 http://localhost:9092/hot_order/1/hot
  2. ab n 100 c 50 http://localhost:9094/hot_order/1/hot
  3. ab n 100 c 50 http://localhost:9092/hot_order/1/nothot
  4. ab n 100 c 50 http://localhost:9094/hot_order/1/nothot

其它

若在生产环境使用集群限流,管控端还需要关注以下的问题:

  • Token Server 自动管理、调度(分配/选举 Token Server)
  • Token Server 高可用,在某个 server 不可用时自动 failover 到其它机器

总结

集群流控,有两种模式,嵌入模式和独立模式,个人不建议在业务系统使用集群流控集群流控可以在网关层做,业务层的话可以使用单机流控,相对来说简单好上手。token server目前存在单点问题,需要个人实现master选举,并修改 cluster-server-config的IP即可。

代码示例

本文示例读者可以通过查看下面仓库中的项目,如下所示:


  1. <module>cloudalibaba-sentinel-cluster</module>

github:https://github.com/jiuqiyuliang/SpringCloud-Learning

到此这篇关于Sentinel实现动态配置的集群流控的文章就介绍到这了,更多相关Sentinel集群流控内容请搜索快网idc以前的文章或继续浏览下面的相关文章希望大家以后多多支持快网idc!

原文链接:https://blog.csdn.net/jiuqiyuliang/article/details/115524800

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Sentinel实现动态配置的集群流控的方法 https://www.kuaiidc.com/108008.html

相关文章

发表评论
暂无评论