Sentinel | 配置持久化

Sentinel 的工作模式

sentinel 工作模式.png
  • 在 Sentinel 客户端(微服务)中用代码写的配置,在启动后,当有第一次流量进来的时候,会推送给 Sentinel-Dashboard;
  • 在 Sentinel-Dashboard 中的配置,会被推送到 Sentinal 客户端(微服务);
  • 默认情况下,不论 Sentinel-Dashboard 中的配置还是 Sentinal 客户端中的配置,都是在内存中的,一点重启,这些变化过的规则就都消失了;

Sentinel 引入配置中心

Sentinel 引入配置中心.png
  • Sentinel-Dashboard 需要知道,当配置发生变化的时候,要把配置推送到配置中心,持久话保存起来;
  • 客户端要知道,配置全在配置中心里存的呢,一旦配置发生变化,配置中心要把配置推给我;
  • 这样的话,不论是 Sentinel-Dashboard 还是 Sentinel 客户端重启后,都不会丢失配置;
启动 zookeeper
  • bin/zkServer.sh start

修改 sentinel-dashboard 的源码

打开项目 sentinel-dashboard
  • 作为一个 Module 引入项目 ms-security;
修改 pom 文件
  • 把 scope 的 test 注释掉,这样打包的时候,就可以把和 zookeeper 整合相关的依赖打进去了;
<!--for Zookeeper rule publisher sample-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>${curator.version}</version>
<!--            <scope>test</scope>-->
</dependency>
把已经写好的整合 zookeeper 的代码发到合适的位置
  • 其实和 zookeeper 整合的代码是有的,只不过在 test 目录下,把这写代码移到 main 的相同目录下就行了;
  • com.alibaba.csp.sentinel.dashboard.rule.zookeeper从 test 目录下拷贝到 main 下,一共 4 个类;
改代码
  • com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2

原来的:

@Autowired
@Qualifier("flowRuleDefaultProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleDefaultPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

改成:

@Autowired
@Qualifier("flowRuleZookeeperProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleZookeeperPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
改配置文件
  • 位置:sentinel-dashboard/src/main/resources/application.properties
  • 启动的端口改一下,因为 zookeeper 启动的时候,会占用 8080 端口号;
server.port=8082
改页面
  • 位置:src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html

原来的:

<li ui-sref-active="active" ng-if="!entry.isGateway">
  <a ui-sref="dashboard.flowV1({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>

改成:

<li ui-sref-active="active" ng-if="!entry.isGateway">
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>
直接运行启动类
  • 位置:com.alibaba.csp.sentinel.dashboard.DashboardApplication

重新配置 Sentinel 客户端(微服务:order)

用代码配置的规则都不要了
  • com.lixinlei.security.order.config.SentinelConfig 这个类可以直接删了;
sentinel-dashboard 的地址改一下
spring:
  application:
    # sentinel-bashboard 中会显示这个名字
    name: orderApi
  cloud:
    sentinel:
      transport:
        # 在 9080 启动 orderApi 的时候,sentinel 还会在 8719 这个端口起一个服务和 sentinel-dashboard 通信(发心跳)
        port: 8719
        # sentinel-dashboard 的地址
        dashboard: localhost:8082

配置规则

配置流控规则 createOrder (sentinel-dashboard 中)
  • 配置完了 zookeeper 的根目录中就会有一个 sentinel_rule_config 节点,sentinel 所有的规则配置都是放在这个节点下面的;
  • sentinel_rule_config 下面有一个 /orderApi 节点,这个是 order 这个微服务的 spring.application.name
  • 重启 sentinel-dashboard 和 orderApi,sentinel-dashboard 依然还有上次的配置;
  • 此时,虽然限流规则已经通过 sentinel-dashboard 写到 zookeeper 中了,但是 Sentinel 客户端(orderApi)还不知到;

让 Sentinel 知道规则配置在 zookeeper 中

依赖
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-zookeeper</artifactId>
    <version>1.5.2</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>
application.yml
# 让 Sentinel 客户端(orderApi)知道去 zookeeper 中拿配置规则 
sentinel:
  zookeeper:
    address: 127.0.0.1:2181
    path: /sentinel_rule_config
配置类
package com.lixinlei.security.order.config;

import java.util.List;

import javax.annotation.PostConstruct;

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

import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;

/**
 * 这里配置,让 Sentinel 客户端到 zookeeper 中读配置规则
 */
@Component
public class SentinelConfigZookeeper {

    @Value("${sentinel.zookeeper.address}")
    private String zkServer;

    @Value("${sentinel.zookeeper.path}")
    private String zkPath;

    @Value("${spring.application.name}")
    private String appName;

    /**
     * 这个 Bean 构造好了之后,马上就取 zookeeper 中读配置规则
     */
    @PostConstruct
    public void loadRules() {

        // 第一个泛型 String,就是应用的名字,orderApi,
        // 第二个泛型,就是这个应用对应的一组流量规则;
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource
                = new ZookeeperDataSource<>(zkServer,
                zkPath + "/" + appName,
                // source 就是从 zookeeper 中读出来的字符串
                source -> JSON.parseArray(source, FlowRule.class));

        // 把从 zookeeper 中读到的配置规则,写入内存
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }

}

重启之后,在 sentinel-dashboard 配置的规则就可以在 orderApi 中生效了,此时 sentinel-dashboard 和 orderApi 之间就没有直接通信了,两者通过 zookeeper 完成交互。