源码分析,当sentinel控制台修改规则时,会发送数据到UpdateGatewayRuleCommandHandler,其中WritableDataSource为null
- com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayRuleCommandHandler
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.adapter.gateway.common.command;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.net.URLDecoder;
import java.util.Set;
/**
* @author Eric Zhao
* @since 1.6.0
*/
@CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
private static WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds = null;
@Override
public CommandResponse<String> handle(CommandRequest request) {
String data = request.getParam("data");
if (StringUtil.isBlank(data)) {
return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
}
try {
data = URLDecoder.decode(data, "utf-8");
} catch (Exception e) {
RecordLog.info("Decode gateway rule data error", e);
return CommandResponse.ofFailure(e, "decode gateway rule data error");
}
RecordLog.info("[API Server] Receiving rule change (type: gateway rule): {}", data);
String result = SUCCESS_MSG;
Set<GatewayFlowRule> flowRules = JSON.parseObject(data, new TypeReference<Set<GatewayFlowRule>>() {
});
GatewayRuleManager.loadRules(flowRules);
if (!writeToDataSource(gatewayFlowWds, flowRules)) {
result = WRITE_DS_FAILURE_MSG;
}
return CommandResponse.ofSuccess(result);
}
/**
* Write target value to given data source.
*
* @param dataSource writable data source
* @param value target value to save
* @param <T> value type
* @return true if write successful or data source is empty; false if error occurs
*/
private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
//默认dataSource是null
if (dataSource != null) {
try {
dataSource.write(value);
} catch (Exception e) {
RecordLog.warn("Write data source failed", e);
return false;
}
}
return true;
}
public synchronized static WritableDataSource<Set<GatewayFlowRule>> getWritableDataSource() {
return gatewayFlowWds;
}
public synchronized static void setWritableDataSource(WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds) {
UpdateGatewayRuleCommandHandler.gatewayFlowWds = gatewayFlowWds;
}
private static final String SUCCESS_MSG = "success";
private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
}
WritableDataSource实现类只有FileWritableDataSource,那么类比实现一个NacosWritableDataSource,即可同步数据到nacos;
package com.example.config;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* NacosWritableDataSource
*
* @author: liuzhiguo
* @date: 2021/8/27 10:54
*/
public class NacosWritableDataSource<T> implements WritableDataSource<T> {
private final String groupId;
private final String dataId;
private final String serverAddr;
private ConfigService configService;
private final Converter<T, String> configEncoder;
private final Lock lock;
public NacosWritableDataSource(String serverAddr, String groupId, String dataId, Converter<T, String> configEncoder) {
this.lock = new ReentrantLock(true);
this.serverAddr = serverAddr;
this.groupId = groupId;
this.dataId = dataId;
this.configEncoder = configEncoder;
initConfigService();
}
private void initConfigService() {
try {
this.configService = NacosFactory.createConfigService(buildProperties(serverAddr));
} catch (NacosException e) {
e.printStackTrace();
}
}
private static Properties buildProperties(String serverAddr) {
Properties properties = new Properties();
properties.setProperty("serverAddr", serverAddr);
return properties;
}
@Override
public void write(T t) throws Exception {
this.lock.lock();
try {
configService.publishConfig(dataId, groupId, this.configEncoder.convert(t), ConfigType.JSON.getType());
} finally {
this.lock.unlock();
}
}
@Override
public void close() throws Exception {
}
}
给UpdateGatewayRuleCommandHandler注入WritableDataSource,可以基于SPI实现;
还可以实现接口CommandLineRunner,在项目启动后注入;
public class DataSourceInitFunc implements InitFunc {
@Override
public void init() throws Exception {
System.out.println("自定义写出源NacosWritableDataSource");
WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds = new NacosWritableDataSource<>("localhost:8848",
"DEFAULT_GROUP",
"gateway-start-gw-flow",
data -> JSONObject.toJSONString(data, true));
UpdateGatewayRuleCommandHandler.setWritableDataSource(gatewayFlowWds);
}
}
sentinel控制台修改规则,即可同步到nacos