美文网首页服务监控和治理
sentinel集群流控实践

sentinel集群流控实践

作者: 捞月亮的阿汤哥 | 来源:发表于2021-01-01 20:53 被阅读0次

    嵌入模式

    代码示例

    示例可以参见sentinel-demo-cluster-embedded

    代码说明

    通过spi初始化

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

    InitFunc实现类

    initDynamicRuleProperty 初始化动态规则

    这个主要目的是通过动态数据源的方式配置sentinel的流量控制和热点参数限流的规则。flowDataId和paramDataId的命名是appB-flow-rules和appB-param-rules(这个需要和registerClusterRuleSupplier的parser对应,appB看作是namespace)
    代码

    private void initDynamicRuleProperty() {
        ReadableDataSource<String, List<FlowRule>> ruleSource = new NacosDataSource<>(remoteAddress, groupId,
            flowDataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
        FlowRuleManager.register2Property(ruleSource.getProperty());
    
        ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(remoteAddress, groupId,
            paramDataId, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
        ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
     }
    

    动态配置
    打开nacos的控制台,新建一个配置,group填SENTINEL_GROUP, dataId填写appB-flow-rules
    ,配置内容选择json格式,示例如下。其实就是上面代码的FlowRule的各项属性值写成json格式,因为解析的是List<FlowRule>,所以配置是个json数组

    [
        {
            "resource" : "resource-1", //资源名 这里只是为了注释实际json格式不允许写注释
            "grade" : 1, //具体见RuleConstant 线程数限流0,qps限流1,默认1
            "count" : 3, //限流阀值                  
            "clusterMode" :  true, //是否是集群模式
            "clusterConfig" : { //下面是集群模式的配置 见ClusterRuleConstant
                "flowId" : 112, //规则id 唯一,github官网wiki建议管控端生成或者db生成
                "thresholdType" : 1,//0是单机均摊,1是全局阀值
                "fallbackToLocalWhenFail" : true //如果没引入依赖或者通信失败退化为单机流控
            }
        }
    ]
    

    initClientConfigProperty

    代码

    private void initClientConfigProperty() {
        ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(remoteAddress, groupId,
            configDataId, source -> JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {}));
        ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
    }
    

    动态配置
    打开nacos的控制台,新建一个配置,group填SENTINEL_GROUP, dataId填写appB-cluster-client-config
    ,配置内容选择json格式,示例如下。对应代码的ClusterClientConfig

    {
        "requestTimeout": 20
    }
    

    initClientServerAssignProperty

    代码

    private void initClientServerAssignProperty() {
        ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(remoteAddress, groupId,
            clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {});
            return Optional.ofNullable(groupList)
                .flatMap(this::extractClientAssignment)
                .orElse(null);
        });
        ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
    }
    

    动态配置
    打开nacos的控制台,新建一个配置,group填SENTINEL_GROUP, dataId填写appB-cluster-map
    ,配置内容选择json格式,示例如下。对应代码的ClusterGroupEntity

    [
        {
            "clientSet": [
                "10.32.35.18@8729",
                "10.32.35.18@8727"
            ],
            "ip": "10.32.35.18",
            "machineId": "10.32.35.18@8728",
            "port": 18730 //这个是token通信端口,和csp.sentinel.api.port区分开
        }
    ]
    

    registerClusterRuleSupplier

    代码

    private void registerClusterRuleSupplier() {
        // Register cluster flow rule property supplier which creates data source by namespace.
        // Flow rule dataId format: ${namespace}-flow-rules
        ClusterFlowRuleManager.setPropertySupplier(namespace -> {
            ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
                namespace + DemoConstants.FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
            return ds.getProperty();
        });
            
        // Register cluster parameter flow rule property supplier which creates data source by namespace.
        ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
            ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
                 namespace + DemoConstants.PARAM_FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
            return ds.getProperty();
        });
    }
    

    动态配置
    具体的json配置和之前的appB-flow-rules一样,这个是集群的流控规则,比如限制整个集群的流控阀值是30qps。比如之前的叫做appB-flow-rules,那么namespace是appB, 启动的时候需要添加-Dproject.name=appB

    initServerTransportConfigProperty

    代码

    private void initServerTransportConfigProperty() {
        ReadableDataSource<String, ServerTransportConfig> serverTransportDs = new NacosDataSource<>(remoteAddress, groupId,
            clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {});
            return Optional.ofNullable(groupList)
                .flatMap(this::extractServerTransportConfig)
                .orElse(null);
        });
       ClusterServerConfigManager.registerServerTransportProperty(serverTransportDs.getProperty());
    }
    

    **
    动态配置
    具体的json配置同之前的appB-cluster-map一样

    initStateProperty

    代码

    private void initStateProperty() {
        ReadableDataSource<String, Integer> clusterModeDs = new NacosDataSource<>(remoteAddress, groupId,
            clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {});
            return Optional.ofNullable(groupList)
                .map(this::extractMode)
                .orElse(ClusterStateManager.CLUSTER_NOT_STARTED);
        });
        ClusterStateManager.registerProperty(clusterModeDs.getProperty());
    }
    

    动态配置
    具体的json配置同之前的appB-cluster-map一样

    启动参数

    • idea启动三个示例,可以通过copy configuration快捷键快速复制配置,添加vm参数

    第一个实例

    -Dproject.name=appB
    -Dserver.port=8081
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.api.port=8727
    -Dcsp.sentinel.log.use.pid=true
    

    第二个实例

    -Dproject.name=appB
    -Dserver.port=8082
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.api.port=8728
    -Dcsp.sentinel.log.use.pid=true
    

    第三个实例

    -Dproject.name=appB
    -Dserver.port=8083
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.api.port=8729
    -Dcsp.sentinel.log.use.pid=true
    

    实验1 通过配置动态调整

    配置内容见initFunc实现类中具体的json说明,分别是appB-flow-rules,appB-cluster-map,appB-cluster-client-config

    1. 首先登陆sentinel控制台,会发现没有看到对应的应用,主要原因是埋点没有触发
    2. 分别请求localhost:8081/hello/zihao,localhost:8082/hello/zihao,localhost:8083/zihao
    3. 然后重新刷新sentinel控制台,在机器列表可以看到对应的机器实例
    截屏2020-12-31 下午11.11.45.png截屏2020-12-31 下午11.11.45.png
    1. 在流控规则那里,可以看到自己配置的流控规则

    这里对原先的代码示例DemoService的@SentinelResource注解指定了resource为resource-1

    @SentinelResource(value = "resource-1",blockHandler = "sayHelloBlockHandler")
    public String sayHello(String name) {
        return "Hello, " + name;
    }
    
    截屏2020-12-31 下午11.13.57.png截屏2020-12-31 下午11.13.57.png
    1. 在集群流控那里,现在可能是空的,原因主要是因为我们的ip可能发生了变化,所以需要把ip调整为机器列表的配置,在nacos控制台把appB-cluster-map的配置调整下,然后重新发布
    [
        {
            "clientSet": [
                "192.168.70.176@8729",
                "192.168.70.176@8727"
            ],
            "ip": "192.168.70.176",
            "machineId": "192.168.70.176@8728",
            "port": 18730 //这个端口是token server通信的端口
        }
    ]
    
    1. 然后刷新sentinel控制台,进入集群流控可以看到token server列表和token client列表
    截屏2020-12-31 下午11.20.42.png截屏2020-12-31 下午11.20.42.png
    截屏2020-12-31 下午11.23.26.png截屏2020-12-31 下午11.23.26.png
    1. 然后开始使用wrk对流量进行压力测试
    • 首先将流量阀值设置为90,然后使用wrk对两个token client节点请求
     wrk -c 5 -d 1000 -t 5 'http://localhost:8081/hello/zihao'
    
     wrk -c 5 -d 1000 -t 5 'http://localhost:8083/hello/zihao'
    
    1. 查看实时监控面板的qps情况
    截屏2021-01-01 下午7.56.22.png截屏2021-01-01 下午7.56.22.png

    查看csp的metric日志输出


    截屏2021-01-01 下午7.57.05.png截屏2021-01-01 下午7.57.05.png
    1. 对内嵌的token server也开始请求
    wrk -c 5 -d 1000 -t 5 'http://localhost:8082/hello/zihao'
    

    查看metric日志可以看到单台的qps下降到30左右

    截屏2021-01-01 下午8.00.20.png截屏2021-01-01 下午8.00.20.png
    相比较不请求内嵌的server token,可以发现qps波动非常大。

    小结

    • 嵌入模式的统计感觉不准确
    • 特别是对嵌入的server token请求的时候,qps的值会很高,达不到目标的流控效果。所以个人建议内嵌模式的时候尽量不要server token在承担token分发外,还需要处理别的请求
    • 对于嵌入的server token自身也带来了流量压力, 不过可以设置qps阀值保护embeded token server

    实验2 通过控制台指定token server和token client

    首先移除原先的集群配置。
    点击集群流控,点击右上角的新增token server。选择对应的token server和client,截图如下。

    截屏2021-01-01 上午12.45.26.png截屏2021-01-01 上午12.45.26.png
    这种模式配置有个缺点就是不能持久化,重新启动就会失效,通过nacos配置的话就不会有这个问题。

    独立模式

    代码示例

    示例可以参见sentinel-demo-cluster-server-alone

    代码说明

    服务端

    注意点

    sentinel-demo-cluster-server-alone示例下的ClusterServerDemo中手动指定了namespace set和server transport config,把这段代码注释掉,然后使用动态配置。如果不注释的话,不要添加cluster-server-namespace-set和cluster-server-transport-config的动态配置

    public static void main(String[] args) throws Exception {
            // Not embedded mode by default (alone mode).
            ClusterTokenServer tokenServer = new SentinelDefaultTokenServer();
    
            /*ClusterServerConfigManager.loadGlobalTransportConfig(new ServerTransportConfig()
                .setIdleSeconds(600)
                .setPort(11111));
            ClusterServerConfigManager.loadServerNamespaceSet(Collections.singleton(DemoConstants.APP_NAME));*/
    
            // Start the server.
            tokenServer.start();
    }
    

    registerClusterRuleSupplier

    主要目的是注册集群流控的规则,和嵌入模式的registerClusterRuleSupplier代码是一致的
    代码

    ClusterFlowRuleManager.setPropertySupplier(namespace -> {
        ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
            namespace + DemoConstants.FLOW_POSTFIX,
            source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
        return ds.getProperty();
    });
     // Register cluster parameter flow rule property supplier.
     ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
          ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(remoteAddress, groupId,
          namespace + DemoConstants.PARAM_FLOW_POSTFIX,
            source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
         return ds.getProperty();
    });
    

    动态配置
    打开nacos的控制台,新建一个配置,group填SENTINEL_GROUP, dataId填写appA-flow-rules
    ,配置内容选择json格式,示例如下。其实就是上面代码的FlowRule的各项属性值写成json格式,因为解析的是List<FlowRule>,所以配置是个json数组

    [
        {
            "resource" : "cluster-resource",
            "grade" : 1,
            "count" : 30,                   
            "clusterMode" :  true,
            "clusterConfig" : {
                "flowId" : 111,
                "thresholdType" : 1,      
                "fallbackToLocalWhenFail" : true
            }
        }
    ]
    

    _

    namespace set

    代码

    ReadableDataSource<String, Set<String>> namespaceDs = new NacosDataSource<>(remoteAddress, groupId,
        namespaceSetDataId, source -> JSON.parseObject(source, new TypeReference<Set<String>>() {}));
    ClusterServerConfigManager.registerNamespaceSetProperty(namespaceDs.getProperty());
    

    动态配置
    groupId填写SENTINEL_GROUP, dataId选择cluster-server-namespace-set

    ["appA"]
    

    initServerTransportConfigProperty

    代码

    ReadableDataSource<String, ServerTransportConfig> transportConfigDs = new NacosDataSource<>(remoteAddress,
        groupId, serverTransportDataId,
        source -> JSON.parseObject(source, new TypeReference<ServerTransportConfig>() {}));
    ClusterServerConfigManager.registerServerTransportProperty(transportConfigDs.getProperty());
    

    动态配置
    groupId填写SENTINEL_GROUP, dataId选择cluster-server-transport-config

    {
        "port": 11111,
        "idleSeconds": 600
    }
    

    客户端

    原先的代码示例没有client的demo,我这边仿照了embeded模式的写了一个
    代码

    @Override
    public void init() throws Exception {
        //指定其为集群的客户端,因为不需要将其从客户端变成服务端
        ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
    
        initDynamicRuleProperty();
        initClientServerAssignProperty();
        initClientConfigProperty();
    }
    

    动态配置

    • appA-flow-rules同上
    • appA-cluster-client-config
    {
        "requestTimeout":20
    }
    
    • appA-cluster-map
    {
        "serverHost":"192.168.70.176", //独立的token server地址
        "serverPort":11111
    }
    

    启动参数

    token server的启动参数

    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.log.use.pid=true
    -Dcsp.sentinel.api.port=8788
    -Dproject.name=appA
    

    token client的启动参数

    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.log.use.pid=true
    -Dcsp.sentinel.api.port=8721
    -Dproject.name=appA
    
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.log.use.pid=true
    -Dcsp.sentinel.api.port=8722
    -Dproject.name=appA
    
    -Dcsp.sentinel.dashboard.server=127.0.0.1:8080
    -Dcsp.sentinel.log.use.pid=true
    -Dcsp.sentinel.api.port=8723
    -Dproject.name=appA
    

    实验1 通过配置动态调整

    配置内容见之前的说明,配置分别是appA-flow-rules,appA-cluster-map,appA-cluster-client-config,cluster-server-transport-config,
    cluster-namespace-set

    1. 启动sentinel控制台,查看机器列表
    截屏2021-01-01 下午3.28.34.png截屏2021-01-01 下午3.28.34.png

    前面三个是token client, 最后一个是token server

    1. 点击实时监控,查看qps情况,现在设置的qps是30
    截屏2021-01-01 下午3.40.16.png截屏2021-01-01 下午3.40.16.png

    查看csp的其中一台日志看到qps是10,因为总的qps设置的是30,每台是10


    截屏2021-01-01 下午3.41.22.png截屏2021-01-01 下午3.41.22.png
    1. 然后在nacos控制台,将appA-flow-rules的总qps设置为90,可以发现sentinel控制台和日志都变为90了
    截屏2021-01-01 下午7.25.43.png截屏2021-01-01 下午7.25.43.png
    截屏2021-01-01 下午7.26.10.png截屏2021-01-01 下午7.26.10.png

    小结

    1. 独立模式下,集群每台服务器的qps分布均匀,总的qps调控也均匀
    2. token server目前是单节点,所以存在高可用的问题,所以生产情况下需要使用token server集群,或者可以发生故障的时候转移token server。
    3. 对于集群流控,个人看法是有一定复杂度的,不建议在业务系统使用集群流控,集群流控可以在网关层做,业务层的话可以使用单机流控相对来说简单好上手。

    参考资料

    相关文章

      网友评论

        本文标题:sentinel集群流控实践

        本文链接:https://www.haomeiwen.com/subject/hjtsoktx.html