对接第三方系统实操经验分享

前言

为使得指示性更强,有以下名词说明

  • 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;
  • 解释
  1. 类似fastjson@JSONField 注解一样,对字段进行重命名,根据目标系统的字段名进行关联
  2. 在重命名的同时,亦可以对字段值的类型进行标记,为字段值转换机制打上标记
  3. 可以按照对接框架的需要附上所需要的属性,比如说 字段是否需要强转成数字形式,单位是多少,保留多少位小数等属性,也是为了字段值转换机制打基础
  • 优点:
  1. 注解写在字段上,清晰易懂,维护直接在代码中
  2. 反射也更好获取——工具类更好写
  • 缺点:
  1. 由于字段之间的映射关系已经以 注解的形式 硬编码写在了代码里,想要 修改就必须重构代码,而重构往往对于 生产环境是不可容忍的
  2. 有可能污染了代码,尤其是 pojo(可以通过 新建一个vo类专门用于对接)
  • 参考代码片段:一个详细的注解

方案二:使用数据库建立字段关联关系

其实也就是将注解的内容 放进数据库里进行维护,通过如前端填写注解表格进行 对接交互,可实时增删改,更为灵活

  • 优点:
  1. 灵活,不使用硬编码
  2. 如果不涉及值转换机制的新增,基本可以实现 不用写任何代码,不用重构代码,即可新对接一张表单。
  • 缺点
  1. 反射工具类书写难度加大:需要考虑到 字符串的拼写、合法性以及嵌套字段名解析等问题
  • 参考代码:a2b对接脚手架项目

字段值转换方案

我们知道 A系统中供应商id 为 333 的供应商 = B系统中 供应商唯一主键 为 S303 的供应商,我们需要在值转换阶段,将其转换成 B系统能够接受的值

建议实现步骤

  1. 在字段名转换阶段,给字段打上 值转换类型属性(如上述注解中的dataType属性)
  2. 维护值转换 策略模式集合,各种转换策略实现同一个接口
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),//....}
  1. 在值转换阶段,根据值转换类型进入对应的转换策略中

转换策略推荐实现方案——以员工信息为例

  • 输入的参数含有 需要转换的A系统的员工id值 : A23,要转换成 B系统的值 为 B100567
  • 以下 值映射关系 是指 A系统的值 => B系统的值,如A系统员工id A23 => B系统 的 B100567

值映射关系不稳定

也就是说 A23 => B100567 在某时刻是成立的,但是之后又不成立了

此种情况只能通过 B系统开发转换接口,然后A系统每次转换值时 调用接口 实时查询对应的值

值映射关系稳定

  1. 根据员工id查找数据库,如果可以找到映射关系则立即返回
  2. 根据B系统提供的查询接口(如果有),则根据一定的筛选条件获取对应的B系统的值(如根据员工工号 查询B系统的员工id),如果查询成功,则保存映射关系保存在数据库中(以便再次查询),然后立即返回
  3. 如果还不行,则根据B系统提供的新增表单接口(如果有),则以A系统的员工 在B系统创建 该员工的信息,一般创建成功 B系统 会返回能唯一确定这个员工的 B系统id,此时也将映射关系保存到数据库中,然后返回
  4. 否则,则报错,手动处理
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;}
}

重试机制

前文我们提到过 任何一个网络请求 都可能出现 失败情况,因此我们必须要考虑加入重试功能

建议实现方案

  1. 推送服务接收到 数据后,立即将能唯一确定数据的 信息保存在数据库,然后将 原始数据 临时缓存在 Redis等缓存容器

    保存在数据库的信息,必须能保证足够可以用从 缓存容器 中重新取出 原始数据

  2. 最后再进行 值转换、发送网络请求等操作

  3. 所有操作成功后,将缓存删除;否则,延长 缓存时间

    记得缓存时间 要大于 预期下一次定时任务处理时间,否则还没等到 定时任务来处理自己,缓存的数据就没了

触发机制

定时任务触发
设置原因
  1. 解决由于非数据错误导致的失败,如网络波动
  2. 减少人工处理的压力
注意事项
  1. 设置最大失败次数、缓存超时机制,以便淘汰由于数据错误导致的对接失败
  2. 设置每次最大处理量,保证不会使得 A系统的服务压力猛增 ,同时 B系统也不一定能够同时处理那么多内容
人工立即触发
设置原因
  1. 肯定有等不到定时任务,紧急需要重试 的需求
  2. 方便测试
注意事项
  1. 不需要跟定时任务一样处理多条任务,往往用户只需要 紧急处理 少量的单据
源头手动触发
设置原因
  1. 方便测试
  2. 缓存已经超时,需要重新提交数据
  3. 数据源有可能已经修改了——尤其在 由于数据错误导致失败时,更需要使用
注意事项
  1. 源头重新发起后,如果旧数据仍留在 缓存中,应该用新数据覆盖
  2. 源头重新发起后,旧数据对接产生的日志记录不应该被抹去,应该被保留下来,以便追踪问题

系统数据一致性(幂等)机制实现

但是如果重试,有可能就会出现以下情况

问题分析

为什么会出现上图的结果?关键在于 A系统在请求失败后无法判断请求是否成功了

想一下,如果你是B系统的设计者,你要怎样才能让 A系统 知道是否成功了呢?

幂等机制支持关键点

举例

幂等机制需要 B系统来支持,什么样的系统才能支持幂等机制?我以 金蝶K3Cloud ERP 中销售订单表单(下称 SO单)举例,以下是简介:

  1. so单中的订单号(字段名:FbillNo) 是唯一的,支持创建的时候自定义订单号(关键1),如不自定义则自动生成
  2. 除此之外,每一张创建的单据有另外一个唯一的自增id,称为 FerpId
  3. erp提供了查询接口,可以根据 订单号 查询 表单信息,包括 FerpId(关键2)
关键点分析: 存在一个字段,可以在创建阶段指定值并且可以据此值查询到表单是否存在

如果提供以上的条件,就可以实现幂等机制:因为你可以根据上次请求预期结果,来查询上次预期结果是否成功

可以在创建阶段指定值 的字段

问:这种字段非常多,能传值的字段都是可以指定值的字段,是否需要挑选值做了唯一索引的呢?
答:有更好,没有也可以。为什么呢?因为此值是 A系统自己控制的,A系统在传值时完全可以构造一个全局唯一的 值,比如加入时间戳、全局id生成器之类的。
再问:如果我选的 K 字段没有唯一索引,A系统传的值为 :k123-20200613,此时保存成功了但A系统没有收到结果,超时失败了;但是B系统的前端用户想搞事情,也创建了一张 K 字段为 k123-20200613,保存成功了。后来A系统重试时,查询时发现了两张单,不知道哪张单是它推送过去的了,这怎么办?
再答:这就是为啥说 有唯一索引更好 的原因。个人觉得程序无法解决,只能人工来处理,揪出这个”捣蛋鬼”了。

可以据指定值查询到单据是否存在 的字段

这个是幂等机制的基础!因为没有 这个字段或者查询接口不支持 ,则无法在重试前查询 上一次推送的结果是否成功

实现示例
  1. A系统在每次重试so单推送erp前,需要根据 so单号 去查询此 so单是否存在于erp
  2. 如果存在,则将 FerpId存入数据库,并标记为已成功
  3. 如果没有查询成功(网络又失败了),则放弃重试
  4. 如果查询结果说明不存在,则重新推送
注意事项
  1. 不是所有第三方系统都符合实现幂等机制的条件,也不是所有的 A系统都有实现幂等机制的需求,如发送邮件、短信——多一条感觉也没什么关系(反正都是垃圾邮件/邮箱)

完整的日志机制

推送日志

推送日志是 事后定位bug 重要内容,最好不要只停留在日志文件中,数据库中应该也保留最近一次的错误原因等重要信息

日志基本元素
  1. 推送时间
  2. 错误原因
  3. 推送人(如果有)
  4. 推送数据(如果是json就得打印,webservice 就算了吧)
日志打印时间点
  1. 构造数据前:开始构造数据了
  2. 网络请求前:请求参数
  3. 网络请求后:请求结果,catch 超时异常
日志打印原则
  1. 重要操作
  2. 易错操作
  3. 反映程序执行链路,也就是可追踪

回调日志

如果B系统会在某个时间回调 A系统的某些接口,一定要打印接收到的原始数据,因为这是 追责的重要依据。

对接第三方系统实操经验分享相关推荐

  1. stm32移植paho_如何在STM32上移植Linux?超详细的实操经验分享

    原标题:如何在STM32上移植Linux?超详细的实操经验分享 刚从硬件跳槽为嵌软时,没有任何一丝的准备.一入职,领导就交代了一项特难的任务--在stm32上移植linux! 瞬间我就懵了,没办法硬着 ...

  2. 短视频如何赚钱 短视频自媒体实操经验分享+真实

    以今日头条为例,视频的播放量主要通过系统推荐来获得,它的智能推荐系统(俗称人工智能)就是把合适的新闻推荐给对他感兴趣的人群.头条每天的内容几百万,不可能靠小编来干预推荐,都是智能机器人根据网友的互动数 ...

  3. 外卖cps返利怎么做,我的一些实操经验分享

    之前写过一篇<近期实操的"月躺赚3000块"的小项目>,很多朋友也因此关注我,不过当时用的是订阅号,引流也都采用的免费渠道,也是认知不够,小"富"即 ...

  4. stm32可以移植linux系统吗,如何在STM32上移植Linux?超详细的实操经验分享

    使用initramfs最简单的方式,莫过于用已经做好的cpio.gz把kernel里面那个空的给换掉.这是2.6 kernel天生支持的,所以,你不用做什么特殊的设置. kernel的config o ...

  5. 万字干货:大道至简,用户增长模型体系/完整方法论/实操经验分享

    相信大家这两年可能都有这种感觉,无论大小厂都开始关注起了增长,都在聊起了增长,在聊这个之前,我们有必要知道2个问题. 1. 什么是增长? 小学时我们都学过这样的数学题:一个水池子打开进水口X小时装满水 ...

  6. 今日头条自媒体平台实操经验分享,让你月入上万不是梦

    最近自媒体火的不得了,无论是今日头条,腾讯自媒体.百度百家还是这两天炙手可热的UC云观,似乎让所有的从业者找到了微信公众号之后的另外一波红利机会! 说实在的,网上赚钱只要你在恰当的时机切入进去,是个傻 ...

  7. Rook 1.5.1 部署Ceph实操经验分享

    一.Rook概述 1.1 Rook简介 Rook 是一个开源的cloud-native storage编排, 提供平台和框架:为各种存储解决方案提供平台.框架和支持,以便与云原生环境本地集成.目前主要 ...

  8. 【信号调理】用“晶体三极管”搭建CE放大器的实操经验分享

    基于的题目 2020 年TI 杯大学生电子设计竞赛--E 题:放大器非线性失真研究装置 使用的元件 博主使用常用的小功率三极管S9013,是NPN型的.由于博主在洞洞板上焊接搭建电路,所以选择了直插的 ...

  9. 如何用adsense赚钱_我的实操经验分享:如何通过 Adsense 赚3万美金

    事实上"Android 技术文章精选"到目前为止已经连续推荐 217 期了,累计订阅人数超过12500,每天推荐国内外优秀的 Android 技术文章,期待大家多多点赞,让更多人看 ...

最新文章

  1. 什么样的模型是好的模型 好的数据胜于好的特征,好的特征胜于好的算法
  2. SQL 交集 差集 并集 笛卡尔积 应用实例
  3. element手机验证格式_Laravel 自定义封装表单验证类
  4. 执行pip安装的程序:command not found
  5. dom4j解析XML文件,通过属性查找节点,出现异常
  6. “华为杯”第17届中国研究生数学建模竞赛B题二等奖论文
  7. python 获取像素颜色_python如何读取像素值
  8. Win 10 蓝屏,出现DRIVER_POWER_STATE_FAILURE的解决方法
  9. linux 查看内存大小命令,Linux查看命令:CPU型号,内存大小,硬盘空间
  10. JDBC bug : You must configure either the server or JDBC driver
  11. 多边形标注收缩python代码实现
  12. 2022年全球100个可持续发展城市榜公布,挪威首都奥斯陆排第一,中国有十个城市入选 | 美通社头条...
  13. 让你重拾信心的单片机入门,小哥带你了解
  14. 最小生成树(MST)相关三题
  15. matplotlib animation 模拟弹簧的强迫振动 以及odeint函数的应用
  16. 手机文件管理android可以删除吗,手机上什么文件可以删除?答案都在这里了
  17. 公司绝不会告诉你的20大秘密值得一看很受用
  18. 中小互联网软件公司的骗局--前言
  19. 包及权限配置java存储机理绘制
  20. iOS VS Android ,10年之战,谁是最后赢家?

热门文章

  1. linux基础命令3(ps、top、ulimit、mkdir、more、cat、diff、grep、touch、cp、find、rm、split、mv、)
  2. 【来日复制粘贴】数据透视表分类不同账龄
  3. 数据泄漏检测和溯源技术
  4. iOS Zip文件压缩
  5. Tarjan模板 人工栈版Tarjan
  6. er图转换成关系模型的例题_有关数据库系统的练习题 E-R图的关系画图转换,,急需 谢谢了...
  7. ESP8266(ESP-12F)+DS18B20+贝壳物联
  8. SAP UI5 数据类型(data type) 学习笔记
  9. python爬虫 爬取360图片(非结构化数据)
  10. 2018年Java面试题