分页接口的实现,在偏业务的服务端开发中应该很常见,PC时代的各种表格,移动时代的各种feed流、timeline。
出于对流量的控制,或者用户的体验,大批量的数据都不会直接返回给客户端,而是通过分页接口,多次请求返回数据。
而最常用的分页接口定义大概是这样的:
router.get('/list', async ctx => {
const { page, size } = this.query
// ...
ctx.body = {
data: []
}
})
// > curl /list?page=1&size=10
复制代码
接口传入请求的页码、以及每页要请求的条数,我个人猜想这可能和大家初学的时候所接触的数据库有关吧- -,我所认识的人里边,先接触MySQL、SQL Server什么的比较多一些,以及类似的SQL语句,在查询的时候基本上就是这样的一个分页条件:
SELECT FROM
复制代码
或者类似的Redis中针对zset的操作也是类似的:
> ZRANGE
复制代码
所以可能习惯性的就使用类似的方式创建分页请求接口,让客户端提供page、size两个参数。
这样的做法并没有什么问题,在PC的表格,移动端的列表,都能够整整齐齐的展示数据。
但是这是一种比较常规的数据分页处理方式,适用于没有什么动态的过滤条件的数据。
而如果数据是实时性要求非常高的那种,存在有大量的过滤条件,或者需要和其他数据源进行对照过滤,用这样的处理方式看起来就会有些诡异。
页码+条数 的分页接口的问题
举个简单的例子,我司是有直播业务的,必然也是存在有直播列表这样的接口的。
而直播这样的数据是非常要求时效性的,类似热门列表、新人列表,这些数据的来源是离线计算好的数据,但这样的数据一般只会存储用户的标识或者直播间的标识,像直播间观看人数、直播时长、人气,这类数据必然是时效性要求很高的,不可能在离线脚本中进行处理,所以就需要接口请求时才进行获取。
而且在客户端请求的时候也是需要有一些验证的,举例一些简单的条件:
确保主播正在直播
确保直播内容合规
检查用户与主播之间的拉黑关系
这些在离线脚本运行的时候都是没有办法做到的,因为每时每刻都在发生变化,而且数据可能没有存储在同一个位置,可能列表数据来自MySQL、过滤的数据需要用Redis中来获取、用户信息相关的数据在XXX数据库,所以这些操作不可能是一个连表查询就能够解决的,它需要在接口层来进行,拿到多份数据进行合成。
而此时采用上述的分页模式,就会出现一个很尴尬的问题。
也许访问接口的用户戾气比较重,将第一页所有的主播全部拉黑了,这就会导致,实际接口返回的数据是0条,这个就很可怕了。
let data = [] // length: 10
data = data.filter(filterBlack