Alibaba Sentinel 使用ZooKeeper集中管理

2020-08-11  本文已影响0人  xiaolyuh

如果不做任何修改,Sentinel Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中,这种方式规则保存在内存中,重启即消失不建议在线上使用,架构图如下:

image.png

Sentinel官方是建议使用推模式,这种方式规则是持久化的,服务重启不会消失;通过配置中心来保证规则的一致性;规则实时下发,响应速度快,架构图如下:

image.png

Sentinel 目前提供了ZooKeeper, Apollo, Nacos 等的动态数据源实现,但是为了使用第二种架构方式我们需要对原来的Dashboard进行一定的改造。

搭建ZooKeeper环境

ZooKeeper常用命令

1. 启动ZK服务:       sh bin/zkServer.sh start
2. 查看ZK服务状态: sh bin/zkServer.sh status
3. 停止ZK服务:       sh bin/zkServer.sh stop
4. 重启ZK服务:       sh bin/zkServer.sh restart

ZooInspector 图形化工具

ZooInspector是ZooKeeper的图形化工具,ZooInspector下载地址:

https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

解压,进入ZooInspector\build目录,通过如下命令执行jar包:

java -jar zookeeper-dev-ZooInspector.jar  & //执行成功后,会弹出java ui client
image.png

获取源码

https://github.com/alibaba/Sentinel

改造Dashboard工程

引入Zookeeper包

<!--for Zookeeper rule publisher sample-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>${curator.version}</version>
    <scope>test</scope>
</dependency>

去掉<scope>test</scope>

通过Zookeeper来同步

在test目录中,sentinel提供了使用Zookeeper来同步流控规则的实现,我们直接复制到com.alibaba.csp.sentinel.dashboard.rule包中,并根据样例写出降级规则的实现,目录如图:

image.png image.png

这里需要注意的一点是,流控规则和降级规则的zkpath:

// 流控规则
final String flowPath = "/sentinel_rule_config/" + appName + "/flow";

// 降级规则
final String degradePath = "/sentinel_rule_config/" + appName + "/degrade";

// 应用启动时指定的-Dproject.name=sentinel-demo参数
String appName = System.getProperty("project.name");

修改Controller,当规则变动时给Zookeeper发消息

FlowControllerV1 流控规则Controller

流控规则Controller可以参考FlowControllerV2类来修改,主要有两点修改:

  1. apiQueryMachineRules方法,当获取规则时,直接通过zk获取:
image.png
  1. publishRules方法,当规则变更时,通过zk做通知:
image.png

DegradeController 降级规则Controller

DegradeController的修改和FlowControllerV1修改的地方时一样的。

修改规则ID的ID生成器

这里算是有个小bug把,规则的ID生成器如InMemDegradeRuleStore使用的是private static AtomicLong ids = new AtomicLong(0);,这样如果Dashboard重新部署的话,就会导致生成规则的id又从0开始了,这样有可能会导致新创建规则的时候,会将老规则给覆盖掉,做如下修改:

image.png

InMemoryRuleRepositoryAdapter这个类的子类都有这个问题,可以一起修改。

然后打成jar包,通过如下命令启动:

-Dserver.port=8900 -Dcsp.sentinel.dashboard.server=localhost:8900 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456789

客户端的修改

适配框架

Sentinel 支持主流框架的适配。这里我适配的是Web Servlet

引入jar包的支持

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>${sentinel.version}</version>
</dependency>

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>${sentinel.version}</version>
</dependency>

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>${sentinel.version}</version>
</dependency>

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-zookeeper</artifactId>
    <version>${sentinel.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.14</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

通过Spring 配置过滤器

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        registration.setName("sentinelFilter");
        registration.setOrder(1);

        return registration;
    }
}

接入 CommonFilter 之后,所有访问的 Web URL 就会被自动统计为 Sentinel 的资源,可以针对单个 URL 维度进行流控。若希望区分不同 HTTP Method,可以将 HTTP_METHOD_SPECIFY 这个 init parameter 设为 true,给每个 URL 资源加上前缀,比如 GET:/foo。更多资料说明请查看文档

适配 Sentinel 动态数据源,支持规则的持久化

我这里主要适配了流控和降级的规则通知和持久化。

@Configuration
public class SentinelDataSourceInitFuncConfig {

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

    @PostConstruct
    public void initSentinelDataSourceInitFuncConfig() {

        String appName = StringUtils.isNotBlank(System.getProperty("project.name")) ? System.getProperty("project.name") : applicationName;

        final String remoteAddress = "127.0.0.1:2181";

        // 流控规则
        final String flowPath = "/sentinel_rule_config/" + appName + "/flow";
        ReadableDataSource<String, List<FlowRule>> redisFlowDataSource = new ZookeeperDataSource<>(remoteAddress, flowPath,
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                }));
        FlowRuleManager.register2Property(redisFlowDataSource.getProperty());

        // 降级规则
        final String degradePath = "/sentinel_rule_config/" + appName + "/degrade";
        ReadableDataSource<String, List<DegradeRule>> redisDegradeDataSource = new ZookeeperDataSource<>(remoteAddress, degradePath,
                source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
                }));
        DegradeRuleManager.register2Property(redisDegradeDataSource.getProperty());
    }
}

这里ZooKeeper的地址可以改成配置项。

业务系统埋点

Sentinel支持多种埋点方式,这里我只列举了对一段特定代码块进行埋点的方式,其他方式可以参考文档,源码如下:

@Override
public Result thread(String arg) {
    String resourceName = "testSentinel";
    int time = random.nextInt(700);
    ContextUtil.enter("entrance1", "appA");
    Entry entry = null;
    String retVal;
    try {
        entry = SphU.entry(resourceName, EntryType.IN);
        Thread.sleep(time);
        if (time > 690) {
            throw new RuntimeException("耗时太长啦");
        }

        retVal = "passed";
    } catch (BlockException e) {
        retVal = "blocked";
    } catch (Exception e) {
        // 异常数统计埋点
        Tracer.trace(e);
        throw new RuntimeException(e);
    } finally {
        if (entry != null) {
            entry.exit();
        }
    }
    return Result.success(retVal + "::" + time);
}

这里需求特别说明的是 EntryType,共有两种类型:INOUT

启动应用

应用启动时需要通过JVM启动参数指定Sentinel Dashboard的地址和端口,并且需要指定项目名称。

-Dcsp.sentinel.dashboard.server=127.0.0.1:8900 -Dproject.name=sentinel-demo
image.png

应用被注册到Dashboard上的效果

客户端配置好了与控制台的连接参数之后,并不会主动连接上控制台,需要触发一次客户端的规则才会开始进行初始化,并向控制台发送心跳和客户端规则等信息。

image.png

参考

在生产环境中使用-Sentinel

源码

https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases

spring-boot-student-sentinel 工程和spring-boot-student-sentinel-dashboard工程

上一篇下一篇

猜你喜欢

热点阅读