七牛数据处理架构变迁
据统计,互联网数据量正以每三年翻一番的速度膨胀,其中,95%以上都是非结构化数据,且这个比例仍在不断提升。如今,互联网已全面覆盖大家生 活的方方面面,每个人的消费行为、娱乐行为和社交行为都将产生海量的图片、音视频、网络日志等非结构化数据。非结构化数据的持续在线及数据种类的多样对数 据的处理提出了很高的要求。作为国内最专业的数据管理平台,七牛已覆盖国内50%以上的互联网用户。那么,七牛数据处理架构都经过了哪些演变呢?小编带你 一探究竟。
七牛云存储的数据处理主要分为实时处理和异步处理,对于较小的文件(如图片),一般推荐使用实时处理的方式。
对于实时处理,用户可简单通过URL对已有的文件进行实时处理。当用户将文件上传到七牛KODO对象存储平台,会得到相应的key,可通过URL访问。例如 http://xxx.com/key ,当需要对该文件进行实时处理时,可以通过 http://xxx.com/key?<fop>/<param1_value>/<param2_name>/<param2_value>/…. 进行处理。具体操作参数可以参考七牛官方文档。参数过多过长时可以设置样式,若涉及操作复杂多变可以采用管道技术。
异步请求可以通过Portal、命令行工具、SDK发起请求。一般适合处理较大文件,比如较大的音视频,这种情况下实时响应的效果并不理想,则 可以采用异步持久化处理方式,返回的结果是处理后的文件在七牛云存储中的相关元信息(bucket、key等),用户可以通过设置回调服务器地址,将结果 POST到自己的接收服务器,也可以主动查询数据处理状态和结果信息。
上图描述了简单的实时处理的基本架构,用户可以通过七牛云存储的I/O入口发起请求,判断出是合法的数据处理请求后,就会将其传给数据处理调度 服务,通过调度分发给计算处理集群。每个worker处理的流程为参数检查->下载数据->结合原数据信息对参数进行检查(若数据的相关信息 不需要download下来就可以获取,那么可以和前面的步骤对调)->自己编写算法实现或者调用工具对数据进行处理,比如转码、图像缩略等操作 ->结果返回(异步请求一般会持久化保存,通过对象存储服务,将结果上传,得到文件上传后的相关信息,例如bucket、key等,返回的是文件的 相关信息)。这里可以简单的把数据处理调度看做负载均衡器,请求根据接口判断,通过调度指向对应服务,然后将结果原路返回给用户。
上面的结构简单清晰,但同时面临几个问题:
- 数据处理调度这个服务相对比较重,它不仅仅需要实现负载均衡,还需要对所有处理终端服务进行管理,单台计算型服务器上可能有多个服务worker(多个图片处理、音视频处理服务worker),随着业务发展,管理的worker数量是非常多的。
- 内部实现缓存机制,使得读写缓存的流量全部集中在数据处理调度这个服务。
- 数据的处理离不开原数据,一般我们可以在worker中待前面的步骤顺利通过后,调用七牛的对象存储服务开始下载,那么每个 worker都必须配置对象存储服务的地址信息,然后才能download原数据,这套地址信息对所有worker都是共用的。前面提到1台物理机器上可 能有多个服务worker,每个worker自身有不同的属性参数(最起码端口号不同),而且可能机器上的服务worker有Image Worker也有Video Worker,共用一套配置显然不能满足,若每个worker都将这些普遍共用的信息写入配置,那么维护起来非常不方便。因此,七牛的数据处理架构有了一 些演变,就有了如下的架构。
首先,将Data Cache独立出来,理由非常简单,在很多环节都需要缓存服务,并且根据缓存数据大小、热度选择是SSD或者HDD进行缓存,小文件且热度高适合SSD,大文件且热度较低适合HDD。
其次,为每台服务器添加了agent服务。一台服务器可能有多个worker且可能是不同种的worker,数据处理调度服务只需要知道该请求 的对应worker存在于哪些机器即可,剩下的判断则交由agent处理。因为整个计算集群的服务器存在性能差异,采用权重轮询调度,这时某个 worker对应所在的机器一目了然,也不需要对worker整体标记序号,只需要知道某台服务器有哪些worker。agent可以承担 download原数据的责任,相当于提取各个worker的一些公共操作都可以都交给Agent,同时,agent分担了向Data Cache写入数据的任务。
值得一提的是,对于返回失败的数据也可以缓存。假如请求量巨大,每天100亿条请求,确认是客户端请求信息不当的错误约占5%,那么就有5亿错 误请求,即使服务迭代升级,也会保持原有接口功能不变。那么,若是同一个文件的同一个错误请求,基本上必然重现,这样的请求实际上就可以被缓存,一来用户 那边获得快速响应,二来减少计算压力而且减少拖取数据的流量。后来发现这个方案存在一个瑕疵,就是给Data Cache造成的压力略微变大,且有部分错误请求响应并没有加快,至少为了获得缓存数据而读盘会有时间消耗。先前worker的设计是检查参数是否合法 ->download数据->结合原数据信息检查参数是否合法。这里我们对错误请求做了细化,单独屏蔽了download数据之前所产生的错 误请求,因为这部分响应非常快,本身也没有多少计算,无需写入缓存。
最后,通过Discover服务来监控服务情况的变化,所有的agent和worker都需要向Discover上报心跳状态,Load Balancer会从Discover读取各个服务状态、服务相关信息(地址、权重等),同时允许人工通过Discover修改各个服务的状态。
异步请求的架构,则是在整个实时处理架构前面加上异步队列服务、异步请求状态服务等,每个worker的处理结果需要持久化,返回的是结果文件持久化保存后的相关信息。
现在的整个数据处理架构,看上去中规中矩,同时也存在一些弊端,即使做到了可以对单条请求大致消耗资源的预估,经过多次数据回滚压力测试以及峰 值比对确定一台服务器应该部署多少个worker,实现较为合理的调度,但机器上的worker数量基本上是固定的,无法跨机器实现弹性的实时调度,服务 器的资源仍然不能充分利用。例如,当实时处理服务的机器在非峰值阶段,可以将异步请求的服务worker迁移到该机器上,分担一部分任务,使得每台机器的 负载较为均衡;当实时请求到达峰值的时候,可以迁移部分worker到处理公有队列异步请求的机器(付费的私有队列用户不受影响),分担部分压力。
因此,七牛后续将会对整个数据处理的架构做一些关于Container的尝试,希望打破原有的一些束缚,带来比较好的效果,可以把agent和 worker放在一个Container内部,成为1:1的关系。Container自身具有隔离性,可以依据系统的资源情况选择这台机器有多少 Container运行。当某一台服务器资源已经接近饱和时,就会在下一台服务器上启动一个新的Container继续接收请求。一旦某台服务器空闲下 来,那么这台服务器就属于Container待运行的机器,整个计算服务器集群就是个资源池,worker无需被机器束缚,哪里空闲就启动在哪里,再也不 用担心机器资源浪费了。