订单导出应对大流量订单导出时的设计问题

背景

2018年4月16日,订单导出跪了。几乎接近于崩溃,导出接口响应很是慢,以致于前端直接报错。最后只能经过重启服务器解决。html

过后排查发现: 当时有多个VIP商家在线上导出,有 X 个大流量导出,都在几十万的订单量导出之间,导出只有m台机器,当时访问Hbase集群大量超时,线程被hang住,最终无力支撑。虽然在预发验证过 100w+ 万订单的导出,但是没预料到多个稍小但也很大的导出同时“侵袭”导出服务器。 自此,开始了“应对VIP大流量导出”的设计优化旅程。

前端

总体设计

这里须要说明下大流量订单导出的总体设计,采用了“批量并发”的处理策略。服务器

  • 因为导出订单量可能很是大,“所有加载到内存,计算报表字段逻辑,再写入文件”的方式很容易把内存撑爆,所以,采用了批次的概念。将所有要导出的订单量,切分红多个批次,每批次处理 Batch 条订单,批量写入指定文件; 批次之间是串行的;架构

  • 为了保证总体性能,当发现要导出订单数大于 Threshold 时,就会经过并发策略来导出。上面的每批次 Batch 条订单,会被切分红 num 个订单,多个线程并发去获取订单详情数据,而后聚合起来进行格式化计算,写入文件。并发

  • 订单导出拉取订单详情先经过订单详情批量API接口,而后要访问多个Hbase表,其中有一个大量级表的一次scanByPrefix和一个稍小量级表的2次scanByPrefix,四个大量级表的屡次batchGet,以及一些稍小量级的batchGet。大量级表和小量级表的差异也就至多一个数量级的差异。性能

  • 合法者不拒。 只要是合法的导出请求,都会开启线程去处理,没有限制。

    大数据

求解之旅

限制与隔离

显然,若是多个大流量订单导出同时来到,假设一个 X 订单的导出。那么就须要 X/num 次详情接口的并发调用, 4X/num 次大量级表的屡次batchGet,X/num次大量级表的 scanByPrefix 和 2X/num 次小量级表的 scanByPrefix, N*X/num次小量级表的batchGet。 假设有 10X 订单量的同时导出,上述每一个数字乘以 10, 而后还要在比较短的时间内完成,可见,1000个线程也不够用啊!优化

所以,大流量订单的合法导出即便不拒绝,也不能当即响应。 这里须要做出一些限制。 好比每次同时只能导出 Y 个VIP大流量导出。但这个策略也是很粗略的。 更优化的策略能够是: 专门设置一个大流量订单导出队列, 检测到大流量导出后先放到该队列,在机器相对空闲的时候再进行处理。线程

最完全的方案是隔离大流量导出。系统稳定性,实质上是资源分配和使用的问题。保证系统稳定性,能够从两个方面来保证: 1. 保证合理的资源分配和利用;2. 隔离不稳定性源,保证总体不受影响。合理的资源利用,包括资源的限速、限流、线程池和链接池的优化; 隔离不稳定性源,便是将可能致使不稳定性的因素隔离在正常服务以外,即便部分出现不稳定,总体也不受影响。架构设计

之因此大流量导出会影响总体导出服务稳定性,正是由于不稳定性源混杂在正常服务中,未作有效隔离。 大流量导出与正常流量导出混杂在一块儿,大流量导出会占用大量线程,致使正常流量导出资源分配不够,从而影响总体服务。 所以,有必要将大流量导出抽离出来,用专门的服务器来完成。这样,正常流量导出始终是稳定的,而大流量导出即便有问题,也只会影响极少数导出,不会影响总体导出服务的稳定性。且能够重试。

识别关键威胁

要真正提高系统稳定性,就要识别出关键威胁并解决它。对于订单导出来讲,有个大量级表的 scanByPrefix 容易超时,消耗大量服务器资源并带来很大压力。之因此用 scan, 这个是历史设计问题。 当时导出总体架构设计从DB迁移到大数据中心来获取数据,总体方案是可行的,但没有意识到这个设计会给大流量导出以及多个大流量导出下的导出系统稳定性埋下隐患。如今看来,干掉 scan ,尽量采用 batchGet 来获取数据,而后在客户端来聚合数据,是一个更好的策略。 遗憾的是,当时没有作太多思考,等意识到这个设计问题时,有点为时过晚。 参阅: 两个设计教训:前瞻性思考与根本性解决方案


Hbase表的访问优化

当时故障的直接缘由是,scanByPrefix 大量超时,致使线程被hang住。跟数据组同窗讨论后,认为这个操做太耗Hbase集群服务器资源。所以,作了三个优化:

  • Hbase超时设定。 Hbase默认的超时设定值比较大,致使线程长久被hang住没法被释放。 合理的超时设置,不必定能避免应用崩溃,但不仔细的超时设置,在应用出现问题征兆时会放大问题;触类旁通,对于应用中所用到的默认设置,都应该仔细斟酌下,量身定制。

  • 加 Hbase 主备切换, 若是主集群访问超时,自动切换到备集群访问,减小主集群压力。

  • scan 在服务端, filter by prefix 移到客户端去过滤。 能够很大程度上减小Hbase集群的压力。作过这个优化后,超时基本没有了。不过也带来了新的问题:导出客户端压力过大。这种作法致使导出应用在scan和filter大流量订单时,CPU和内存都大幅攀升。由于导出须要获取(可能远)超出所需的数据,而后过滤出指定的订单号列表的数据。这说明,有些优化会解决面临的问题,可是会引入新的问题。尽管如此,解决超时仍然是向正确方向迈进了一步,保证Hbase集群的总体稳定性高于单个导出服务器的稳定性。

  • 同一个表的屡次访问进行合并。

减小没必要要的访问

另外一个方法是梳理和优化详情流程,减小没必要要的访问。 新报表或老报表的导出不须要某些字段,就能够不用访问某些Hbase表,减小访问Hbase表的IO流量;此外,只获取须要的字段,也能够减小服务间的传输消耗。若有可能,都应该指定要获取的列集合,避免暴力性获取全部字段的数据。

线程池独立和监控

在故障发生后,发现导出任务提交和订单详情数据拉取共用一个导出任务提交线程池,这样也是有隐患的。所以增长了两个线程池: 批量调用详情接口的线程池和并发获取Hbase数据的线程池,并进行线程池的监控。

小结

一些初始设计在常见场景下并不存在问题,可是在大压力场景下会给系统稳定性带来隐患,这一点往后切要注意。 另外,作系统局部优化时,也要全局考虑,避免由于优化某个局部又引入了新的甚至威胁更大的问题。

相关文章
相关标签/搜索