Sentinel dashboard无法查询到应用的限流配置问题以及解决

一。问题引入

使用sentinle-dashboard控制台 项目整体升级后,发现控制台上无法看到流控规则了
在这里插入图片描述

之前的问题是无法注册上来 现在是注册上来了。结果看不到流控规则配置了。 关于注册不上来的问题,可以看另一篇文章
https://blog.csdn.net/a15835774652/article/details/132234943

项目的组件版本如下
spring cloud 2021.0.5
spring cloud alibaba 2021.0.5.0
spring boot 2.6.13

二。问题分析

  • 配置检查 无误
  • 控制台服务应用 检查 确认已经启动
  • 网络连接检查无误
  • 源码分析

首先我们打开检查- 网络 看下调用的接口

在这里插入图片描述
可以看到 接口地址为 http://localhost:8080//v1/flow/rules?app=local-gateway-service&ip=xxx&port=8720

然后打开源码 注意分支为release-1.8
业务代码 主要在 sentinel-dashboard module 中 控制类在 FlowControllerV1 中

com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1#apiQueryMachineRules

@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app,@RequestParam String ip,@RequestParam Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}if (port == null) {return Result.ofFail(-1, "port can't be null");}if (!appManagement.isValidMachineOfApp(app, ip)) {return Result.ofFail(-1, "given ip does not belong to given app");}try {List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("Error when querying flow rules", throwable);return Result.ofThrowable(-1, throwable);}
}

重点来看下 sentinelApiClient.fetchFlowRuleOfMachine的调用

com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient#fetchFlowRuleOfMachine

public List<FlowRuleEntity> fetchFlowRuleOfMachine(String app, String ip, int port) {List<FlowRule> rules = fetchRules(ip, port, FLOW_RULE_TYPE, FlowRule.class);if (rules != null) {return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule)).collect(Collectors.toList());} else {return null;}
}

接着调用 fetchRules 方法

com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient#fetchRules

private <T extends Rule> List<T> fetchRules(String ip, int port, String type, Class<T> ruleType) {return fetchItems(ip, port, GET_RULES_PATH, type, ruleType);
}

com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient#fetchItems

private <T> List<T> fetchItems(String ip, int port, String api, String type, Class<T> ruleType) {try {AssertUtil.notEmpty(ip, "Bad machine IP");AssertUtil.isTrue(port > 0, "Bad machine port");Map<String, String> params = null;if (StringUtil.isNotEmpty(type)) {params = new HashMap<>(1);params.put("type", type);}return fetchItemsAsync(ip, port, api, type, ruleType).get();} catch (InterruptedException | ExecutionException e) {logger.error("Error when fetching items from api: {} -> {}", api, type, e);return null;} catch (Exception e) {logger.error("Error when fetching items: {} -> {}", api, type, e);return null;}
}
private <T> CompletableFuture<List<T>> fetchItemsAsync(String ip, int port, String api,String type, Class<T> ruleType) {AssertUtil.notEmpty(ip, "Bad machine IP");AssertUtil.isTrue(port > 0, "Bad machine port");Map<String, String> params = null;if (StringUtil.isNotEmpty(type)) {params = new HashMap<>(1);params.put("type", type);}return executeCommand(ip, port, api, params, false).thenApply(json -> JSON.parseArray(json, ruleType));
}

看到这里基本明白这个逻辑了 本质上就是发起http请求 同时指定api 以及type参数 这里的api名称是getRules type为 flow

请求的地址 为localhost: 8719

接下来我们来看到客户端对该接口的处理

Client命令的处理主要在CommandCenterInitFunc 中

public class CommandCenterInitFunc implements InitFunc {@Overridepublic void init() throws Exception {CommandCenter commandCenter = CommandCenterProvider.getCommandCenter();if (commandCenter == null) {RecordLog.warn("[CommandCenterInitFunc] Cannot resolve CommandCenter");return;}commandCenter.beforeStart();commandCenter.start();RecordLog.info("[CommandCenterInit] Starting command center: "+ commandCenter.getClass().getCanonicalName());}
}

重点关注start方法 来看实现类 com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter#start

@Override
public void start() throws Exception {//cpu核心数int nThreads = Runtime.getRuntime().availableProcessors();this.bizExecutor = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),            new NamedThreadFactory("sentinel-command-center-service-executor", true),new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {CommandCenterLog.info("EventTask rejected");throw new RejectedExecutionException();}});//初始化任务Runnable serverInitTask = new Runnable() {int port;{try {port = Integer.parseInt(TransportConfig.getPort());} catch (Exception e) {port = DEFAULT_PORT;}}@Overridepublic void run() {boolean success = false;//获取serverSocketServerSocket serverSocket = getServerSocketFromBasePort(port);if (serverSocket != null) {CommandCenterLog.info("[CommandCenter] Begin listening at port " + serverSocket.getLocalPort());socketReference = serverSocket;executor.submit(new ServerThread(serverSocket));success = true;port = serverSocket.getLocalPort();} else {CommandCenterLog.info("[CommandCenter] chooses port fail, http command center will not work");}if (!success) {port = PORT_UNINITIALIZED;}TransportConfig.setRuntimePort(port);executor.shutdown();}};new Thread(serverInitTask).start();
}

存在个server初始化任务

任务中主要有几个步骤

1。创建socket连接

2。向executor线程池中提交任务 ServerThread

3。更新port 以及成功标识 success

4。关闭 executor

核心来看ServerThread的run方法

com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter.ServerThread#run

@Override
public void run() {while (true) {Socket socket = null;try {socket = this.serverSocket.accept();setSocketSoTimeout(socket);HttpEventTask eventTask = new HttpEventTask(socket);bizExecutor.submit(eventTask);} catch (Exception e) {CommandCenterLog.info("Server error", e);if (socket != null) {try {socket.close();} catch (Exception e1) {CommandCenterLog.info("Error when closing an opened socket", e1);}}try {// In case of infinite log.Thread.sleep(10);} catch (InterruptedException e1) {// Indicates the task should stop.break;}}}}
}

run方法中的核心逻辑

1。接收socket连接

2。提交HttpEventTask 任务

再来看HttpEventTask 任务的逻辑

com.alibaba.csp.sentinel.transport.command.http.HttpEventTask#run

@Override
public void run() {if (socket == null) {return;}PrintWriter printWriter = null;InputStream inputStream = null;try {long start = System.currentTimeMillis();inputStream = new BufferedInputStream(socket.getInputStream());OutputStream outputStream = socket.getOutputStream();printWriter = new PrintWriter(new OutputStreamWriter(outputStream, Charset.forName(SentinelConfig.charset())));String firstLine = readLine(inputStream);CommandCenterLog.info("[SimpleHttpCommandCenter] Socket income: " + firstLine+ ", addr: " + socket.getInetAddress());CommandRequest request = processQueryString(firstLine);if (firstLine.length() > 4 && StringUtil.equalsIgnoreCase("POST", firstLine.substring(0, 4))) {// Deal with post methodprocessPostRequest(inputStream, request);}// Validate the target command.String commandName = HttpCommandUtils.getTarget(request);if (StringUtil.isBlank(commandName)) {writeResponse(printWriter, StatusCode.BAD_REQUEST, INVALID_COMMAND_MESSAGE);return;}// Find the matching command handler.CommandHandler<?> commandHandler = SimpleHttpCommandCenter.getHandler(commandName);if (commandHandler != null) {CommandResponse<?> response = commandHandler.handle(request);handleResponse(response, printWriter);} else {// No matching command handler.writeResponse(printWriter, StatusCode.BAD_REQUEST, "Unknown command `" + commandName + '`');}long cost = System.currentTimeMillis() - start;CommandCenterLog.info("[SimpleHttpCommandCenter] Deal a socket task: " + firstLine+ ", address: " + socket.getInetAddress() + ", time cost: " + cost + " ms");} catch (RequestException e) {writeResponse(printWriter, e.getStatusCode(), e.getMessage());} catch (Throwable e) {CommandCenterLog.warn("[SimpleHttpCommandCenter] CommandCenter error", e);try {if (printWriter != null) {String errorMessage = SERVER_ERROR_MESSAGE;e.printStackTrace();if (!writtenHead) {writeResponse(printWriter, StatusCode.INTERNAL_SERVER_ERROR, errorMessage);} else {printWriter.println(errorMessage);}printWriter.flush();}} catch (Exception e1) {CommandCenterLog.warn("Failed to write error response", e1);}} finally {closeResource(inputStream);closeResource(printWriter);closeResource(socket);}
}

核心处理逻辑

1。读取socket 输入流 解析转化为CommandRequest

2。校验命令commandName

3。获取与命令匹配的处理器 CommandHandler

4。处理器处理该命令并写入响应

我们再来看下具体的命令处理器的匹配逻辑

首先是一个map结构 key是命令名称 值是CommandHandler 具体的命令处理器

private static final Map<String, CommandHandler> handlerMap = new ConcurrentHashMap<String, CommandHandler>();

因为我们需要排查流控为啥没有显示到控制台,dashboard console上调用请求的是getRules 的api, 所以我们来看下 getRules 对应的处理器

@CommandMapping(name = "getRules", desc = "get all active rules by type, request param: type={ruleType}")
public class FetchActiveRuleCommandHandler implements CommandHandler<String> {@Overridepublic CommandResponse<String> handle(CommandRequest request) {String type = request.getParam("type");if ("flow".equalsIgnoreCase(type)) {return CommandResponse.ofSuccess(JSON.toJSONString(FlowRuleManager.getRules()));} else if ("degrade".equalsIgnoreCase(type)) {return CommandResponse.ofSuccess(JSON.toJSONString(DegradeRuleManager.getRules()));} else if ("authority".equalsIgnoreCase(type)) {return CommandResponse.ofSuccess(JSON.toJSONString(AuthorityRuleManager.getRules()));} else if ("system".equalsIgnoreCase(type)) {return CommandResponse.ofSuccess(JSON.toJSONString(SystemRuleManager.getRules()));} else {return CommandResponse.ofFailure(new IllegalArgumentException("invalid type"));}}
}

dashboard中传入的类型 为flow 所以执行的逻辑如下

CommandResponse.ofSuccess(JSON.toJSONString(FlowRuleManager.getRules()));

由于我们使用的网关限流 使用是用 GatewayRuleManager.loadRules(rules); 进行加载规则 而不是使用的

FlowRuleManager.loadRules(rules); 那使用FlowRuleManager.getRules() 获取到的规则就是空的

所以控制台无法显示我们的规则

备注:sentinel整合gateway 中也存在一个个 获取规则的命令处理器 com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayRuleCommandHandler 类代码如下

@CommandMapping(name = "gateway/getRules", desc = "Fetch all gateway rules")
public class GetGatewayRuleCommandHandler implements CommandHandler<String> {@Overridepublic CommandResponse<String> handle(CommandRequest request) {return CommandResponse.ofSuccess(JSON.toJSONString(GatewayRuleManager.getRules()));}
}

所以我们使用这个命令名称即可加载我们网关中的规则

改动方案

  • 网关加载流控规则使用FlowRuleManager.loadRules(rules);
  • dashboard请求接口 使用gateway/getRules 而不是 getRules

第一个方案无法满足我们的需求,所以我们需要基本源码来改造 sentinel-dashboard 修改请求的API

本来以为到这里结束了,结果后面前端的源码中发现了问题
源码路径
sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.js
其中有段获取APP列表的方法 ,方法中会进行判断该应用是否是网关

// appAppService.getApps().success(function (data) {if (data.code === 0) {console.log(`data: ${data}`);let path = $location.path().split('/');let initHashApp = path[path.length - 1];$scope.apps = data.data;$scope.apps = $scope.apps.map(function (item) {if (item.app === initHashApp) {item.active = true;}let healthyCount = 0;for (let i in item.machines) {if (item.machines[i].healthy) {healthyCount++;}}item.healthyCount = healthyCount;// 重点在这里  处理appType Handle appTypeitem.isGateway = item.appType === 1 || item.appType === 11 || item.appType === 12;if (item.shown) {return item;}});}});

而这个是否为网关字段在 左边的菜单又会用到
业务代码位置
sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html

  <li ui-sref-active="active" ng-if="entry.isGateway"><a ui-sref="dashboard.gatewayFlow({app: entry.app})"><i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a></li><li ui-sref-active="active" ng-if="!entry.isGateway"><a ui-sref="dashboard.flow({app: entry.app})"><i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a></li>

可以看到如果是网关的话 那么就会触发dashboard.gatewayFlow路由页面 最后会跳转到
app/views/gateway/flow.html 其中会触发 flow.js 加载规则列表 方法如下

 this.queryRules = function (app, ip, port) {var param = {app: app,ip: ip,port: port};return $http({url: '/gateway/flow/list.json',params: param,method: 'GET'});};

最终调用的接口在 GatewayFlowRuleController里
com.alibaba.csp.sentinel.dashboard.controller.gateway.GatewayFlowRuleController
接口详细代码如下

@GetMapping("/list.json")
@AuthAction(AuthService.PrivilegeType.READ_RULE)
public Result<List<GatewayFlowRuleEntity>> queryFlowRules(String app, String ip, Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}if (port == null) {return Result.ofFail(-1, "port can't be null");}try {List<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("query gateway flow rules error:", throwable);return Result.ofThrowable(-1, throwable);}
}public CompletableFuture<List<GatewayFlowRuleEntity>> fetchGatewayFlowRules(String app, String ip, int port) {if (StringUtil.isBlank(ip) || port <= 0) {return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter"));}try {return executeCommand(ip, port, FETCH_GATEWAY_FLOW_RULE_PATH, false).thenApply(r -> {List<GatewayFlowRule> gatewayFlowRules = JSON.parseArray(r, GatewayFlowRule.class);List<GatewayFlowRuleEntity> entities = gatewayFlowRules.stream().map(rule -> GatewayFlowRuleEntity.fromGatewayFlowRule(app, ip, port, rule)).collect(Collectors.toList());return entities;});} catch (Exception ex) {logger.warn("Error when fetching gateway flow rules", ex);return AsyncUtils.newFailedFuture(ex);}
}

最终调用发起调用的地址就是 gateway/getRules

到这里就明白两个东西

  • 如果应用是网关的话 调用的获取规则列表地址接口是正确的
  • 我们这应该使用gateway应该属于网关才对。那目前来看 这个appType 应该不正确,或者是没正确被赋值

那我们接着来看这个 appType 为什么没有有被正确赋值

首先这个 appType是从接口中获取的 接口类为 app/briefinfos.json

接口地址在sentinel-dashboard项目 com.alibaba.csp.sentinel.dashboard.controller.AppController#queryAppInfos

@GetMapping("/briefinfos.json")
public Result<List<AppInfo>> queryAppInfos(HttpServletRequest request) {List<AppInfo> list = new ArrayList<>(appManagement.getBriefApps());Collections.sort(list, Comparator.comparing(AppInfo::getApp));return Result.ofSuccess(list);
}

重点是从appManagement.getBriefApps() 方法获取的 我们在看来下

com.alibaba.csp.sentinel.dashboard.discovery.AppManagement#getBriefApps

@Override
public Set<AppInfo> getBriefApps() {return machineDiscovery.getBriefApps();
}

具体的实现类是在SimpleMachineDiscovery

com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery#getBriefApps

@Override
public Set<AppInfo> getBriefApps() {return new HashSet<>(apps.values());
}

apps 是一个成员变量 那这个值又是从哪里赋值的呢 发现只有一个地方在进行赋值

就是添加的机器的方法

com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery#addMachine

@Override
public long addMachine(MachineInfo machineInfo) {AssertUtil.notNull(machineInfo, "machineInfo cannot be null");//不存在才进行加入 AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), o -> new AppInfo(machineInfo.getApp(), machineInfo.getAppType()));appInfo.addMachine(machineInfo);return 1;
}

在看下具体在哪里被调用

代码如下 com.alibaba.csp.sentinel.dashboard.controller.MachineRegistryController#receiveHeartBeat

@ResponseBody
@RequestMapping("/machine")
public Result<?> receiveHeartBeat(String app,@RequestParam(value = "app_type", required = false, defaultValue = "0")Integer appType, Long version, String v, String hostname, String ip,Integer port) {if (StringUtil.isBlank(app) || app.length() > 256) {return Result.ofFail(-1, "invalid appName");}if (StringUtil.isBlank(ip) || ip.length() > 128) {return Result.ofFail(-1, "invalid ip: " + ip);}if (!InetAddressUtils.isIPv4Address(ip) && !InetAddressUtils.isIPv6Address(ip)) {return Result.ofFail(-1, "invalid ip: " + ip);}if (port == null || port < -1) {return Result.ofFail(-1, "invalid port");}if (hostname != null && hostname.length() > 256) {return Result.ofFail(-1, "hostname too long");}if (port == -1) {logger.warn("Receive heartbeat from " + ip + " but port not set yet");return Result.ofFail(-1, "your port not set yet");}String sentinelVersion = StringUtil.isBlank(v) ? "unknown" : v;version = version == null ? System.currentTimeMillis() : version;try {MachineInfo machineInfo = new MachineInfo();machineInfo.setApp(app);machineInfo.setAppType(appType);machineInfo.setHostname(hostname);machineInfo.setIp(ip);machineInfo.setPort(port);machineInfo.setHeartbeatVersion(version);machineInfo.setLastHeartbeat(System.currentTimeMillis());machineInfo.setVersion(sentinelVersion);appManagement.addMachine(machineInfo);return Result.ofSuccessMsg("success");} catch (Exception e) {logger.error("Receive heartbeat error", e);return Result.ofFail(-1, e.getMessage());}
}

那这个接口又是什么时候被调用的呢

在客户端中 维护心跳的类是 HeartbeatSenderInitFunc

其中有个 定时发送心跳的方法

@Override
public void init() {HeartbeatSender sender = HeartbeatSenderProvider.getHeartbeatSender();if (sender == null) {RecordLog.warn("[HeartbeatSenderInitFunc] WARN: No HeartbeatSender loaded");return;}initSchedulerIfNeeded();long interval = retrieveInterval(sender);setIntervalIfNotExists(interval);//定时发送心跳任务scheduleHeartbeatTask(sender, interval);
}private void scheduleHeartbeatTask(/*@NonNull*/ final HeartbeatSender sender, /*@Valid*/ long interval) {pool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {//发送具体的心跳sender.sendHeartbeat();} catch (Throwable e) {RecordLog.warn("[HeartbeatSender] Send heartbeat error", e);}}}, 5000, interval, TimeUnit.MILLISECONDS);RecordLog.info("[HeartbeatSenderInit] HeartbeatSender started: "+ sender.getClass().getCanonicalName());
}

其中发送心跳的调用是sender.sendHeartbeat();

com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender#sendHeartbeat

@Override
public boolean sendHeartbeat() throws Exception {if (TransportConfig.getRuntimePort() <= 0) {RecordLog.info("[SimpleHttpHeartbeatSender] Command server port not initialized, won't send heartbeat");return false;}Endpoint addrInfo = getAvailableAddress();if (addrInfo == null) {return false;}SimpleHttpRequest request = new SimpleHttpRequest(addrInfo, TransportConfig.getHeartbeatApiPath());request.setParams(heartBeat.generateCurrentMessage());try {SimpleHttpResponse response = httpClient.post(request);if (response.getStatusCode() == OK_STATUS) {return true;} else if (clientErrorCode(response.getStatusCode()) || serverErrorCode(response.getStatusCode())) {RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addrInfo+ ", http status code: " + response.getStatusCode());}} catch (Exception e) {RecordLog.warn("[SimpleHttpHeartbeatSender] Failed to send heartbeat to " + addrInfo, e);}return false;
}

请求地址从配置csp.sentinel.heartbeat.api.path 没有配置的话 则使用 /registry/machine

那就这个流程就通了 断点来看下具体的参数

在这里插入图片描述

在这里插入图片描述
看看这个参数哪里配置

app_type为0的话 那这个就表示是非网关应用 那还是调用接口路径为getRules 加载规则 因为我们这个属于网关环境这个肯定获取不到

在HeartbeatMessage 构造中设置了 基础的配置 ip app_type app 等

public HeartbeatMessage() {message.put("hostname", HostNameUtil.getHostName());message.put("ip", TransportConfig.getHeartbeatClientIp());message.put("app", AppNameUtil.getAppName());// Put application type (since 1.6.0).message.put("app_type", String.valueOf(SentinelConfig.getAppType()));message.put("port", String.valueOf(TransportConfig.getPort()));
}

这里重点关注 SentinelConfig.getAppType()

com.alibaba.csp.sentinel.config.SentinelConfig#getAppType

public static int getAppType() {return appType;
}

发现appType 是个成员变量 默认值为0 另外 SentinelConfig 在初始化的时候 会对这个值 进行修改

com.alibaba.csp.sentinel.config.SentinelConfig#72

static {try {initialize();loadProps();//解析appNameresolveAppName();//解析appTyperesolveAppType();RecordLog.info("[SentinelConfig] Application type resolved: {}", appType);} catch (Throwable ex) {RecordLog.warn("[SentinelConfig] Failed to initialize", ex);ex.printStackTrace();}
}

com.alibaba.csp.sentinel.config.SentinelConfig#resolveAppType

private static void resolveAppType() {try {//配置中获取  csp.sentinel.app.typeString type = getConfig(APP_TYPE_PROP_KEY);if (type == null) {appType = APP_TYPE_COMMON;return;}appType = Integer.parseInt(type);if (appType < 0) {appType = APP_TYPE_COMMON;}} catch (Exception ex) {appType = APP_TYPE_COMMON;}
}

首先从 csp.sentinel.app.type 配置中获取 如果为空 那就用默认值- 并返回

如果不为空 直接赋值 如果小于0 则赋值给0

异常情况 使用默认值

那我们再来看下 这个配置 csp.sentinel.app.type 是在哪里进行赋值的

发现只有一个地方 是在 网关的自动配置中

com.alibaba.cloud.sentinel.gateway.scg.SentinelSCGAutoConfiguration#initAppType

private void initAppType() {System.setProperty(SentinelConfig.APP_TYPE_PROP_KEY,ConfigConstants.APP_TYPE_SCG_GATEWAY);
}

那为什么设置到System中 会生效到SentinelConfig的属性中

那是因为 在 解析APPType之前还会调用 loadProps();

private static void loadProps() {Properties properties = SentinelConfigLoader.getProperties();for (Object key : properties.keySet()) {setConfig((String) key, (String) properties.get(key));}
}

方法中 SentinelConfigLoader.getProperties(); 触发SentinelConfigLoader构造

static {try {load();} catch (Throwable t) {RecordLog.warn("[SentinelConfigLoader] Failed to initialize configuration items", t);}
}private static void load() {// Order: system property -> system env -> default file (classpath:sentinel.properties) -> legacy pathString fileName = System.getProperty(SENTINEL_CONFIG_PROPERTY_KEY);if (StringUtil.isBlank(fileName)) {fileName = System.getenv(SENTINEL_CONFIG_ENV_KEY);if (StringUtil.isBlank(fileName)) {fileName = DEFAULT_SENTINEL_CONFIG_FILE;}}Properties p = ConfigUtil.loadProperties(fileName);if (p != null && !p.isEmpty()) {RecordLog.info("[SentinelConfigLoader] Loading Sentinel config from {}", fileName);properties.putAll(p);}for (Map.Entry<Object, Object> entry : new CopyOnWriteArraySet<>(System.getProperties().entrySet())) {String configKey = entry.getKey().toString();String newConfigValue = entry.getValue().toString();String oldConfigValue = properties.getProperty(configKey);properties.put(configKey, newConfigValue);if (oldConfigValue != null) {RecordLog.info("[SentinelConfigLoader] JVM parameter overrides {}: {} -> {}",configKey, oldConfigValue, newConfigValue);}}
}

会加载到所有的System配置 会存入到SentinelConfigLoader的 properties成员变量中

分析到这里 感觉没啥问题

断点查看 发现了问题

在这里插入图片描述
在这里插入图片描述
可以看当网关还没设置app_type时 这个sentinelConfig已经被加载,对应的SentinelConfigLoader的properties 已经被加载 这时候System中属性还未设置。所以后面网关在设置app_type后,也无法生效了。因为配置已经初始化完成。

那这个解决的办法也就明朗了

  • 项目启动前手动设置 app_type 对应的配置项为 csp.sentinel.app.type

    • 可以在项目启动类设置

      public class GatewayServiceApplication {public static void main(String[] args) {System.setProperty("csp.sentinel.dashboard.server","localhost:8080");System.setProperty("csp.sentinel.app.name","local-gateway-service");System.setProperty("csp.sentinel.app.type","11");SpringApplication.run(GatewayServiceApplication.class, args);}}
      
    • jvm参数中添加 -Dcsp.sentinel.app.type=11 启动参数

同样这个问题本质还是sentinelConfig被提前初始化,导致后面设置的配置项都无法生效了

至于为什么升级了版本后 sentinelConfig会被提前初始化。一方面等待官方的回答,另一方面我们也自行寻找。
https://github.com/alibaba/Sentinel/issues/3201


good day

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/106003.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java之ApI之Math类详解

1 Math类 1.1 概述 tips&#xff1a;了解内容 查看API文档&#xff0c;我们可以看到API文档中关于Math类的定义如下&#xff1a; Math类所在包为java.lang包&#xff0c;因此在使用的时候不需要进行导包。并且Math类被final修饰了&#xff0c;因此该类是不能被继承的。 Math类…

ARM开发,stm32mp157a-A7核PWM实验(驱动蜂鸣器,风扇,马达工作)

1.分析框图&#xff1b; 2.比较捕获寄存器&#xff08;产生PWM方波&#xff09;&#xff1b; 工作原理&#xff1a; 1、系统提供一个时钟源209MHZ&#xff0c;需要通过分频器进行分频&#xff0c;设置分频器值为209分频&#xff1b; 2、当定时器启动之后&#xff0c;自动重载…

编码基础一:侵入式链表

一、简介概述 1、普通链表数据结构 每个节点的next指针指向下一个节点的首地址。这样会有如下的限制&#xff1a; 一条链表上的所有节点的数据类型需要完全一致。对某条链表的操作如插入&#xff0c;删除等只能对这种类型的链表进行操作&#xff0c;如果链表的类型换了&#…

巨人互动|Facebook海外户Facebook游戏全球发布实用策略

Facebook是全球最大的社交媒体平台之一&#xff0c;拥有庞大的用户基数和广阔的市场。对于游戏开发商而言&#xff0c;利用Facebook进行全球发布是一项重要的策略。下面小编将介绍一些实用的策略帮助开发商在Facebook上进行游戏全球发布。 巨人互动|Facebook海外户&Faceboo…

visual studio 2022.NET Core 3.1 未显示在目标框架下拉列表中

问题描述 在Visual Studio 2022我已经安装了 .NET core 3.1 并验证可以运行 .NET core 3.1 应用程序&#xff0c;但当创建一个新项目时&#xff0c;目标框架的下拉列表只允许 .NET 6.0和7.0。而我在之前用的 Visual Studio 2019&#xff0c;可以正确地添加 .NET 核心项目。 …

【【萌新的STM32学习-17 中断的基本概念2】】

萌新的STM32学习-17 中断的基本概念2 STM32中断优先级的基本概念 抢占优先级&#xff1a; 高抢占优先级可以打断正在执行的低抢占优先级中断 响应优先级&#xff1a; 这个也叫子优先级 抢占优先级相同&#xff0c;响应优先级高的中断不能打断响应优先级低的中断。还有一种情况…

华为OD机试 - 字符串筛选排序 - 数组(Java 2022 Q4 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

深度学习优化入门:Momentum、RMSProp 和 Adam

目录 深度学习优化入门&#xff1a;Momentum、RMSProp 和 Adam 病态曲率 1牛顿法 2 Momentum:动量 3Adam 深度学习优化入门&#xff1a;Momentum、RMSProp 和 Adam 本文&#xff0c;我们讨论一个困扰神经网络训练的问题&#xff0c;病态曲率。 虽然局部极小值和鞍点会阻碍…

【深度学习】实验02 鸢尾花数据集分析

文章目录 鸢尾花数据集分析决策树K-means 鸢尾花数据集分析 决策树 # 导入机器学习相关库 from sklearn import datasets from sklearn import treeimport matplotlib.pyplot as plt import numpy as np# Iris数据集是常用的分类实验数据集&#xff0c; # 由Fisher, 1936收集…

NSSCTF——Web题目2

目录 一、[HNCTF 2022 Week1]2048 二、[HNCTF 2022 Week1]What is Web 三、[LitCTF 2023]1zjs 四、[NCTF 2018]签到题 五、[SWPUCTF 2021 新生赛]gift_F12 一、[HNCTF 2022 Week1]2048 知识点&#xff1a;源代码审计 解题思路&#xff1a; 1、打开控制台&#xff0c;查看…

Android中的APK打包与安全

aapt2命令行实现apk打包 apk文件结构 classes.dex&#xff1a;Dex&#xff0c;即Android Dalvik执行文件 AndroidManifest.xml&#xff1a;工程中AndroidManifest.xml编译后得到的二进制xml文件 META-INF&#xff1a;主要保存各个资源文件的SHA1 hash值&#xff0c;用于校验…

【Linux】【驱动】驱动挂载的时候给驱动传递参数

【Linux】【驱动】驱动挂载的时候给驱动传递参数 绪论1.什么是驱动传参驱动传参就是传递参数给我们的驱动举例:2.驱动传参数有什么作用呢?3. 传递单个参数使用如下的数组4. 传递数组使用以下函数&#xff1a; 传递数字值代码指令 传递数组代码传递数组指令 绪论 1.什么是驱动…

服务器中了mkp勒索病毒该怎么办?勒索病毒解密,数据恢复

mkp勒索病毒算的上是一种比较常见的勒索病毒类型了。它的感染数量上也常年排在前几名的位置。所以接下来就由云天数据恢复中心的技术工程师来对mkp勒索病毒做一个分析&#xff0c;以及中招以后应该怎么办。 一&#xff0c;中了mkp勒索病毒的表现 桌面以及多个文件夹当中都有一封…

【JAVA】抽象类与接口--下

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 抽象类与接口 1. 实现多个接口2. 接口间…

开源的安全性:挑战与机会

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

MR混合现实实训教学系统演示

MR混合现实实训教学系统的应用场景&#xff1a; 1、汽车检测与维修 使用MR混合现实技术&#xff0c;学生可以通过虚拟头戴式设备&#xff0c;将课堂延伸到实地。例如&#xff0c;在汽车维修课程中&#xff0c;学生可以通过MR技术&#xff0c;熟悉汽车模型内部关键结构&#x…

简析SCTP开发指南

目录 前言一、SCTP基本概念二、SCTP开发步骤1. **环境配置**&#xff1a;2. **建立Socket**&#xff1a;3. **绑定和监听**&#xff1a;4. **接收和发送数据**&#xff1a;5. **关闭连接**&#xff1a; 三、 C语言实现SCTP3.1SCTP客户端代码&#xff1a;3.2 SCTP服务器端代码&a…

24 | 紧跟时代步伐:微服务模式下API测试要怎么做?

微服务架构&#xff08;Microservice Architecture&#xff09; 微服务是一种架构风格。在微服务架构下&#xff0c;一个大型复杂软件系统不再由一个单体组成&#xff0c;而是由一系列相互独立的微服务组成。其中&#xff0c;各个微服务运行在自己的进程中&#xff0c;开发和部…

Java的锁大全

Java的锁 各种锁的类型 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念&#xff0c;体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。 先说概念。对于同一个数据的并发操作&#xff0c;悲观锁认为自己在使用数据的时候一定有别的线程来修改数…

wordpress搭建博客教程总结_01主题配置

目前搭建效果站 薪火相传 (yuetan.wang) 日间 月间 爱心 页面、 爱心冲击波! (yuetan.wang) 好看的主题分享 个人博客主题分享(WordPress) – Echo小窝 博客主要效果 1. Argon