Alibaba Sentinel对接Spring Cloud Gateway关于不显示API管理及请求链路的坑附带解决方案

在调试Sentinel对接Scg网关的时候,出现了一个比较坑的现象,就是网关菜单,不显示请求链路和API管理的菜单项,这个问题主要是由于,Sentinel的dashboard前端sidebar.js的 代码逻辑中,       /registry/machine查询返回的数据,返回的appType = 0

AppService.getApps().success(
          function (data) {
            if (data.code === 0) {
              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;
                // 对应后端接口 /registry/machine查询数据,返回的appType = 0
                item.isGateway = item.appType === 1 || item.appType === 11 || item.appType === 12;

                if (item.shown) {
                  return item;
                }
              });
            }
          }
        );

通过后端MachineRegistryController的receiveHeartBeat方法返回的数据 item的appType为0,这就使得菜单一直显示普通的服务类型的菜单,而不显示网关类型的菜单。

receiveHeartBeat方法实际是由连接dashboard的各个客户端发送心跳数据,传入的参数中含有appType, 而透过客户端的代码可以看到以,使用http方式传输为例,可以看一下客户端SimpleHttpHeartbeatSender的源码,这里的sendHeartbeat方法,就是对应dashboard的receiveHeartBeat来进行信息交互的,通过调试可以看出发送的心跳数据HeartbeatMessage中,appType就为0而不是为1,HeartbeatMessage类中app_type的值是来源于SentinelConfig.getAppType() 

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中 appType的默认值APP_TYPE_COMMON = 0,由于SentinelSCGAutoConfiguration自动配置的时候,init初始化方法未能在发送心跳前将appType的值进行变更,所以导致都是按默认值0来传递数据的,也就是说,SimpleHttpHeartbeatSender中的HeartbeatMessage在初始化的时候SentinelSCGAutoConfiguration里的initAppType方法还未未执行,SentinelSCGAutoConfiguration是在SimpleHttpHeartbeatSender读取csp.sentinel.app.type之后执行的,所以SentinelSCGAutoConfiguration这里的自动配置csp.sentinel.app.type是无效的,这就使得dashboard一直按普通服务展示菜单。所以只能通过,SentinelConfigLoader 会从System.getProperty加载变量信息,并最终由SentinelConfig的loadProps方法加载到props的内存Map中,解决办法是可以在服务的启动命令中加上 -Dcsp.sentinel.app.type=1 或者 在服务的Application的main方法里加入System.setProperty("csp.sentinel.app.type", "1"); 这样就可以使得机器的appType变为1

@SpringBootApplication
@EnableDiscoveryClient
public class Application {
    public static void main(String[] args) {
        System.setProperty("csp.sentinel.app.type", "1");
        SpringApplication.run(Application.class, args);
    }
}

这样调整之后只是改变了一半的问题,你会发现即便改变完成,dashboard刷新后,菜单仍然不会刷新变为网关类型,这是什么原因呢,调试代码发现,虽然客户端心跳发送的machineInfo已经变更appType,但是dashboard查询数据AppInfo里中的apptype依旧为0,

public class AppInfo {

    private String app = "";

    private Integer appType = 0;

    private Set<MachineInfo> machines = ConcurrentHashMap.newKeySet();  这里的appType已经变为1了

    public AppInfo() {}

这又是什么原因呢?经过调试,最后发现,问题就出现在了,SimpleMachineDiscovery里的addMachine方法上了

@Component
public class SimpleMachineDiscovery implements MachineDiscovery {

    private final ConcurrentMap<String, AppInfo> apps = new ConcurrentHashMap<>();

    @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()));
// 只有当apps里对象不存在的时候,才会放一个新的进去,才会发生变化
        appInfo.addMachine(machineInfo);
        return 1;
    }

这是因为,apps里存储了所有服务的服务信息,开始提到的,item.isGateway = item.appType === 1 || item.appType === 11 || item.appType 这个item对象实际对应的是appInfo对象,而appInfo对象里的appType值,是在第一次apps里对象不存在的时候,才会放一个新的进去,才会发生变化。所以当你不重启dashboard的时候,apps里的服务列表中单个对象中的appType是不会变化的,即便是你网关服务已经变更appTpye=1并发送心跳过来。所以直到你重启dashboard ,apps重新装载数据的时候,apps里的服务列表中单个对象中的appType才会重新取到客户端修改后的值并存进去。

AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), o -> new AppInfo(machineInfo.getApp(), machineInfo.getAppType()));

machineInfo.getAppType()变为1了
apps的中AppInfo的appType才会被machineInfo.getAppType值覆盖掉

这个时候页面中的菜单才会变为

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值