核对体系-资损防控(核对篇)
导语:感谢开发小伙伴卢启宝(花名:七宝)、尹逊志(花名:小鱼儿)在项目中辛苦付出!
一.前言
随着有赞的业务增长,单量与日俱增,业务场景变得越来越复杂,迭代的速度变得更快,出现故障的概率更大,从而产生的资损可能性也变大,这无论对于有赞本身还是对于有赞的商家来说都是很可怕的事情,我们要保证商家在有赞做生意是安全的、值得信赖的,所以及时发现问题、及时止血变得极其重要。同时,我们发现由于业务场景变得复杂,开发人员和测试人员也疲惫地奔波在各种场景的测试中,捉襟见肘,所以需要一个可以通过表中数据反推迭代的代码逻辑、和相关配置是否正确,在这种背景下,我们建立了核对体系,资损防控系统应运而生,我们也可以叫它实时核对系统,今天我们介绍核对体系中资损防控的第一部分:事前和事中处理。事后处理,例如:熔断止血、差错处理等,我们放在下一遍详述。
核对体系:
资损防控系统:也可叫实时核对系统
离线核对:内部核对、机构核对和实收核对等
差错系统:渠道差错、业务差错、实时核对差错等
二.前世
基于前面说到的背景,资损防控平台在18年开始建起,在不断探索中,历经两版,我们暂且称第一版为基于sql方式核对,第二版为硬编码方式核对:
版本 | 优点 | 缺点 |
---|---|---|
基于sql方式 | 1入门简单,配置门槛较低 2.sql脚本易于理解 |
1.主动拉取数据,占用sql查询资源 2.sql方式核对,性能较低,例如:索引问题 3.表中大字段问题没办法解决,例如:字段中存有json串 4.业务方表结构DDL后,资损防控感知困难 5.业务方对表数据DML后,资损防控无法感知,可能导致资损 |
基于硬编码方式 | 1.可以看一笔单号在整个链路上每一个环节的核对结果 2.通过监听binlog消息解决了表数据DML后造成资损的可能 3.解决表中大字段问题 4.Hbase存储待核对数据,读写性能较高 |
1.硬编码编排核对链路,降低开发效率,不易维护 2.与业务链接紧密,开发人员理解业务成本过高 3.检测链路脆弱,一个环节修改导致整个链路存在问题 4.规则不能掌握在业务方手里,相对于业务方来说变成黑盒 |
通过比较,我们知道第一版与第二版的区别,其中各有利弊,在不同时期解决不同的问题。我们基于两年的探索,充分了解了资损防控开发者、业务方或使用方的痛点,通过各种业务场景的抽象,得出核对模型,推出最新一版资损防控平台,就是这样,我们有了“今生”。
“今生”要解决什么问题?我们通过前期的积累,无论从系统性能和扩展性上,还是需求收集、理解和整理上,我们要解决几点问题:
- 及时性:核对出异常要报警及时,将报警时效控制在分钟级别,通知相关业务方,及时修改;
- 敏感性:表中数据变化要及时反应,例如:下面的场景四;
- 无代码入侵:作为使用方并不希望在自己的系统中编写过多代码或是开发API,供资损防控平台拉取数据使用;
- 不影响链路上正常业务:举个例子,资损防控所配置的规则不能调用正常业务接口API读取数据,这样会占用正常业务资源,很可能触发接口熔断;
- 配置可视化:对于规则、报警相关配置,通过资损防控运营平台配置;
- 控制反转:将规则配置从资损防控平台的开发者手里反转到真正的业务方或使用方,术业有专攻,让平台开发者更专注于平台建设;
- 易用性:降低使用入门成本,将门槛降到最低,而不是望而却步;
- 高吞吐量:降低Nsq消息RT时间,提高吞吐量;
- 动态扩容:对于生成的检测点,要分库,降低单库压力,动态扩展分库的分片数,下面在分片策略部分会有解释。
三.今生
1.能力
- 日处理binlog消息2亿左右
- 日处理检测数据6千万以上
- Nsq的QPS峰值达到1.2w
2.使用场景
在说今生之前,我们先聊聊几个使用场景:
场景一:C端用户支付或是退款,清结算系统需要将用户支付的金额与退款金额轧差(简单理解:支付金额-退款金额)结算给商家余额账户,这样就需要将清结算系统中结算数据与账务系统收支记录做比对,两边是否相等,可能会出现两种资损的场景,多结或是少结,也就是长款或是短款;
场景二:C端用户重复支付的情况,收单系统是要自动将多支付的金额退给用户的,这时就需要核对有赞是少退还是多退;
场景三:系统重构后,从老系统切流到新系统这个过渡期间,会有双写的情况,所以需要核对切流过程中两系统中是否数据正常,状态金额都需要核对;
场景四:当开发人员手动通过DML修改数据库表的数据,导致修改成错误的金额,影响接下来的一连串的错误,并造成资损,所以我们要核对修改后的数据与上游的数据或是下游数据是否存在差异。
3.技术架构图
在事前与事中处理这部分,我们一共分为5层:数据来源层、收据收集层、数据转化层、数据核对层和异常处理层
- 数据来源层:此层为数据起源,监听各系统对应数据库中表的binlog信息;
- 数据收集层:此层为数据加载,加载的过程中要区分是分库的binlog还是常规库的binlog数据,同时要对binlog消息解析和过滤,过滤方式:Groovy脚本和SpEL表达式;
- 数据转化层:此层是经过上层以后,将收集到的数据通过使用方配置需要用到的字段映射、加工和组装成资损防控平台抽象出的数据模型(可类比宽表的概念),并将数据持久化,供下层核对使用,转化方式:默认字段直接映射和Groovy脚本;
- 数据核对层:对收集到的数据进行核对比较,核对方式:默认金额核对和Groovy脚本;
- 异常处理层:经过上层核对,直到超出核对存疑期,依然没有核对成功,将进行报警,通知相关核对规则负责人,并保留当前核对失败时核对数据的快照,也可称为保存“犯罪”现场,供规则负责人发现问题所在。
4.技术流程图
在技术架构图中,已初步简单聊过binlog从进入资损防控平台、核对、报警整个过程,就不过多赘述,这里有几点作为补充聊聊:
4.1 数据准备
4.1.1 异常降级重试
对于资损防控平台最重要的一点之一,就是尽量不能丢binlog数据,极小范围丢失是在容忍限度之内,当大范围丢失消息,会产生大量报警,是不能容忍的,而往往出现异常的可能就是,当多方binlog消息在同一时间进入同一个核对池子中时,为了避免Hbase中数据覆盖的情况,所以要顺序消费,并判断库表中数据产生的binlog消息的顺序号(sequenceNo),所以启用分布式Bond锁,将未获取到锁的数据消息重投,放入另一个消息队列,等待消费,重试策略会按两个维度,一个是重试次数,第二个是重试时间(时间按升序排列,每次重试的间隔时间会越来越长,直到达到最大重试次数),通俗的讲就是同一个房子,大家不能一起涌入,要排队,一个个进入,进入房子后,会按顺序分床,避免大家在无序状态下可能躺到一张床上;
4.1.2 检测点
这个检测点是资损防控定义的一个概念,其实很好理解,就是库表中数据每变化一次,产生的binlog就是一个检测点,例如:同一条数据当状态变更时,初始状态1,生成一个检测点,状态变更到2,再生成一个检测点,两个是不同的检测点,都会触发核对任务; 检测点另一个作用是只存储了待检测数据在Hbase中的rowkey,而不存实际待检测数据;同时,检测点是以分库的形式存储在Mysql中,这就引出下面的一个内容:分片策略;
4.2 分片策略
使用场景
- 当监听到binlog消息,我们要落检测点的时候,选择落在哪个分片下的库表中,由分片策略下发分片ID;
- 当核对任务启动开始核对的时候,再通过分片策略从Redis队列中获取分片ID,通过分片ID查询Mysql中状态为未检测或是存疑的检测点,进行核对。
时序图 为什么使用分片策略?
- 资损防控平台每日处理消息数据与日俱增,单库已经不能支持如此庞大的数据,所以使用了分库,那么一个伸缩性较强的分片策略变得至关重要,当每日生成的检测点变得越来越多,我们要有一个可以动态扩容的能力,动态扩大分片数;
- 将检测点在各个分片下均匀分布,减少数据集中,以支撑后续更大数据量;
- 核对任务捞起检测点,也要根据分片ID“雨露均沾”,消费检测点,触发核对。
分片策略降级
当应用Redis做的顶层分片策略,由于网络或是其他原因导致不可用,平台会自动降级到随机分片策略,但是随机分片策略可能造成检测点捞起不均匀,在短时间内是可以做到不影响核对任务,当自动检测到顶层分片策略可用后,会自动升级到顶层分片策略。
4.3 binlog动态监听器
为什么要使用动态监听器?
- 业务方在配置核对规则的时候,数据的来源是来自于库表中的binlog,而资损防控平台作为一个消息的Consumer,不能每次新增一个消息的监听就创建一个Channel,重启应用,这样的成本太高,所以经过重写Nsq消费客户端,可以动态上下线Consumer;
- 另一个目的:Nsq中Consumer的消费线程数与ReadyCount可以掌握到自己的手里,当发现消息消费较慢,可以调高这两个参数,当发现消息过多,我们可以放缓消费,达到蓄洪的作用。
4.4 Redis+TMC
使用Redis与本地缓存(TMC)结合使用,对于核对规则的热点Key,从Redis中拉到本地缓存中,系统从本地获取规则信息,减小远程IO,提升binlog消息处理和核对任务效率,减小Nsq消息RT时间,提高消息处理吞吐量,规则版本控制器定时刷新Redis中核对规则,保证核对规则在缓存中为最新版本。
5.支持的功能
5.1 数据准备
- 数据过滤:过滤方式分为Groovy脚本和SpEL表达式两种,Groovy脚本灵活,与java代码无缝衔接,SpEL表达式书写简单,易于理解;
- 数据转化:转化方式分为Groovy脚本与字段直接映射两种,字段直接映射是将binlog中的数据直接映射成资损防控平台数据模型上,此方法配置优点:省力、快速,缺点:有局限性,当想获取的字段在json串或是另一张表中时,是没办法这样使用的,要通过Groovy脚本方式;
5.2 对账方式
- 单边对账:以一方数据核对另一方数据;
- 多边对账:以多方数据为基础,任何一方数据进入资损防控,都可以作为核对任务触发点,核对多方数据是否存在差异。
5.3 通用服务
- DB查询器:支持在Groovy脚本中查询各方数据库备库,对于数据过滤与数据转化是很重要的工具;
- JSON工具:对于binlog中的大字段并使用了json格式存储时,此工具可以在Groovy脚本中引入后直接使用,解析json字符串;
- 日志服务:日志服务为脚本内置服务之一,可以在Groovy脚本中使用日志服务logService打印日志,在单据追踪功能上可以查看脚本中所打印的日志。
5.4 单据追踪
有三个重要作用:
- 供规则配置人员查看binlog消息从进入资损防控平台,到核对完成整个过程中日志信息;
- 使业务方或是使用方都可以通过资损防控平台自行完成规则配置、测试到上线,形成核对配置闭环;
- 对于线上的核对异常报警,可以查看报警的原因,在哪个环节上出了问题,是规则没有覆盖到场景,还是脚本书写错误,针对性及时解决问题。
四.方向
- 当单量较小、业务不复杂时,及时发现问题是非常重要的,但当单量变大、业务复杂时,发现问题是基础,而及时止血、熔断变为更加重要,这一部分是未来重中之重;
- 核对出的异常,在资损防控平台上形成闭环异常处理;
- 核对的结果,要有统计的功能,将事后所有处理收拢到平台上;
- 依然要在易用性、配置可视化再下功夫,将资损防控周边功能做的更好。
五.总结
核对体系依然在不断的持续完善中,无论是技术、业务和性能上,这其中充满很大挑战,我们在探索的路上持续进步前行,为有赞的业务保驾护航,做商家资金管理的坚强后盾,这篇文章只是提供个思路,不同公司有不同的业务场景和体量,做法一定会有所不同,希望对各位小伙伴有所帮助,成为资金安全上的铺路石。对数据核对方面有想法或是想拍砖的,可以联系我:sunqinghao@youzan.com