版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/linzhiqiang0316/article/details/100532267
需求:
每间隔2个小时,定时从亚马逊接口获取商家广告数据
细则说明:
- 商家指的是亚马逊商家授权给平台的用户。(类似淘宝店)
- 亚马逊接口指的是亚马逊对外提供数据的公开接口。
- 广告数据指的是商家在亚马逊平台添加的广告,产生的点击率,转化率等广告相关数据。
现有问题:
- 授权商家达到一定量级,每2个小时需要获取大量广告数据,存在2个小时数据获取不完的问题。
- 短时间内获取大量数据,导致服务内存飙升,导致内存OOM。
方案构思:
针对上述需求,我们主要需要攻克两个问题:
- 如何快速高效的获取到亚马逊接口数据
- 如何降低服务内存消耗
我们目前的架构如下所示:
备注:
目前架构主要通过xxl-job、springboot框架实现,通过xxl-job分布式调度框架实现定时发送消息到MQ中,springboot中集成MQ,通过监听MQ,来进行消息消费,获取所需数据。
架构问题:
但是随着绑定的商家数量越来越大之后,发现广告消费服务(springboot)不能在2个小时之内,造成严重的消息堆积。
最简单的办法就是增加MQ消费线程,这样就可以加快数据获取速度,but这种方案看似解决了上面的问题,但是在运行一段时间后就发现服务cpu飙升、mysql数据库cpu飙升,直接导致服务宕机,服务停止。
问题分析:
所以仅仅只是提高MQ线程数量肯定是不行的,我们需要对架构进行优化才能彻底解决这个问题。
我们先来分析当前架构有什么问题?
消息队列中每一个消息执行动作太多,消耗很长的时间和内存。为什么这么说呢,首先服务在消费消息的时候,需要经过如过程:
- 第一步需要从亚马逊获取商家广告数据
- 第二步需要解析亚马逊数据,封装成我们入库需要的对象
- 第三步就是将这些入库对象插入数据库中。
我们从这三个步骤就可以看出来,每一步都需要消耗一定内存,而且步骤没有细分,我们没有办法简单的通过增加线程数来提高服务的消费能力。
因为从亚马逊获取数据,还会受到服务带宽的限制,但是因为我们一个消息执行动作过多,没办法将服务的下载带宽利用到最大。
最后还有一个很重要的原因,每一个消息的第三步都需要插入数据库,这将会导致数据库插入操作频繁,我们来设想一下,每个1分钟插入1w条数据和每隔30分钟插入60w数据,哪个效率会更高。
分析到这边,我们可以很明显的看出,当前架构问题主要出在:
- 每一个消息消费的执行动作过多
- 入库太频繁,且入库数据不均匀
- 服务的职能划分不明显,没办法简单的进行横向扩容
- 无法充分的利用服务下载带宽,导致下载速度上限低
架构优化:
针对上诉问题分析结果,我们需要做如下的优化:
- 拆分消息的执行力度,每一个消息只做一件事情,提高单个消息的执行速度。
- 将需要入库的对象先存储到第三方缓存中(redis),然后定时进行统一入库操作。
- 为了提高下载带宽的利用率,我们可以先将文件下载到本地,然后再做其它的处理。
优化之后的架构可以将下载带宽和服务资源发挥到最大,因为亚马逊返回的数据IO流(不会耗费内存),所以我们可以开多一点的线程来进行数据下载(加到服务下载带宽的峰值左右)。文件解析速度非常快,但是又比较耗费内存,所以我们可以设置少一点的线程数,最后的统一入库可以根据mysql服务器的性能来设置线程数量。
这也是今天要给大家介绍的下载-解析-入库模型,叙述过程虽然简单,但是在模型搭建过程却遇到很多问题,为了大家能够少走弯路,我下面给大家分享一下搭建血泪历程。
搭建巨坑:
- 下载、解析、入库线程参数设置不合理,导致运行内存过大,服务OOM宕机,第1次卒。。。
- 没有对IO流进行压缩,导致下载的文件过大,直接怼爆磁盘,服务宕机,第2次卒。。。
- 没有对已经解析过的文件进行处理,导致磁盘快速爆满,服务宕机,第3次卒。。
- 因为网络存在不稳定因素,所以存在一定损坏的文件,解析损坏文件出现异常没有处理,导致损坏文件没没有及时删除,久而久之磁盘饱满,第4次卒。。。
- 下载速度一直上不去,一度怀疑是程序问题(问题出现在服务下载带宽上面),所以拼命加线程,导致内存飙升,服务宕机,第5次卒。。。
- IO下载没有使用buffer流缓存,导致下载速度受限,MQ的下载消息堆积,第6次卒。。
- 使用了buffer流缓存,但是缓存的byte设置过大,导致服务内存OOM,服务宕机,第7次卒。。。
- 存入redis的数据没有进行压缩,导致redis内存溢出,redis服务宕机,第8次卒。。。
- 统一入库后,没有及时删除redis缓存数据,导致redis内存溢出,redis服务宕机,第9次卒。。。
- 没有合并redis删除指令,循环大批量删除redis数据,导致redis中cpu飙升,redis服务宕机,第10次卒。。。
- redis因外挂掉,导致解析数据全部插入失败,没有进行错误重试,数据丢失(等同删库跑路),第11次卒。。。
- 入库成功需要推送到广告数据统计队列,因为数据没有去重,一下次推送上百万的数据,MQ内存溢出,MQ服务宕机,第n次卒。。。
搭建后记:
你以为这样就结束了???还早着呢,请童鞋们一起思考如下问题
- 文件下载到本地磁盘后,后期如何进行分布式扩容???
- 因为文件解析插入redis缓存、入库后删除redis缓存有可能会同时进行,那如何保证统一入库的数据不会缺失???
- 解析超大文件是,如何保证速度和内存???
- 下载、解析、入库三个步骤如何进行关联,才能保证服务性能和可靠性达到最大???