对接第三方系统实操经验分享
对接第三方系统实操经验分享
前言
为使得指示性更强,有以下名词说明
- A系统:是指要发起对接的我方系统,可以理解成
Client
- B系统:是要对接的第三方系统,可以理解成
Server
对接第三方的特殊性
- 请求方式不同。比较老的系统有可能使用
webservice
接受参数,比较人性化的系统会使用http + json
方式接受参数 - 登录方式不同。有些系统压根就不需要登录,有些系统通过 token登录,有些系统通过 cookie 登录
- 字段名不同。除非双方共同开发,否则字段名不一致的概率几乎为 100%
- 基础资料不同。就算是同一个供应商,在A系统的唯一id 为 333,在B系统的唯一id 为 supplier301,这才是棘手之处
- 网络请求不可靠,数据一致性(事务)无法保证。对接过程中,你想实现分布式事务的可能性基本为0,因为你基本没有可能能使得对面的数据库按照你的意愿 回滚。示例如下图:
对接框架必备功能
- 字段名转换工具类
- 字段值转换机制
- 重试机制
- 系统数据一致性(幂等)机制
- 完整的日志机制
必备功能实现参考方案
字段名转换工具类
方案一:使用注解建立字段关联关系
- 例子:
@ErpDataField(name = "FSaler", dataType = ErpBaseInfoTypeEnum.Employee,keyName = "FSTAFFNUMBER")private Integer fsaleAid;@ErpDataField(name = "F_PAEZ_Text4")private String fsoNumber;
- 解释
- 类似
fastjson
的@JSONField
注解一样,对字段进行重命名,根据目标系统的字段名进行关联 - 在重命名的同时,亦可以对字段值的类型进行标记,为字段值转换机制打上标记
- 可以按照对接框架的需要附上所需要的属性,比如说 字段是否需要强转成数字形式,单位是多少,保留多少位小数等属性,也是为了字段值转换机制打基础
- 优点:
- 注解写在字段上,清晰易懂,维护直接在代码中
- 反射也更好获取——工具类更好写
- 缺点:
- 由于字段之间的映射关系已经以 注解的形式 硬编码写在了代码里,想要 修改就必须重构代码,而重构往往对于 生产环境是不可容忍的
- 有可能污染了代码,尤其是 pojo(可以通过 新建一个vo类专门用于对接)
- 参考代码片段:一个详细的注解
方案二:使用数据库建立字段关联关系
其实也就是将注解的内容 放进数据库里进行维护,通过如前端填写注解表格进行 对接交互,可实时增删改,更为灵活
- 优点:
- 灵活,不使用硬编码
- 如果不涉及值转换机制的新增,基本可以实现 不用写任何代码,不用重构代码,即可新对接一张表单。
- 缺点
- 反射工具类书写难度加大:需要考虑到 字符串的拼写、合法性以及嵌套字段名解析等问题
- 参考代码:a2b对接脚手架项目
字段值转换方案
我们知道 A系统中供应商id 为 333
的供应商 = B系统中 供应商唯一主键 为 S303
的供应商,我们需要在值转换阶段,将其转换成 B系统能够接受的值
建议实现步骤
- 在字段名转换阶段,给字段打上 值转换类型属性(如上述注解中的
dataType
属性) - 维护值转换 策略模式集合,各种转换策略实现同一个接口
public interface ErpBaseInfoHandler {String handleField(ErpJsonField jsonField,String extInfo);
}
public enum ErpBaseInfoTypeEnumAdmin{None(0,"不是基本信息", NotBaseInfoHandler.class),Channel(1,"渠道客户", ChannelBaseInfoHandler.class),XyCompany(2,"公司主体Id", XyCompanyBaseInfoHandler.class),Employee(3,"员工faid", EmployeeBaseInfoHandler.class),Currency(4,"币种", CurrencyBaseInfoHandler.class),//....}
- 在值转换阶段,根据值转换类型进入对应的转换策略中
转换策略推荐实现方案——以员工信息为例
- 输入的参数含有
需要转换的A系统的员工id值 : A23
,要转换成 B系统的值 为B100567
- 以下 值映射关系 是指 A系统的值 => B系统的值,如A系统员工id A23 => B系统 的 B100567
值映射关系不稳定
也就是说
A23 => B100567
在某时刻是成立的,但是之后又不成立了
此种情况只能通过 B系统开发转换接口,然后A系统每次转换值时 调用接口 实时查询对应的值
值映射关系稳定
- 根据员工id查找数据库,如果可以找到映射关系则立即返回
- 根据B系统提供的查询接口(如果有),则根据一定的筛选条件获取对应的B系统的值(如根据员工工号 查询B系统的员工id),如果查询成功,则保存映射关系保存在数据库中(以便再次查询),然后立即返回
- 如果还不行,则根据B系统提供的新增表单接口(如果有),则以A系统的员工 在B系统创建 该员工的信息,一般创建成功 B系统 会返回能唯一确定这个员工的 B系统id,此时也将映射关系保存到数据库中,然后返回
- 否则,则报错,手动处理
public class EmployeeBaseInfoHandler implements ErpBaseInfoHandler{@Overridepublic String handleField(ErpJsonField jsonField,String extInfo){//在数据库中查询String dbFnumber = this.searchInDb(jsonField);if (!StringUtils.isEmpty(dbFnumber)) {return dbFnumber;}String fname = this.getFname(jsonField);//在第三方系统,如 erp 中查询String fnumberByName = this.searchInErpByFname(jsonField, fname);if (!StringUtils.isEmpty(fnumberByName)){return fnumberByName;}//使用现有的员工信息,保存至第三方系统String fnumber = saveToGetFnumber(jsonField, fname);return fnumber;}
}
重试机制
前文我们提到过 任何一个网络请求 都可能出现 失败情况,因此我们必须要考虑加入重试功能
建议实现方案
推送服务接收到 数据后,立即将能唯一确定数据的 信息保存在数据库,然后将 原始数据 临时缓存在 Redis等缓存容器
保存在数据库的信息,必须能保证足够可以用从 缓存容器 中重新取出 原始数据
最后再进行 值转换、发送网络请求等操作
所有操作成功后,将缓存删除;否则,延长 缓存时间
记得缓存时间 要大于 预期下一次定时任务处理时间,否则还没等到 定时任务来处理自己,缓存的数据就没了
触发机制
定时任务触发
设置原因
- 解决由于非数据错误导致的失败,如网络波动
- 减少人工处理的压力
注意事项
- 设置最大失败次数、缓存超时机制,以便淘汰由于数据错误导致的对接失败
- 设置每次最大处理量,保证不会使得 A系统的服务压力猛增 ,同时 B系统也不一定能够同时处理那么多内容
人工立即触发
设置原因
- 肯定有等不到定时任务,紧急需要重试 的需求
- 方便测试
注意事项
- 不需要跟定时任务一样处理多条任务,往往用户只需要 紧急处理 少量的单据
源头手动触发
设置原因
- 方便测试
- 缓存已经超时,需要重新提交数据
- 数据源有可能已经修改了——尤其在 由于数据错误导致失败时,更需要使用
注意事项
- 源头重新发起后,如果旧数据仍留在 缓存中,应该用新数据覆盖
- 源头重新发起后,旧数据对接产生的日志记录不应该被抹去,应该被保留下来,以便追踪问题
系统数据一致性(幂等)机制实现
但是如果重试,有可能就会出现以下情况
问题分析
为什么会出现上图的结果?关键在于 A系统在请求失败后无法判断请求是否成功了
想一下,如果你是B系统的设计者,你要怎样才能让 A系统 知道是否成功了呢?
幂等机制支持关键点
举例
幂等机制需要 B系统来支持,什么样的系统才能支持幂等机制?我以 金蝶K3Cloud ERP 中销售订单表单(下称 SO单)举例,以下是简介:
- so单中的订单号(字段名:
FbillNo
) 是唯一的,支持创建的时候自定义订单号(关键1),如不自定义则自动生成 - 除此之外,每一张创建的单据有另外一个唯一的自增id,称为
FerpId
吧 - erp提供了查询接口,可以根据 订单号 查询 表单信息,包括
FerpId
(关键2)
关键点分析: 存在一个字段,可以在创建阶段指定值并且可以据此值查询到表单是否存在
如果提供以上的条件,就可以实现幂等机制:因为你可以根据上次请求预期结果,来查询上次预期结果是否成功。
可以在创建阶段指定值 的字段
问:这种字段非常多,能传值的字段都是可以指定值的字段,是否需要挑选值做了唯一索引的呢?
答:有更好,没有也可以。为什么呢?因为此值是 A系统自己控制的,A系统在传值时完全可以构造一个全局唯一的 值,比如加入时间戳、全局id生成器之类的。
再问:如果我选的 K 字段没有唯一索引,A系统传的值为 :k123-20200613
,此时保存成功了但A系统没有收到结果,超时失败了;但是B系统的前端用户想搞事情,也创建了一张 K 字段为 k123-20200613
,保存成功了。后来A系统重试时,查询时发现了两张单,不知道哪张单是它推送过去的了,这怎么办?
再答:这就是为啥说 有唯一索引更好 的原因。个人觉得程序无法解决,只能人工来处理,揪出这个”捣蛋鬼”了。
可以据指定值查询到单据是否存在 的字段
这个是幂等机制的基础!因为没有 这个字段或者查询接口不支持 ,则无法在重试前查询 上一次推送的结果是否成功了
实现示例
- A系统在每次重试so单推送erp前,需要根据 so单号 去查询此 so单是否存在于erp
- 如果存在,则将
FerpId
存入数据库,并标记为已成功 - 如果没有查询成功(网络又失败了),则放弃重试
- 如果查询结果说明不存在,则重新推送
注意事项
- 不是所有第三方系统都符合实现幂等机制的条件,也不是所有的 A系统都有实现幂等机制的需求,如发送邮件、短信——多一条感觉也没什么关系(反正都是垃圾邮件/邮箱)
完整的日志机制
推送日志
推送日志是 事后定位bug 重要内容,最好不要只停留在日志文件中,数据库中应该也保留最近一次的错误原因等重要信息
日志基本元素
- 推送时间
- 错误原因
- 推送人(如果有)
- 推送数据(如果是json就得打印,webservice 就算了吧)
日志打印时间点
- 构造数据前:开始构造数据了
- 网络请求前:请求参数
- 网络请求后:请求结果,catch 超时异常
日志打印原则
- 重要操作
- 易错操作
- 反映程序执行链路,也就是可追踪
回调日志
如果B系统会在某个时间回调 A系统的某些接口,一定要打印接收到的原始数据,因为这是 追责的重要依据。
对接第三方系统实操经验分享相关推荐
- stm32移植paho_如何在STM32上移植Linux?超详细的实操经验分享
原标题:如何在STM32上移植Linux?超详细的实操经验分享 刚从硬件跳槽为嵌软时,没有任何一丝的准备.一入职,领导就交代了一项特难的任务--在stm32上移植linux! 瞬间我就懵了,没办法硬着 ...
- 短视频如何赚钱 短视频自媒体实操经验分享+真实
以今日头条为例,视频的播放量主要通过系统推荐来获得,它的智能推荐系统(俗称人工智能)就是把合适的新闻推荐给对他感兴趣的人群.头条每天的内容几百万,不可能靠小编来干预推荐,都是智能机器人根据网友的互动数 ...
- 外卖cps返利怎么做,我的一些实操经验分享
之前写过一篇<近期实操的"月躺赚3000块"的小项目>,很多朋友也因此关注我,不过当时用的是订阅号,引流也都采用的免费渠道,也是认知不够,小"富"即 ...
- stm32可以移植linux系统吗,如何在STM32上移植Linux?超详细的实操经验分享
使用initramfs最简单的方式,莫过于用已经做好的cpio.gz把kernel里面那个空的给换掉.这是2.6 kernel天生支持的,所以,你不用做什么特殊的设置. kernel的config o ...
- 万字干货:大道至简,用户增长模型体系/完整方法论/实操经验分享
相信大家这两年可能都有这种感觉,无论大小厂都开始关注起了增长,都在聊起了增长,在聊这个之前,我们有必要知道2个问题. 1. 什么是增长? 小学时我们都学过这样的数学题:一个水池子打开进水口X小时装满水 ...
- 今日头条自媒体平台实操经验分享,让你月入上万不是梦
最近自媒体火的不得了,无论是今日头条,腾讯自媒体.百度百家还是这两天炙手可热的UC云观,似乎让所有的从业者找到了微信公众号之后的另外一波红利机会! 说实在的,网上赚钱只要你在恰当的时机切入进去,是个傻 ...
- Rook 1.5.1 部署Ceph实操经验分享
一.Rook概述 1.1 Rook简介 Rook 是一个开源的cloud-native storage编排, 提供平台和框架:为各种存储解决方案提供平台.框架和支持,以便与云原生环境本地集成.目前主要 ...
- 【信号调理】用“晶体三极管”搭建CE放大器的实操经验分享
基于的题目 2020 年TI 杯大学生电子设计竞赛--E 题:放大器非线性失真研究装置 使用的元件 博主使用常用的小功率三极管S9013,是NPN型的.由于博主在洞洞板上焊接搭建电路,所以选择了直插的 ...
- 如何用adsense赚钱_我的实操经验分享:如何通过 Adsense 赚3万美金
事实上"Android 技术文章精选"到目前为止已经连续推荐 217 期了,累计订阅人数超过12500,每天推荐国内外优秀的 Android 技术文章,期待大家多多点赞,让更多人看 ...
最新文章
- 什么样的模型是好的模型 好的数据胜于好的特征,好的特征胜于好的算法
- SQL 交集 差集 并集 笛卡尔积 应用实例
- element手机验证格式_Laravel 自定义封装表单验证类
- 执行pip安装的程序:command not found
- dom4j解析XML文件,通过属性查找节点,出现异常
- “华为杯”第17届中国研究生数学建模竞赛B题二等奖论文
- python 获取像素颜色_python如何读取像素值
- Win 10 蓝屏,出现DRIVER_POWER_STATE_FAILURE的解决方法
- linux 查看内存大小命令,Linux查看命令:CPU型号,内存大小,硬盘空间
- JDBC bug : You must configure either the server or JDBC driver
- 多边形标注收缩python代码实现
- 2022年全球100个可持续发展城市榜公布,挪威首都奥斯陆排第一,中国有十个城市入选 | 美通社头条...
- 让你重拾信心的单片机入门,小哥带你了解
- 最小生成树(MST)相关三题
- matplotlib animation 模拟弹簧的强迫振动 以及odeint函数的应用
- 手机文件管理android可以删除吗,手机上什么文件可以删除?答案都在这里了
- 公司绝不会告诉你的20大秘密值得一看很受用
- 中小互联网软件公司的骗局--前言
- 包及权限配置java存储机理绘制
- iOS VS Android ,10年之战,谁是最后赢家?
热门文章
- linux基础命令3(ps、top、ulimit、mkdir、more、cat、diff、grep、touch、cp、find、rm、split、mv、)
- 【来日复制粘贴】数据透视表分类不同账龄
- 数据泄漏检测和溯源技术
- iOS Zip文件压缩
- Tarjan模板 人工栈版Tarjan
- er图转换成关系模型的例题_有关数据库系统的练习题 E-R图的关系画图转换,,急需 谢谢了...
- ESP8266(ESP-12F)+DS18B20+贝壳物联
- SAP UI5 数据类型(data type) 学习笔记
- python爬虫 爬取360图片(非结构化数据)
- 2018年Java面试题