定时补偿方案

news2024/12/28 19:18:22

 1:需求描述

支持NVR升级后通道数变更,完成升级后,设备SDK上报通道数量给A平台,A平台将NVR通道数量同步给B平台,B平台自动调用C平台接口,同步通道数量给C平台,C平台重新生成通道序列号,并完成序列号入库,再由C平台将通道序列号同步给A平台,A平台完成序列号入库。

2:需求分析

我所在的小组维护的是B平台;拿到需求后,开始分析:

逻辑很简单:设备SDK在启动后,会将信息上报到A平台,A会同步到B平台,B再上报给C;所以只需要在kafka里加入更新后的通道数就可以

现有接口:A平台在设备重启后会通过Kafka上报设备信息给C、获取NVR的通道信息接口

存在问题:获取NVR通道信息的接口是http请求,可能出现网络异常掉用失败的情况,所以需要设计一个补偿机制

3:补偿逻辑

当走网络掉接口失败后,将失败后的kafka信息存入数据库,用定时任务去扫描失败信息进行kafka再次发送;考虑到以后还会有同类型的升级,补偿机制可以设计成通用的。

4:表设计

固件版本同步失败记录表:

重要字段:status:判断是否同步成功;biz_type: 业务类型,本次升级可以设为1(需要补偿的业务类型);biz_msg:业务信息(需要补偿的信息)

5:代码逻辑

更新版本信息接口:

@Override
public String updateVersionInfo(String macId, String currentVersion, String modelId) {

     /**更新前置代码**/

    try {
        //将更新后的设备信息用kafka同步给C,加入升级后的通道数
        JSONObject kafkaData = new JSONObject();
        kafkaData.put("macId", macId);
        kafkaData.put("version", currentVersion);
        kafkaData.put("modelId", modelId);
        kafkaData.put("videoCapability", 2);
        //升级通道数的是nvr总线设备(1nvr总线设备(总设备)可以理解为总设备是一个大的机盒,下面挂了许多子设备,这次升级的就是设备SDK可以上报通道数量(可以挂子设备的数量)
        if (cameraDaoMybatis.getDbCameraInfo(macId).getNvrType() == 1) {
            //通过macID获取nvr的信息(设备id,(state 1:在线),nvrList(这个的size就是通道数));因为获取nvr信息的方法是走网络掉接口,所有可能出现网络异常掉用失败的情况
            NvrDetailInfo nvrChannels = dcsManager.getNvrChannels(macId);
            //当调用成功,并且nvr状态为在线
            if (nvrChannels != null && nvrChannels.getState() == 1) {
                //在kafka中加入升级后获取到的通道数
                kafkaData.put("channelNumber", nvrChannels.getNvrChannelList().size());
            }
            //当调用失败,将设备id,版本,已经kafka的信息全部存入固件版本同步失败记录表
            Integer row = syncFailureRecordMybatis.addSyncFailureMsg(macId, currentVersion, kafkaData.toString());
            LOG.info("addSyncFailureMsg success, rows={} affected, macId={}", row, macId);
        }

       /**更新后置代码**/
       
    } catch (Exception e) {
        LOG.error("updateVersionInfo error: ", e);
        result = JsonUtil.addErrorCode(EErrorCode.ERROR_SYSTEM, result);
    }

    return result.toString();
}

6:定时任务补偿

@Component
@Slf4j
public class SyncFailureRecordJob {
    @Autowired
    private LittlecConfig littlecConfig;

    @Autowired
    private SyncFailureRecordMybatis syncFailureRecordMybatis;

    @Autowired
    private DcsManager dcsManager;

    @Autowired
    private DeviceVersionProducer deviceVersionProducer;

    @XxlJob("SyncFailureRecord")
    public ReturnT<String> syncFailureRecord(String param) {
        //配置一次升级的条数
        long limitSize = littlecConfig.getConfigAsLong("camera.SyncFailureRecordMybatis.querySyncFailureRecord.limitSize", 5000L);
        //记录每次启动定时器,开始扫描的起始id
        Integer startId = 0;
        log.info("syncFailureRecord param:limitSize:{} startId:{}", limitSize, startId);
        boolean isCompleted = false;
        //记录循环次数
        int reconTimes = 0;
        //查t_firmware_version_sync_failure_record表,一天内的数据
        while (!isCompleted) {
            reconTimes++;
            //通过limitSize和startId查出的list集合
            List<SyncFailureRecordDO> syncFailureRecordDOList = syncFailureRecordMybatis.querySyncFailureRecordList(limitSize, startId);
            //取出list里的所有mcaid
            String macIds = syncFailureRecordDOList.stream().map(n -> n.getMacId()).collect(Collectors.joining(","));
            //日志记录循环次数,与这一次扫描的所有macid
            log.info("In querySyncFailureRecordList: recon {} time with syncFailureRecordDOList size {} , collect macIds={}", reconTimes, syncFailureRecordDOList.size(),macIds);
            //如果扫描出的集合小于定下的升级条数,结束循环(代表已经扫描到了最后的一组数据)
            if (syncFailureRecordDOList.size() < limitSize) {
                isCompleted = true;
            }
            //如果扫描出的集合大小大于0(代表查出数据)
            if (syncFailureRecordDOList.size() > 0) {
                //修改下次扫描的起始id为集合的最后一个id
                startId =syncFailureRecordDOList.get(syncFailureRecordDOList.size() - 1).getId();
                //循环取出list里的对象
                for (SyncFailureRecordDO dto : syncFailureRecordDOList) {
                    if (dto != null) {
                        //同步需要放入kafuka的所有数据给安防管理平台
                        NvrDetailInfo nvrChannels = dcsManager.getNvrChannels(dto.getMacId());
                        JSONObject bizMsgObject = JSONObject.parseObject(dto.getBizMsg());
                        org.json.JSONObject kafkaData = new org.json.JSONObject();
                        kafkaData.put("macId", bizMsgObject.getString("macId"));
                        kafkaData.put("version", bizMsgObject.getString("version"));
                        kafkaData.put("modelId", bizMsgObject.getString("modelId"));
                        kafkaData.put("videoCapability", 2);
                        //加入升级后的通道数
                        kafkaData.put("channelNumber", nvrChannels.getNvrChannelList().size());
                        //同步方法
                        deviceVersionProducer.sendDeviceVersionMessage(dto.getMacId(), kafkaData.toString());
                        log.info("sendDeviceVersionMessage success, macId={}, channelNumber={}", dto.getMacId(), nvrChannels.getNvrChannelList().size());
                        //修改表中的status,为已经升级
                        Integer row = syncFailureRecordMybatis.updateRecordStatus(dto.getMacId());
                        log.info("updateRecordStatus success,rows={} affected,macId={}",row,dto.getMacId());
                    }
                }
            }
        }
        log.info("syncFailureRecord success");
        return ReturnT.SUCCESS;
    }
}

7:mybatis

List<SyncFailureRecordDO> querySyncFailureRecordList(@Param("limitSize") long limitSize, @Param("startId") Integer startId);

Integer updateRecordStatus(@Param("macId") String macId);

8:xml

<select id="querySyncFailureRecordList"
        resultType="com.cmcc.littlec.camera.dao.entity.SyncFailureRecordDO">
    SELECT uc.mac_id AS macId, uc.biz_msg AS bizMsg
    FROM t_firmware_version_sync_failure_record uc
    WHERE uc.id<![CDATA[ > ]]>#{startId}
      AND uc.biz_type = 1
      AND uc.status = 0
      AND created &gt;= DATE_SUB(NOW(), INTERVAL 1 DAY)
</select>

<update id="updateRecordStatus">
        UPDATE t_firmware_version_sync_failure_record
        SET modified = NOW(),
            status   = 1
        WHERE mac_id = #{macId}
    </update>

9:提交任务

开开心心提交代码给老大code review,老大说:这补偿为什么要我们来做,不合理;几个电话加个会议后决定:

让A平台上报SDK信息的时候带上升级后的通道数,我们只需要加个字段就可以了。。。。

好嘛!两天白干

10:总结

遇到问题能抛出去就抛出去!能麻烦别人就别麻烦自己

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP 之道(PHP The Right Way 中文版)

PHP 之道&#xff08;PHP The Right Way 中文版&#xff09;

HeartBeat监控Mysql状态

目录 一、概述 二、 安装部署 三、配置 四、启动服务 五、查看数据 一、概述 使用heartbeat可以实现在kibana界面对 Mysql 服务存活状态进行观察&#xff0c;如有必要&#xff0c;也可在服务宕机后立即向相关人员发送邮件通知 二、 安装部署 参照章节&#xff1a;监控组件…

液态二氧化碳储存罐远程无线监测系统

二氧化碳强化石油开采技术&#xff0c;须先深入了解石油储层的地质特征和二氧化碳的作用机制。现场有8辆二氧化碳罐装车&#xff0c;每辆罐车上有4台液态二氧化碳储罐&#xff0c;每台罐的尾部都装有一台西门子S7-200 smart PLC。在注入二氧化碳的过程中&#xff0c;中控室S7-1…

租一台服务器多少钱决定服务器的价格因素有哪些

租一台服务器多少钱决定服务器的价格因素有哪些 大家好我是艾西&#xff0c;服务器这个名词对于不从业网络行业的人们看说肯定还是比较陌生的。在21世纪这个时代发展迅速的年代服务器在现实生活中是不可缺少的一环&#xff0c;平时大家上网浏览自己想要查询的信息等都是需要服…

terser

环境&#xff1a; 一、使用vueCli创建的项目的vue.config.js 添加terser配置 验证了在打包后生成的.js文件中确实没有了console.log() 这里的.js.map可以省略&#xff0c;公司里代码打包后就没有.js.map文件了 要配置下除去.js.map文件或者统一分到.map文件夹里 二、vite 安…

抓紧收藏!软考个税抵扣3600元详细操作流程

软考证书有很多项福利政策&#xff0c;“个税补贴 ”是很容易被忽略的一项&#xff0c;但其实这一项也是实打实的资金补贴。 持有软考证书可在线上申请&#xff0c;按照3600定额扣除。 具体操作流程&#xff0c;接下来详细说明。 01 政策依据 根据《个人所得税专项附加扣除暂…

极兔速递查询,极兔速递单号查询,筛选出指定派件员的单号

批量查询极兔速递单号的物流信息&#xff0c;并将指定派件员的单号筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 极兔速递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;第一次使用的朋友记得先注册&#xff…

ThreadLocal 本地线程变量详解

概述 ThreadLocal 意为本地线程变量&#xff0c;即该变量只属于当前线程&#xff0c;对其他线程隔离 我们知道&#xff0c;一个普通变量如果被多线程访问会存在存在线程安全问题&#xff0c;这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问&#xff0c…

vector类

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟悉vector库 > 毒鸡汤&#xff1a;从人生低谷…

2-1基础算法-枚举/模拟

文章目录 1.枚举2.模拟 1.枚举 [例1] 特别数的和 评测系统 #include <iostream> using namespace std; bool pa(int x) {while (x) {if (x % 10 2 || x % 10 1 || x % 10 0 || x % 10 9) {return true;}else {x x / 10;}}return false; } int main() {int sum0;i…

设计模式——建造者模式(创建型)

引言 生成器模式是一种创建型设计模式&#xff0c; 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。 问题 假设有这样一个复杂对象&#xff0c; 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码…

育儿类App最佳广告变现方案及运营增长攻略 | TopOn变现干货

近年来&#xff0c;随着全球母婴、亲子及育儿产品的快速发展&#xff0c;移动互联网育儿App迎来了一次井喷式的发展&#xff0c;儿童数字广告支出成为了增长速度最快的细分市场。TopOn联合全球儿童安全广告平台Kidoz举办了一场线上分享会——育儿类App最佳变现解决方案及广告变…

算法——位运算

常见位运算总结 基础位运算 << >> ~与&&#xff1a;有0就是0或|&#xff1a;有1就是1异或^&#xff1a;相同为0&#xff0c;相异为1 / 无进位相加 给一个数n&#xff0c;确定他的二进制表示中的第x位是0还是1 让第x位与上1即可先让n右移x位&上一个1&#…

Docker入门安装gerrit软件

Windows上运行docker 什么是Docker Desktop docker desktop是Docker在Windows 10和macOS操作系统上的官方安装方式&#xff0c;这个方法依然属于先在 Windows 上部署 Docker 的方法都是先安装一个虚拟机&#xff0c;并在安装 Linux 系统的的虚拟机中运行 Docker。 开启Hyper-…

moogose使用

概念 Node.js 的优雅 mongodb 对象建模 Mongoose 提供了一种直接的、基于模式的解决方案来对应用程序数据进行建模。它包括开箱即用的内置类型转换、验证、查询构建、业务逻辑挂钩等 安装 npm i mongoose具体例子 E:\Nextjs\mongoose-use-demo\app\api[crud]\route.ts 连接…

数据结构:栈(Stack)的各种操作(入栈,出栈,判断栈非空,判断栈已满,附源码)

前言&#xff1a;在前面的文章中&#xff0c;我们讲解了顺序表&#xff0c;单链表&#xff0c;双向链表。而我们今天要分享的栈则是基于之前的数据结构上搭建的&#xff0c;但是相较于顺序表和链表来说&#xff0c;栈的实现就非常简单了。 目录 一.栈(Stack)的概念 二.栈的数…

TOWE 高品质220V/380V工业插头插座:插座篇

在不同工业场合和环境中&#xff0c;对工业用插头插座和耦合器的配置有着不同的要求。在实际应用中&#xff0c;我们要根据用途、工作环境、规格大小、外观造型、安装形式、功能等方面进行选择。只有确保正确选择产品&#xff0c;才能确保现实用电环境的安全、高效。 同为科技&…

什么是 web 组态?web 组态与传统组态的区别是什么?

组态软件是一种用于控制和监控各种设备的软件&#xff0c;也是指在自动控制系统监控层一级的软件平台和开发环境。这类软件实际上也是一种通过灵活的组态方式&#xff0c;为用户提供快速构建工业自动控制系统监控功能的、通用层次的软件工具。通常用于工业控制&#xff0c;自动…

数据库——字段拆分与合并

一、GP或PostgreSQL 1.字段拆分成行 unnest(string_to_array(test, ,)) 例如某一字段值为"a,b,c,d"&#xff0c;使用string_to_array将其拆分为数组&#xff0c;然后使用unnest将数组平铺成一张表 SELECT unnest(string_to_array(555,666,777, ,)) 2.字段拆分成列…

一文告诉您企业为什么这么关注数字资产指纹

数字资产指纹 在互联网数字资产管理中&#xff0c;数字资产指纹就是数字资产的“身份证”&#xff0c;也是信息系统安全管理工作的基础。通过网络资产探测&#xff08;指纹&#xff09;可以在0day&#xff08;通常是指还没有补丁的漏洞&#xff09; 爆发时快速匹配到受影响的信…