嵌入模式
代码示例
示例可以参见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
- 首先登陆sentinel控制台,会发现没有看到对应的应用,主要原因是埋点没有触发
- 分别请求localhost:8081/hello/zihao,localhost:8082/hello/zihao,localhost:8083/zihao
- 然后重新刷新sentinel控制台,在机器列表可以看到对应的机器实例
- 在流控规则那里,可以看到自己配置的流控规则
这里对原先的代码示例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
- 在集群流控那里,现在可能是空的,原因主要是因为我们的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通信的端口
}
]
- 然后刷新sentinel控制台,进入集群流控可以看到token server列表和token client列表
截屏2020-12-31 下午11.23.26.png
- 然后开始使用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'
- 查看实时监控面板的qps情况
查看csp的metric日志输出
截屏2021-01-01 下午7.57.05.png
- 对内嵌的token server也开始请求
wrk -c 5 -d 1000 -t 5 'http://localhost:8082/hello/zihao'
查看metric日志可以看到单台的qps下降到30左右
相比较不请求内嵌的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,截图如下。
这种模式配置有个缺点就是不能持久化,重新启动就会失效,通过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
- 启动sentinel控制台,查看机器列表
前面三个是token client, 最后一个是token server
- 点击实时监控,查看qps情况,现在设置的qps是30
查看csp的其中一台日志看到qps是10,因为总的qps设置的是30,每台是10
截屏2021-01-01 下午3.41.22.png
- 然后在nacos控制台,将appA-flow-rules的总qps设置为90,可以发现sentinel控制台和日志都变为90了
截屏2021-01-01 下午7.26.10.png
小结
- 独立模式下,集群每台服务器的qps分布均匀,总的qps调控也均匀
- token server目前是单节点,所以存在高可用的问题,所以生产情况下需要使用token server集群,或者可以发生故障的时候转移token server。
- 对于集群流控,个人看法是有一定复杂度的,不建议在业务系统使用集群流控,集群流控可以在网关层做,业务层的话可以使用单机流控相对来说简单好上手。
网友评论