七牛数据处理架构变迁 

banner

据统计,互联网数据量正以每三年翻一番的速度膨胀,其中,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到自己的接收服务器,也可以主动查询数据处理状态和结果信息。

1

上图描述了简单的实时处理的基本架构,用户可以通过七牛云存储的I/O入口发起请求,判断出是合法的数据处理请求后,就会将其传给数据处理调度服务,通过调度分发给计算处理集群。每个worker处理的流程为参数检查->下载数据->结合原数据信息对参数进行检查(若数据的相关信息不需要download下来就可以获取,那么可以和前面的步骤对调)->自己编写算法实现或者调用工具对数据进行处理,比如转码、图像缩略等操作->结果返回(异步请求一般会持久化保存,通过对象存储服务,将结果上传,得到文件上传后的相关信息,例如bucket、key等,返回的是文件的相关信息)。这里可以简单的把数据处理调度看做负载均衡器,请求根据接口判断,通过调度指向对应服务,然后将结果原路返回给用户。

上面的结构简单清晰,但同时面临几个问题:

  • 数据处理调度这个服务相对比较重,它不仅仅需要实现负载均衡,还需要对所有处理终端服务进行管理,单台计算型服务器上可能有多个服务worker(多个图片处理、音视频处理服务worker),随着业务发展,管理的worker数量是非常多的。
  • 内部实现缓存机制,使得读写缓存的流量全部集中在数据处理调度这个服务。
  • 数据的处理离不开原数据,一般我们可以在worker中待前面的步骤顺利通过后,调用七牛的对象存储服务开始下载,那么每个worker都必须配置对象存储服务的地址信息,然后才能download原数据,这套地址信息对所有worker都是共用的。前面提到1台物理机器上可能有多个服务worker,每个worker自身有不同的属性参数(最起码端口号不同),而且可能机器上的服务worker有Image Worker也有Video Worker,共用一套配置显然不能满足,若每个worker都将这些普遍共用的信息写入配置,那么维护起来非常不方便。因此,七牛的数据处理架构有了一些演变,就有了如下的架构。

2

首先,将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到处理公有队列异步请求的机器(付费的私有队列用户不受影响),分担部分压力。

3

因此,七牛后续将会对整个数据处理的架构做一些关于Container的尝试,希望打破原有的一些束缚,带来比较好的效果,可以把agent和worker放在一个Container内部,成为1:1的关系。Container自身具有隔离性,可以依据系统的资源情况选择这台机器有多少Container运行。当某一台服务器资源已经接近饱和时,就会在下一台服务器上启动一个新的Container继续接收请求。一旦某台服务器空闲下来,那么这台服务器就属于Container待运行的机器,整个计算服务器集群就是个资源池,worker无需被机器束缚,哪里空闲就启动在哪里,再也不用担心机器资源浪费了。