目录
- 1 项目背景
- 2 github项目链接
- 3 应用启动阶段
- 4 控制台修改配置
1 项目背景
sentinel没有提供规则持久化的线上实现,只是提供了实现思路。除了api方式外,其他方案都需要添加比较多的代码修改,包括dashboard源码和应用。为避免如此繁琐的行为,采用API+PUSH混合方式实现sentinel zookeeper的持久化。
2 github项目链接
https://github.com/chlInGithub/sentienlzkdatabase
3 应用启动阶段
3.1 初始化zkClient
CuratorFrameworkFactory.newClient的第二个参数retryPolicy,是指在连接ZK服务过程中重新连接的策略.在它的实现类ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries)中,baseSleepTimeMs参数代表两次连接的等待时间,maxRetries参数表示最大的尝试连接次数。
3.2 调用zookeeperDataSource4Flow方法
3.2.1 ZookeeperDataSource实例化
3.2.1.1 initZookeeperListener(serverAddr, authInfos)方法
3.2.1.1.1 添加节点监听nodeChanged
内部调用loadConfig方法,完成从zk读取数据源,并进行格式转换成List类型。
然后加载到内存中:getProperty().updateValue(newValue);
3.2.1.1.2 NodeCache实例化及添加监听
NodeCache作用:使用节点数据作为本地缓存使用。这个类可以对节点进行监听,能够处理节点的增删改事件,数据同步等。 还可以通过注册自定义监听器来更细节的控制这些数据变动操作。
NodeCache实例化:
this.nodeCache = new NodeCache(this.zkClient, this.path);
NodeCache 添加监听:
this.nodeCache.getListenable().addListener(this.listener, ZookeeperDataSource.pool);
ZookeeperDataSource.pool:
public static final ExecutorService pool = new ThreadPoolExecutor(8, 16, 0, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue(100), new NamedThreadFactory(“sentinel-zookeeper-ds-update”),
new ThreadPoolExecutor.DiscardOldestPolicy());
3.2.1.2 loadInitialConfig() 方法
3.2.1.2.1 loadConfig()方法
调用loadConfig()方法,首先调用readSource()方法从zookeeper里面加载配置信息。
configInfo信息:
[
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 5,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterMode": false,
"controlBehavior": 0,
"count": 20,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 3,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 2,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
}
]
将数据进行转换并返回
convert实际调用的方法:
3.2.1.2.2 更新内存配置信息
调用DynamicSentinelProperty的updateValue方法,遍历监听器更新本地配置,但由于此时监听器数量为0,所以不会进行配置更新的操作。限流规则将赋值给value,而此时的listeners属性(Set<PropertyListener> listeners = new CopyOnWriteArraySet()),大小仍然为0。
3.2.2 FlowRuleManager.register2Property(flowRuleDataSource.getProperty())
3.2.2.1 flowRuleDataSource.getProperty()
获取DynamicSentinelProperty的实例:
3.2.2.2 初始化FlowRuleManager
1.调用currentProperty.addListener(LISTENER);此处的LISTENER为FlowRuleManager.FlowPropertyListener。如下图所示:
addListener方法内部做了2步操作,1是将当前的监听器FlowRuleManager.FlowPropertyListener添加到set集合中
2是调用listener.configLoad(this.value)方法,对此监听器进行配置加载。但由于value未初始化,为null。所以此步骤相当于没做任何操作。
3.2.2.3 register2Property方法
1.从currentProperty里面把FlowRuleManager.FlowPropertyListener这个监听器移除:
currentProperty.removeListener(LISTENER);
2.对当前传进来的实例DynamicSentinelProperty,添加监听:property.addListener(LISTENER);
此方法主要做了2步操作,1是将当前FlowRuleManager.FlowPropertyListener这个监听器添加到set集合中
2是调用listener.configLoad(this.value)方法,完成配置加载到本地:
3.将property赋值给currentProperty
3.2.3 modifyFlowRuleManager(zkClient, zookeeperDataSource, path)
3.2.3.1 ZKPropertyListener初始化
3.2.3.2 zookeeperDataSource.getProperty().addListener(flowPropertyListener)
调用ZKPropertyListener的configLoad方法
4 控制台修改配置
4.1 触发节点更新nodeChanged 方法
4.1.1 loadConfig方法(同3.2.1.2.1)
4.1.2 更新内存配置信息
4.1.2.1 更新DynamicSentinelProperty实例的value属性
调用DynamicSentinelProperty的updateValue方法。此方法首先是把最新的限流规则赋值给了value:
4.1.2.2 遍历调用监听器的configUpdate方法
其次获取DynamicSentinelProperty实例的监听器listeners:ZKPropertyListener以及FlowRuleManager.FlowPropertyListener
然后依次调用其configUpdate方法,将最新的配置加载到内存中:
4.1.2.2.1 FlowRuleManager.FlowPropertyListener:
4.1.2.2.2 ZKPropertyListener:
4.1.2.2.2.1 initPath:
创建存储规则的path,此处的path为 /SENTINEL-RULE/apiGateway/FLOW-DATA
4.1.2.2.2.2 getLockPath:
此处的lockPath为:
/SENTINEL-RULE/apiGateway/FLOW-DATA/lockHashCode-1656893730
4.1.2.2.2.3 gainLock:
确保path数据只被一个app node负责更新
4.1.2.2.2.4 避免重复修改:
判断新值和旧值是否会相等,相等则直接返回,避免重复修改
4.1.2.2.2.5 最新的规则,保存到zk:
先获取最新的规则数据:
此处的data为:
[
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 5,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 1,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterMode": false,
"controlBehavior": 0,
"count": 20,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
},
{
"clusterConfig": {
"acquireRefuseStrategy": 0,
"clientOfflineTime": 2000,
"fallbackToLocalWhenFail": true,
"resourceTimeout": 2000,
"resourceTimeoutStrategy": 0,
"sampleCount": 10,
"strategy": 0,
"thresholdType": 0,
"windowIntervalMs": 1000
},
"clusterMode": false,
"controlBehavior": 0,
"count": 3,
"grade": 1,
"limitApp": "default",
"maxQueueingTimeMs": 500,
"resource": "HelloWorld",
"strategy": 0,
"warmUpPeriodSec": 10
}
]
最新的数据保存到zk:
4.1.2.2.2.6 releaseLock:
释放lock: