电商API接口——实现数据同步的实时数据封装接口

常见的数据同步/集成场景多发生于不同的存储系统、不同的存储格式,如从 mysql 同步数据至数仓、excel 或 csv 导入数据库中,但是众多数据同步解决方案很少涉及从 http 接口同步数据。

如淘宝、拼多多等电商平台平台,平台内部不同团队之间的数据打通,很多的开源数据集成工具可以满足大部分场景。但是像三方卖家入驻电商平台后,就需要在电商平台注册自研应用,通过开放平台提供的接口打通自研应用与电商平台的数据壁垒,准确及时地获取到商品、订单、物流、售后以及评价等实时变更数据,而无法实现数据库 binlog 或消息队列等形式的数据同步。

本系列文章描述在对接快手、淘宝等开放平台同步商品、详情,订单、售后单等数据的一些经验总结和实践方案。

数据同步接口

根据数据量和实时性的不同需求,开放平台数据接口查询接口总共有 2 种形式:

  • 详情接口。

    • 详情接口又分为单详情接口和批量详情接口。如查询店铺信息,单详情接口只支持根据单个店铺 id 获取店铺信息,而批量详情接口可以同时传入 20 个店铺 id,在需要获取 100 个店铺信息的情况下,前者需要发起 100 次调用,后者只需要调用 5 次。

    • 单实体和多实体。如获取店铺详情,使用店铺 id 即可获取,而对于 sku 库存详情,需要同时传入 sku id 和仓库 id。对于 sku 库存,需要同时遍历 sku 和仓库,才能获取完整的 sku 库存数据。

  • 列表接口,或者分页接口。比如店铺下的商品列表接口,仓库列表,平台类目列表等。

  • 增量接口。支持时间范围查询的分页接口。比如订单列表接口,根据订单修改时间分页查询。

而数据同步的难点也在于对支持时间范围查询的分页接口准确、及时地同步数据,下面也主要介绍增量接口的几种实现。

增量接口

1.普通形式

根据时间范围进行分页请求的基本实现,请求参数中带有开始结束时间加上分页参数。

请求参数和响应结果如下:

{
 "startTime": "2021-11-11 00:00:00",
 "endTime": "2021-11-12 00:00:00",
 "pageIndex": 1,
 "pageSize": 50 
}

[
  {...},
  {...},
  {...}
]

请求接口时需要携带时间范围和分页信息,而接口响应为数据列表,响应不包含分页信息,判断是否请求下一页,需要根据当前响应数据列表 size 是否等于请求参数中的 pageSize。github 的 list-commits 就是此种类型。

2.响应包含分页信息

普通形式接口响应是不包含分页信息,github 的 list-commits 接口获取分页信息需要一些特殊的技巧。

在管理后台类型的 web 页面中经常可以看到支持分页的列表页,这种页面接口的返回数据一般如下:

{
 "startTime": "2021-11-11 00:00:00",
 "endTime": "2021-11-12 00:00:00",
 "pageIndex": 1,
 "pageSize": 50 
}

{
 "dataCount": 10000,
 "pageIndex": 1,
 "pageSize": 50,
 "datas": []
}

接入方在判断是否有一页的时候,可以判断 pageIndex * pageSize 是否大于 dataCount 来决定是否继续请求。

这种响应结果,接入方可以根据 dataCount 和 pageSize 计算得出 maxPageIndex,因此分页请求形式可以从第一页请求到最后一页,也可以从最后一页请求到第一页。

一般情况下,开发者都会直接从第一页开始请求,直到最后一页,什么情况下才会出现不得不从最后一页请求呢?当出现修改时间陷阱的时候。

修改时间陷阱

开放平台往往也会根据业务需求,提供不同类型的时间范围,比如订单场景,有订单创建时间,修改时间,支付时间,出库时间等。一般情况下,订单创建时间、支付时间、出库时间等一旦确定就不会再改变,修改时间为每次订单发生状态变更如支付、发货、确认收货等都会将修改时间置为当前时间。如果使用不会变动的订单创建时间等来同步,一旦订单发生变更,如出现退货退款,这些时间是无法感知到订单实时变动的,而使用修改时间,无论是订单创建、还是用户支付、卖家发货,一旦订单有变更,都会反映到修改时间,从而可以实时地获取订单状态变更。

对于数据接入者来说来说,最喜欢的时间类型为修改时间,根据修改时间同步可以确保数据发生修改后实时地同步过来。

因为订单状态每次发生变更,修改时间都会变动。如果正好处在同步时间区间内的订单发生了变更,会存在一个丢数据的陷阱:必须按照修改时间降序进行同步,原因如下:

假设有订单数据在某个时间范围内从左向右按照修改时间升序排序,效果如下:

1 2 3 4 5    6 7 8 9 10    11 12 13 14 15    16 17 18 19 20

在同步这个时间范围内,如果 20 条数据没有发生任何修改,分页拉取第一页 1 ~ 5,第二页 6 ~ 10 等可以确保数据不会漏。

但是在拉取第一页 1 ~ 5 后,数据 5 发生修改,其余数据不变,拉取第二页时再按照修改时间升序排序,此时排序效果如下:

1 2 3 4 6    7 8 9 10 11    12 13 14 15 16    17 18 19 20

可以看到数据 6 被归到第一页中,之后拉取第二页时会从数据 7 开始,数据 6 被遗漏。

按照修改时间同步,需要分页请求多次才能同步完成时,中间数据发生修改会导致数据排序顺序发生变动,从而造成数据遗漏。

处理方式也很简单,从最后一页向前拉即可。

同样是上面的 20 条数据,在同步完最后一页数据 16 ~ 20 条后,数据 5 发生变更,同步倒数第二页时,同步到的数据为 12 ~ 16,逐步推进分页,在最后一次请求中同步到数据 1 ~ 6。

倒着拉虽然不会出现数据遗漏的情况,但是可以观察到数据 16 是被重复拉取的,接入方需要做好数据更新的幂等性。

数据接入时需确定时间类型,会不会发生变动,以及排序顺序:

  • 开放平台根据修改时间升序排序时,接入方需要从后向前翻页;

  • 开放平台根据修改时间降序排序时,接入方需要从前往后翻页。

3.翻页优化:取消 dataCount ,使用 hasNext

对于分页接口,普遍存在深翻页潜在风险。开放平台在实现分页接口时,会尽量减少深翻页的影响或者直接规避深翻页,使用 hasNext 代替 dataCount 就是一种减少翻页消耗的手段。

请求参数和响应结果如下:

{
 "startTime": "2021-11-11 00:00:00",
 "endTime": "2021-11-12 00:00:00",
 "pageIndex": 1,
 "pageSize": 50 
}

{
 "hasNext": true,
 "pageIndex": 1,
 "pageSize": 50,
 "datas": []
}

对于关系型数据库如 mysql,返回结果中的 dataCount 和 datas 无法在一条 sql 中查询获取,需分别执行一次 count 和 select limit offset 请求才能获取 dataCount 和 datas

接口响应包含 dataCount 字段的目的是为了方便接入方判断何时中止分页请求,开放平台使用 boolean 类型的 hasNext 字段,帮助接入方判断是否到达最后一页。

开放平台该如何确定 hasNext 的值呢?执行 select 语句时设置 size 为 pageSize + 1,如果查询语句返回数据量为 pageSize + 1,存在下一页,否则不存在。

接口不返回 dataCount 字段,可以避免 count 查询,极大提高接口性能。

淘宝开放平台接口大量采用此种形式,如查询卖家已卖出的增量交易数据(根据修改时间)。

使用 hasNext 时需要开放平台根据修改时间降序排列,因为接入方无法实现从后向前翻页。

4.翻页优化:取消 pageIndex,使用 cursor

分页接口普遍惧怕深翻页,深分页会拖累接口响应时间,对数据库造成较大压力,带来潜在的系统崩溃风险,而开放平台保证稳定性必须小心处理深分页。

深分页 sql 的问题在于过深的排序:select * from table limit 50 offset 1000000

一般解决深分页的思路是使用 search afterselect * from table where id > 2000000 limit 50

cursor 就是用来实现 search after 的一种方式:取 datas 的最后一条数据的 timestame + id 作为 cursor,交互时第一次请求时 cursor 为空不传入,之后每次请求传入响应中 cursor 值,直到 cursor 返回一个特殊标识,分页结束。

请求参数和响应结果如下:

{
 "startTime": "2021-11-11 00:00:00",
 "endTime": "2021-11-12 00:00:00",
 "cursor": "1639487400913_5",
 "pageSize": 50 
}

{
 "cursor": "1639487400918_10",
 "pageSize": 50,
 "datas": []
}

开放平台接口对 cursor 进行解析构建 select 语句:

select * 
from table 
where id > ${id} 
and time >= ${timestame} 
and time < ${endTime} 
order by time, id
limit ${pageSize} 

快手开放平台大量使用采用此种形式,如获取订单列表v2。

接口常见限制

开放平台为了保护接口安全,会对接口进行一定限制:

  • 开始时间结束时间相差不能过大以避免深翻页。比如不能超过 7 天

  • 只允许同步近期数据。比如订单类只允许同步 90 天内订单

  • 减少每次请求数据量。比如限制每页数据最多 100 条

  • 接口限流。限制接口请求并发和 qps

电商API接口——实现数据同步的实时数据封装接口_第1张图片

你可能感兴趣的:(python,算法,数据结构,json,java,c++,c语言)