女主宣言
在本篇文章中,女主就带大家去分析一下图床系统的上传模块的设计与实现,让大家能够更清晰的了解,图床系统是如何支撑各业务每天数以千万级的图片上传的。
PS:丰富的一线技术、多元化的表现形式,尽在“HULK一线技术杂谈”,点关注哦!
图床上传系统每天处理各个业务上传的数以千万的图片,是目前360线上正式使用的图床系统,提供HTTP接口和SDK,支持JPEG、GIF、PNG、WEBP、ICO、BMP等主流图片格式。今天我们就来根据图床系统的上传模块的具体执行流程,来分析一下系统是如何设计部署的。
图床上传系统的难点
图床上传目前有几百亿的图片,这么多的图片应该如何存储才能保证数据安全和快速访问?
每天有数千万的上传,峰值的时候每分钟就会有几万的图片需要上传,系统要如何快速的处理这些任务?
问题解决
数据的安全存储和快速访问,这里使用了cassandra分布式数据库。Cassandra 是一个来自 Apache 的分布式数据库,具有高度可扩展性,可用于管理大量的结构化数据。它提供了高可用性,没有单点故障。关于cassandra更多的介绍大家可以到cassandra.apache.org 进行了解。
下面,我们来看一下系统的设计架构:
首先系统将任务平均分配到VIP的其中一台RS上,然后将任务保存在本地Redis队列中,随后被gearman抓取到此任务,然后将任务分配给其它worker进程继续处理直到任务处理完毕。
以下内容如果未特别说明,redis为RS对应的本地redis。
下面,以HTTP上传接口为例,来分析具体执行流程。
PHP伪代码:(上传一个二进制图片数据和一个url地址,并判断是否是二维码)
1
用户调用接口
用户上传数据到接口,系统保存任务到redis队列中,如下图。无论是传递了单张还是多张图片,都会为此次请求任务分配一个唯一的groupid,循环分析每一个IMGSTREAM,如果是url不做处理,如果是图片二进制内容,就将内容保存到redis中,保存的key针对每一个图片都是唯一不变的。
在分析下一步之前,先了解一下下面相关词语。
ReqQueue
正在处理(需要处理)的队列类
PreReqQueue
预处理的队列类,只有ReqQueue的队列长度为0,才会将PreReqQueue加入ReqQueue队列中
Imgkey
图片加密后的名称,不包含后缀,redis和cassandra中的数据都是根据此key存入的,针对每个图片都是唯一的
Groupid
组任务。每次http接口调用或SDK调用,都会分配一个唯一的groupid
Taskid
每一个图片及其处理规则都会包含在一个taskid中
Groupid和taskid的关系
一个groupid可以包含一个taskid
一个groupid可以包含多个taskid
gm_client 任务的发起者用于将任务分配给worker
Worker
处理任务的进程,系统中共有以下几种worker
req_distributor
将redis队列中的数据通过一定算法分配给GroupDistributor
GroupDistributor
将任务根据任务数据明细,分配给TaskFetch或TaskProc
TaskFetch
获取url的图片内容并保存在本地redis
TaskProc
处理rule规则,存储图片到cassandra
GroupComplete
任务结束时的处理程序
GroupCallback
如果注册了回调url,则触发这个worker进行处理
2
gearman client分发任务
在这一步中,RS会有一个req_distributer进程,用来分发ReqQueue和PreReqRueue队列任务,分发到GroupDistributor进程。只有当ReqQueue队列为空时,才会处理不需要及时处理的PreReqQueue队列,防止ReqQueue队列产生积压。
现在,我们看到了任务的两个状态阶段,req_distributor(开始任务分发),GroupDistributor(开始处理任务),除了这两个阶段,任务还有以下四个阶段TaskFetch 、TaskProc、GroupComplete GroupCallback,在第一步中,已经解释过了。当然,一个任务不一定六个状态都会经历,其中req_distributor、GroupDistributor、TaskProc、GroupComplete是必经阶段。
让我们熟悉一下,后五个阶段是如何转变的,如下图:
结合第一步的状态说明还是很好理解的。
3
TaskFetch根据url获取图片内容
如果上传的为二进制数据,则无此步,直接进入TaskProc阶段。下面来看一下它的具体执行流程。
先根据url地址获取图片,如果获取成功,就保存到RS的redis队列里面,key则是根据图片内容计算出来的唯一值(和第一步保存内容是相同的),然后分发到TaskProc。如果图片获取失败,那么会分发到GroupComplete。至此TaskFetch就结束了。
4
TaskProc处理图片规则
这一步用来处理上面的代码里RULES内容的,用来对图片做进一步的处理,像水印、裁剪、缩放、人脸识别、二维码识别等等,都是在这一步处理的,下面让我们用图片方式分析它的执行过程。如下图:
需要注意的是如果一个图片指定了多个规则,会依次按顺序进行处理,所以在指定规则时,要想一想指定的规则和顺序是否合理,否则最终的图片可能不是你想要的结果就不同了。
5
GroupComplete 组任务处理
由于一个组任务可能有多个图片任务,即多个taskid任务,在每一个taskid任务变成GroupComplete后,就需要判断组任务是否已经全部完成,如果未全部完成,标记未完成任务减一,如果组任务全部完成,标记组任务完成。
6
GroupCallback 回调用户url(非必须)
如果用户在调用接口时,传递了回调url,则在任务执行GroupComplete时标记组任务全部完成后,会分发到GroupCallback处理。通知调用者任务已经完成,以及任务的详细信息,类似于支付API里的notify_url功能。
7
补充说明
关于六个任务状态分别分配的进程数:
req_distributor是纯IO的操作,所以分配了1个进程
GroupDistributor 也是起到了任务分发操作,目前分配8个进程
TaskFetch 比较消耗时间,建议多一些,目前分配400个进程
TaskProc 也消耗一定时间,建议多一些,目前分配300个进程
GroupComplete 目前分配50个进程
GroupCallback 比较消耗时间,建议多一些,目前分配300个进程
总结
图床上传系统使用了开源软件cassandra、gearman、redis、nginx、lvs、php等,以上软件在互联网里有着广泛的用户,也证明了这些软件的稳定性是非常好的,也是保证图床上传系统能够保持持续稳定可扩展的原因之一。希望以上的知识对大家在平时学习工作中带来帮助。
扫描下方 二维码 了解更多内容