性能优化案例(一):通过修改路由算法提升系统性能

性能优化案例(一):通过修改路由算法提升系统性能

概述

一般性能优化方法有两种:一是,从代码层面优化,提升某个方法的性能,从而提升单机性能;二是,从系统结构层面优化,通过减少无用功,来减少资源消耗。这里,我们写一个用第二种方法优化性能的例子。

系统结构

首先,说一下最初的系统结构。我们有一个机票搜索系统,架构如下所示:

性能优化案例(一):通过修改路由算法提升系统性能

  1. provider 提供搜索服务;
  2. consumer 负责向 provider 发起搜索请求;
  3. router 提供路由功能,按航线将请求哈希到 provider 上处理,哈希是为了提升 provider 上缓存的命中率。当然,为了减小发布抖动,采用的是一致性哈希算法。

在实际中,一个大航线可能会有几百个航班,最小的航线就只有几个航班,同时一次搜索请求,会带上几十个代理商,每个代理商都有自己的报价。假如是某次请求的航线有 200 个航班,有 30 个代理商,那就有 6000 个航班,这样的计算量是很大的。为了加速大航线的查询速度,在 router 部分,将请求又按照代理商维度进行了拆分,将从 consumer 来的一次请求,分散成多个请求发送给 provider 处理。router 路由算法如下图所示:

性能优化案例(一):通过修改路由算法提升系统性能

注:这里不考虑 provider 是如何返回计算结果的,无论是同步返回,还是异步回数,都不影响后续的优化方案。

provider 里的搜索服务是如何运行的呢?可以看下面的图:

性能优化案例(一):通过修改路由算法提升系统性能

在接收到搜索请求后,先异步并行的调用外部接口获取四种资源A、B、C、D,然后再获取报价数据,最后进行计算并返回结果。报价数据是有缓存的,其他数据均无缓存(主要是重复少,就算加缓存命中率也不高)。

当 1 次请求在 router 处分散成 N 个时,这 N 次请求获取的 A、B、C、D 四种资源是一样的,报价数据则各不相同。假设 N 为 30,那一次搜索请求获取的就要请求 30 次 A、B、C、D 数据服务。原本我们按代理商维度来拆分请求,是为了加速大航线的查询。但是由于没有针对大小航线做不同的处理,导致小航线也被拆分了。拆分之前 1 次搜索请求,只需要获取一次 A、B、C、D 四种资源,拆分之后变成了 30 次。对于小航线来说,不拆分处理,其处理耗时也是满足要求的,拆分之后,相当于白白增加了 29*4 次外部调用。

这里再总结一下,要解决的问题是什么?当我们将 1 次搜索请求拆分成 N 个处理时,对于小航线,拆分请求没有导致处理加快多少,反而白白多了 30*4 次外部调用。而大航线也应该适当控制分散程度,比如 1 个请求拆分成 30 个也有点太散了,拆成 10 个比较合理。

优化方法

原先 router 请求拆分里的 N 等于一次请求里的代理商个数,优化方法是,减少拆分出来的子请求数目,也就是减小 N。举个例子,对于航班数量是个位数的小航线,N 可以设置成 5,对于大航线 N 可以设置成 10.

优化效果

  1. router 机器调用 provider 的 QPS 降低了 50%,而 CPU 使用率和网络带宽降低了 25%。
  2. provider 机器的 CPU 使用率和网络带宽降低了 15%。
  3. provider 调用 ABCD 四种接口的 QPS 降低了 50%。
  4. ABCD 所在系统资源使用率也下降了 15% 到 40% 不等,这是因为 ABCD 这四个接口的请求里,来自搜索系统的请求占整体的一半多。