最近由于要经常操作网关扩展,如添加http扩展,新增mqtt扩展的映射。经常出现网关不同步问题。导致了经过网关的数据,设备有创建,但是没有相应遥测数据,之前有看过群里大佬写过thingsboard网关与tb配置同步的源码分析:

https://blog.csdn.net/Zzhou1990/article/details/102477059 Thingsboard Gateway 根据云端配置初始化

前记

以下纯属本人臆想推测,难免会有疏漏。欢迎看者多加指正,互相进步。

理解大佬源码分析

从大佬文章中,大致可以理解成:当网关配置与thingsboard互通后,并允许thingsboard远程更新,则网关中连接thingsboard mqtt broker的mqtt客户端有订阅了属性更新主题v1/devices/me/attributes,用于实时获取共享属性更新。每当在thingsboard平台上配置网关扩展,thingsboard就会自动将配置信息保存为共享属性configuration的值。此时网关中的mqtt客户端监听到共享属性更新,会根据共享属性值重新初始化网关扩展配置。而后再把相应扩展配置上传至网关客户端属性appliedConfiguration,见如下代码:

private void updateConfiguration(String configuration) {
        try {
            if (extensionsConfigListener != null) {
                extensionsConfigListener.accept(configuration);
            }
            onAppliedConfiguration(configuration);
        } catch (Exception e) {
            log.warn("Failed to update extension configurations [[]]", e.getMessage(), e);
        }

public void onAppliedConfiguration(String configuration) {
        byte[] msgData = toBytes(newNode().put("appliedConfiguration", configuration));
        persistMessage(DEVICE_ATTRIBUTES_TOPIC, msgIdSeq.incrementAndGet(), msgData, null, null,
                error ->
                        log.warn("Could not publish applied configuration", error));
    }

同步状态判断依据

查阅thingsboard ui模块下的如下文件:
D:\JAVA\iot\code\thingsboard\ui\src\app\extension\extension-table.directive.js
确认同步方法:

function reloadExtensions() {
        vm.subscribed = false;
        vm.allExtensions.length = 0;
        vm.extensions.length = 0;
        vm.extensionsPromise = attributeService.getEntityAttributesValues(vm.entityType, vm.entityId, types.attributesScope.shared.value, ["configuration"]);
        vm.extensionsPromise.then(
            function success(data) {
                if (data.length) {
                    vm.allExtensions = angular.fromJson(data[0].value);
                } else {
                    vm.allExtensions = [];
                }

                vm.selectedExtensions = [];
                updateExtensions();
                vm.extensionsPromise = null;
            },
            function fail() {
                vm.extensions = [];
                vm.selectedExtensions = [];
                updateExtensions();
                vm.extensionsPromise = null;
            }
        );
    }

function updateExtensions() {
        vm.selectedExtensions = [];
        var result = $filter('orderBy')(vm.allExtensions, vm.query.order);
        // $log.info(result);
        if (vm.query.search != null) {
            result = $filter('filter')(result, function(extension) {
                if(!vm.query.search || (extension.id.indexOf(vm.query.search) != -1) || (extension.type.indexOf(vm.query.search) != -1)) {
                    return true;
                }
                return false;
            });
        }
        vm.extensionsCount = result.length;
        var startIndex = vm.query.limit * (vm.query.page - 1);
        vm.extensions = result.slice(startIndex, startIndex + vm.query.limit);

        vm.extensionsJSON = angular.toJson(vm.extensions);
        checkForSync();
    }

function subscribeForClientAttributes() {
        if (!vm.subscribed) {
            if (vm.entityId && vm.entityType) {
                $scope.subscriber = {
                    subscriptionCommands: [{
                        entityType: vm.entityType,
                        entityId: vm.entityId,
                        scope: 'CLIENT_SCOPE'
                    }],
                    type: 'attribute',
                    onData: function (data) {
                        if (data.data) {
                            onSubscriptionData(data.data);
                        }
                        vm.subscribed = true;
                    }
                };
                telemetryWebsocketService.subscribe($scope.subscriber);
            }
        }
    }

function onSubscriptionData(data) {
         // $log.info(data);
        if ($.isEmptyObject(data)) {
            vm.appliedConfiguration = undefined;
        } else {
            if (data.appliedConfiguration && data.appliedConfiguration[0] && data.appliedConfiguration[0][1]) {
                vm.appliedConfiguration = data.appliedConfiguration[0][1];
            }
        }

        updateExtensions();
        $scope.$digest();
    }

function checkForSync() {
         // $log.info("app:"+vm.appliedConfiguration);
         // $log.info("ext:"+vm.extensionsJSON);
        if (vm.appliedConfiguration && vm.extensionsJSON && vm.appliedConfiguration === vm.extensionsJSON) {
            vm.syncStatus = $translate.instant('extension.sync.sync');
            vm.syncLastTime = formatDate();
            $scope.isSync = true;
        } else {
            vm.syncStatus = $translate.instant('extension.sync.not-sync');

            $scope.isSync = false;
        }
    }

可以看出同步状态是通过判断网关的客户端属性appliedConfiguration与加工过的共享属性configuration值是否一致。
同时发现一个自认为的bug:共享属性configuration会被排序加工成extensionsJSON,排序是根据扩展id升序排序的,所以会导致客户端属性与加工后的共享属性不一致,导致提示不同步,但好像并不影响使用:
var result = $filter('orderBy')(vm.allExtensions, vm.query.order);

vm.query = {
        order: 'id',
        limit: 5,
        page: 1,
        search: null
    };

所以我自己建议是添加网关扩展时id累加命名,如1_green,2_red。