前一段时间,碰到一个问题,后端提供的API是批量接口,允许在一个HTTP请求中放上N个业务上的请求,一起处理,完成后一起返回,但是我们的前端又是以单个请求为主,这样势必导致很多http请求仅仅包含单个业务请求,大量的把带宽浪费在http head,以及把cpu浪费在http协议的解析上,而改写现有代码让请求尽量合并在起来是一件既费力又会遭遇多线程间无法合并等问题的麻烦事情。。。那么,这里就需要找到一个不需要大幅修改现有的代码,并且能完成请求合并的方法。
软件上的问题,大部分可以通过添加一个层来搞定,这次的这个问题也不例外。
首先,来确定一下范围,为了简化问题,我们做了下面几个限制:
- 只支持该请求有一个类似TRequest[]的参数,并且其返回值为类似TResponse[],乍一看,好像很多请求不能满足这个要求,不过,我们可以将其他参数放在Tuple之类的对象中,把请求伪装成一个类型的数组
- 只支持一个请求有且必须有一个返回,也就是说,不能一个请求返回多个结果,也不可以不返回结果
- 只支持请求的顺序与返回的顺序一致,换句话说,请求1、2、3,必须返回1、2、3,顺序必须一致。很多api不会这么设计,不过,我们可以加一些预处理,使这个条件满足
然后,需要分析一下合并策略:
- 不是所有请求都能合并在一起的,这取决于后端API的实现,例如:不同权限角色可能不能合并,部分参数可能必须是一致的
- 批量请求通常会有数量上限,不太可能有后端代码会支持无限数量的批量
- 合并请求往往是以增加延迟为代价的,如何在两者之间平衡是一门艺术,也就是说无法说哪种方式是最好的,只能凭感觉
这里,我们做了这样的一个选择:
- 合并需要一些规则做约束,这些规则可以添加很多,但是必须是轻量级的,否则会影响合并的效率(这里我们只能假设,万一真的有人在里面写个Thread.Sleep,我们也只能呵呵了)
- 延迟和合并之间的平衡,我们决定让链接数来决定,也就是保持总链接数不大于某个值,如果链接很空闲,就直接出去,不做任何合并,反之,如果链接已经被占满,就尝试与排队中的请求合并,无法合并,就新建请求
- 不支持排队中的请求再排序,这里假定所有请求都是相同优先级的
到这里,我们的目标已经明确,剩下的就是写代码实现了。
----------------------------此处省去过程5000字------------------------------
最后,分享一下这段代码。