1、背景
中通在2019年双十一当天的订单量超过了2亿,2019年全年快件量也超过了100亿,持续多年稳居快递行业榜首。随着电商业务量的爆发式增长,整个电商生态上下游之间的数据对接需求也日益增长,参与数据对接的合作伙伴更多,对数据对接的可用性、准确性和安全性等方面的要求也越来越高。中通作为电商快递行业的龙头老大,是电商生态的重要组成部分,对这方面的工作也是十分的重视。为了避免重复造轮子,提高交付质量和效率,我们需要设计和开发一套统一、安全、高效的数据对接服务,并通过它快速实现所有合作伙伴的数据对接需求,同时保障亿万用户的数据安全。
2、问题和挑战
分析现有数据对接需求后,我们发现统一对外数据对接服务将会面临以下五个问题:
数据安全性
部分数据对接需求包含了用户个人隐私信息,如何能保障这些数据的安全,并根据对接需求提供可靠的数据加密方式是我们首先要解决的问题。
传输协议多样
由于每个合作伙伴的对接协议各不相同,有通过HTTP接口来接收数据,有通过FTP来接收数据,也有通过MQTT接收数据等。这些协议如何复用,如何编排是我们接下来要解决的问题。
数据格式多样
不同的数据对接协议一定会带来不同数据格式的要求,甚至相同的对接协议也会有不同的数据格式要求,比如有些HTTP接口接收数据的方式可能需要的数据格式是每一条数据调用一次接口,有的要求几十条数据打包调用,因此对多种多样格式的数据组装和构建逻辑及管理也要精细设计。
数据来源多样
由于需要对接的数据维度较多,且根据不同的业务场景需要做一些合并和关联等逻辑处理,我们需要从公司内部多套系统数据源抽取数据,其中包括从MQ集群订阅和通过内部网关服务抽取等多种方式。所以同一种数据抽取方式的可复用性也是要重点考虑的问题。
对端服务的不确定性
不同合作伙伴的技术能力和基础设施水准各不相同,对端服务的不确定性,导致数据对接服务的稳定性很难统一保障,常出现的问题有:提供的VPN网络抖动和丢包、数据接收接口不可用、数据接收处理服务过载等。异常期间丢失的数据需要重推,以保障数据的完整性及统计结果的准确性。如何在不影响正常服务运行的情况下快速追补丢失的特定批次数据,也是问题之一。
3、方案设计和实施
虽然问题和挑战比较多,但所幸大部分数据对接需求的业务逻辑相对来说比较单一,经过探索,我们按照以下步骤完成方案设计和服务建模。
设计目标
为解决上述问题和挑战,满足功能和非功能性的需求,我们确定了以下几个设计目标:
安全性:保障数据机密性和完整性;
高可用:多节点集群部署,并有较强的容错性,可保证长时间稳定对外提供服务;
高性能:满足每天海量数据的推送能力;
高扩展:满足合作伙伴灵活多变的对接需求;
抽象化:抽象出业务逻辑流程,分清领域边界,创建领域模型;
可追溯:数据处理失败有记录以便重试处理;提供相关统计功能以便输出报表与比对;
复用性:功能相似的模块代码尽可能实现可复用。
业务领域建模
明确业务领域从梳理业务需求开始,找出业务中涉及的概念和功能。具体来说,就是把一个大的系统分成若干个子系统,每个子系统互相独立且有各自明确的领域边界。由于子系统之间跨领域边界的相互调用要符合领域的业务逻辑,所以我们把数据对接服务归结为以下几个领域:
图1. 领域驱动示意图
数据收集域:该域负责从各类通道收集需要的源数据;
数据转化域:该域负责把多个源数据对象转化成需要的数据对象;
数据构建域:该域负责把构建好的数据对象,打包到HTTP请求或者文件中;
数据推送域:该域负责根据不同合作伙伴的要求,把构建好的数据传输出去;
数据统计域:该域负责统计数据对接情况的相关数据。
完成划分领域边界后,就能很容易创建出下面的领域对象:
Driver(驾驶舱对象):用于控制整个数据对接业务流程的领域对象,配置和编排各个领域对象之间的调用(流程章节详细介绍);
Collector(源数据收集域中的对象):收集器对象的基类,通过实现这个基类来实现不同策略的源数据接收方式;
Converter(数据转化域中的对象):转换器对象的基类,通过实现这个基类来实现不同策略的数据转换方式。此领域的业务逻辑作为核心功能,其策略模式是通过单独的Module来实现的(落地章节详细介绍);
Builder(数据构建域中的对象):构建器对象的基类,通过实现这个基类来实现不同策略的数据构建方式;
Pusher:(数据推送域中的对象):推送器对象的基类,通过实现这个基类来实现不同策略的数据推送方式。
抽象业务流程
通过业务需求规整领域边界和领域对象之后,我们需要考虑如何把这些领域对象用最合理的方式进行交互,以保证服务在领域内的高内聚、领域间的低耦合。
由于业务逻辑相对简单,可抽象出通用的业务流程用于衔接各领域。Driver驾驶舱就是用来控制业务流程的(查看下图)。除了控制业务流程外,Driver驾驶舱还需要在每一步流程执行前根据配置的不同,选择不同策略的对象,来完成不同策略的业务逻辑。为了保证整体服务运行的稳定性和可监控性,Driver驾驶舱需要catch到流程中每一步可能的异常,同时打印所需要的流程步骤信息。
图2. 逻辑分层图
非功能性设计
厘清业务并在此基础上划分领域边界、创建领域对象、了解领域间的合理交互调用,还需要做一些非功能性方面的设计。
系统可靠性设计
| 安全性
保障数据安全是我们的重要使命和业务发展的基础,所以,认证、授权、访问控制、审计和数据脱敏加密等安全控制方案需要在推送服务设计之初就予以考虑,数据流过的各个环节也需要我们进行安全管控。为了实现上述目标,我们采取了以下几条措施:
数据流可追踪
从数据仓库到消息队列中间件,再到数据对接服务和合作伙伴的交互,数据对接的每个步骤、每个操作我们都有详细的LOG记录,且重要的LOG会入库永久保存。显然,这样做对于数据流向的追溯、安全风险的防控、异常问题的排查都会有非常大的帮助;
数据不泄漏
为了保证数据不泄露,我们实施了五项举措:
1. 要求所有合作伙伴提供专用VPN网络通道进行数据对接;
2. 我们和合作伙伴相互设置IP白名单,从源头提升信任;
3. 引入CA证书进一步提升信任;
4. 对数据进行加密处理,且选择对称加密的加密方式。我方和合作伙伴需要保存相同的密钥,来对数据进行加解密,那么另一个安全问题被引入了——密钥的安全如何保证?我们在硬件加密机的基础上自研了一套KMS密钥管理服务来对所有秘钥进行全生命周期管理,对接双方需要通过此服务获取相应密钥,才能完成数据的加解密操作;
5. LOG的脱敏是最容易遗漏的安全环节。之前提到的数据操作可追踪需要记录详细的操作LOG,在此过程中难免会有一些敏感数据被LOG记录可能导致数据泄漏。在业务代码中主动脱敏LOG并不是一个好做法,我们选择在代码框架中提供AOP的方式,在对业务无任何侵入的情况完成LOG脱敏。
数据不被篡改
为了保证加密数据在传输过程中不被篡改,我们通过验签的方式对数据的正确性和完整性进行校验。同样地,验签需要使用的密钥也是通过我们的KMS密钥管理服务进行管理。
| 高可用
多节点集群部署,并有较强的容错性,可保证长时间稳定对外提供服务。
| 高性能
采用异步化和缓存设计,单次操作只同步处理关键逻辑,其余逻辑尽可能异步化。频繁读取的数据进入缓存服务,热度降低后自动过期。
| 高扩展
采用插件式设计。在统一固定的业务流程基础上,把数据构建对象和数据对接对象设计成可插拔的方式。完成后无需重新发布服务的情况下,实现合作伙伴的业务需求变更。
| 数据遗失
若某个事务处理失败(无论问题出自我们、对接方或者是运营商网络),为保证此数据不丢失,需要通过使用死信队列的设计方式,来暂存那些重试几次都无法处理的数据,以待后续处理。
| 数据重做
对接服务不仅可以重做死信队列中的数据,还要能快速地重做某一时间窗口或者某个特征维度的所有数据。借助于无状态服务和以数据转化器为主视角的设计,我们可以快速部署出一套全新集群环境,以完成大规模数据重做的需求,且不影响正常服务运行。
| 统计信息
完成数据对接的操作量、操作频率、操作成功率、网络质量监控等数据统计。
整体的数据处理流程如下图所示:
图3. 整体架构图
实施经验
数据对接服务需要能够长期可靠的运行,因此对线上可维护性要求比较高,用什么主视角来启动服务,直接影响了服务上线后的维护难易程度。
第一种方案:以Driver驾驶舱为主视角启动服务,并且通过不同的配置将数据转换器和数据推送加载进来;
第二种方案:以Converter数据转化器为主视角启动服务,需要引用驾驶舱的jar包,并且通过不同的配置将Pusher推送器加载进来。
我们最终选择了第二种方案。数据对接服务的核心逻辑在于——每一个Converter数据转化器是一个Module,并且有可启动的main函数,这就使得与不同的合作伙伴对接就会有完全不同的Converter数据转化器被开发。因此以数据转换器为维度作为一个启动单元,帮助合作伙伴水平扩展推送能力再合适不过。除此以外,方案二在配置管理方面可以做到一个Converter转换器对应一个配置。而方案一则会导致一个配置需要对应多个不同的数据Converter转换器和Pusher推送器,配置维护起来较为复杂。
4、未来展望
根据业务特点和我们面临的挑战,未来我们会向以下两个方面进行演进:
Serverless
数据对接服务有两个特点:第一,业务流程逻辑相对固定,开发者不需要关心整体,只需要开发相应模块代码即可。第二,数据重做,或有新合作伙伴接入时都需要灵活快速的构建新集群实例去运行。这两点和Serverless的快速构建,弹性扩容和缩容,开发无需DevOps非常契合。未来我们会考虑让数据对接服务向Serverless方向发展。
对象存储
合作伙伴的技术能力、运维能力、硬件性能等各有不同,但大都不能承受较高TPS的调用量。因此,他们多选择以文件形式进行数据对接。这就使得我们面临一个问题:当合作伙伴的服务不可用时,数据对接服务生成的文件就会堆积在服务器本地硬盘上,造成磁盘被写满,并导致整体服务出现异常。我们现有的解决方案是通过监控,Shell脚本或人工copy的方式把这些文件进行转移,等合作伙伴的服务恢复正常后把积压的文件重新处理。这种方法十分繁琐,还可能由于短时间集中大量文件到合作伙伴的服务,引发其服务再次不可用。
为了解决上述问题,我们需要有一个在文件存储空间上无限制,且可通过互联网进行文件数据交换的OSS对象存储服务。首先数据对接服务把生成的文件上传到OSS对象存储服务指定的Bucket,然后由合作伙伴从指定的OSS对象存储服务的Bucket中获取文件。这样做不仅解决了生成文件的存储问题,也使得我们可以和合作伙伴间互相解耦:一方面,我们无需关心合作伙伴服务的运行情况,另一方面,合作伙伴也可以主动从OSS对象存储服务获取文件来做后续处理。未来我们的OSS对象存储服务投入生产后,推送服务就会借助其能力按照以上设计来解决这方面的问题。
借助对象存储服务能力,优化后的整体数据处理流程如下图所示:
图4. 整体架构图
参考资料:
《实现领域驱动设计》
《企业应用架构设计》
《重构-改善既有代码的设计》
《深入浅出Serverless》
https://www.amazonaws.cn/en/s3/
https://aws.amazon.com/iam/features/manage-permissions/
作者简介
张仁杰,高级架构师,参与中通信息安全部的开发规范、架构设计、技术支持等相关工作,负责数据对接、云盘服务和对象存储服务等数据安全解决方案的设计和落地。
声明:本文来自中通安全应急响应中心,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。