图床上传系统设计分析


女主宣言

在本篇文章中,女主就带大家去分析一下图床系统的上传模块的设计与实现,让大家能够更清晰的了解,图床系统是如何支撑各业务每天数以千万级的图片上传的。

PS:丰富的一线技术、多元化的表现形式,尽在“HULK一线技术杂谈”,点关注哦!


图床上传系统每天处理各个业务上传的数以千万的图片,是目前360线上正式使用的图床系统,提供HTTP接口和SDK,支持JPEG、GIF、PNG、WEBP、ICO、BMP等主流图片格式。今天我们就来根据图床系统的上传模块的具体执行流程,来分析一下系统是如何设计部署的。

图床上传系统的难点

图床上传目前有几百亿的图片,这么多的图片应该如何存储才能保证数据安全和快速访问?

每天有数千万的上传,峰值的时候每分钟就会有几万的图片需要上传,系统要如何快速的处理这些任务?

问题解决

数据的安全存储和快速访问,这里使用了cassandra分布式数据库。Cassandra 是一个来自 Apache 的分布式数据库,具有高度可扩展性,可用于管理大量的结构化数据。它提供了高可用性,没有单点故障。关于cassandra更多的介绍大家可以到cassandra.apache.org 进行了解。


下面,我们来看一下系统的设计架构:


图床上传系统设计分析_第1张图片


首先系统将任务平均分配到VIP的其中一台RS上,然后将任务保存在本地Redis队列中,随后被gearman抓取到此任务,然后将任务分配给其它worker进程继续处理直到任务处理完毕。

以下内容如果未特别说明,redis为RS对应的本地redis。

下面,以HTTP上传接口为例,来分析具体执行流程。

PHP伪代码:(上传一个二进制图片数据和一个url地址,并判断是否是二维码)


1

用户调用接口

用户上传数据到接口,系统保存任务到redis队列中,如下图。无论是传递了单张还是多张图片,都会为此次请求任务分配一个唯一的groupid,循环分析每一个IMGSTREAM,如果是url不做处理,如果是图片二进制内容,就将内容保存到redis中,保存的key针对每一个图片都是唯一不变的。

图床上传系统设计分析_第2张图片

在分析下一步之前,先了解一下下面相关词语。

  • 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分发任务

图床上传系统设计分析_第3张图片

在这一步中,RS会有一个req_distributer进程,用来分发ReqQueue和PreReqRueue队列任务,分发到GroupDistributor进程。只有当ReqQueue队列为空时,才会处理不需要及时处理的PreReqQueue队列,防止ReqQueue队列产生积压。

现在,我们看到了任务的两个状态阶段,req_distributor(开始任务分发),GroupDistributor(开始处理任务),除了这两个阶段,任务还有以下四个阶段TaskFetch 、TaskProc、GroupComplete GroupCallback,在第一步中,已经解释过了。当然,一个任务不一定六个状态都会经历,其中req_distributor、GroupDistributor、TaskProc、GroupComplete是必经阶段。

让我们熟悉一下,后五个阶段是如何转变的,如下图:

图床上传系统设计分析_第4张图片

结合第一步的状态说明还是很好理解的。

3

TaskFetch根据url获取图片内容

如果上传的为二进制数据,则无此步,直接进入TaskProc阶段。下面来看一下它的具体执行流程。

图床上传系统设计分析_第5张图片

先根据url地址获取图片,如果获取成功,就保存到RS的redis队列里面,key则是根据图片内容计算出来的唯一值(和第一步保存内容是相同的),然后分发到TaskProc。如果图片获取失败,那么会分发到GroupComplete。至此TaskFetch就结束了。

4

TaskProc处理图片规则

这一步用来处理上面的代码里RULES内容的,用来对图片做进一步的处理,像水印、裁剪、缩放、人脸识别、二维码识别等等,都是在这一步处理的,下面让我们用图片方式分析它的执行过程。如下图:

图床上传系统设计分析_第6张图片

需要注意的是如果一个图片指定了多个规则,会依次按顺序进行处理,所以在指定规则时,要想一想指定的规则和顺序是否合理,否则最终的图片可能不是你想要的结果就不同了。

5

GroupComplete 组任务处理

由于一个组任务可能有多个图片任务,即多个taskid任务,在每一个taskid任务变成GroupComplete后,就需要判断组任务是否已经全部完成,如果未全部完成,标记未完成任务减一,如果组任务全部完成,标记组任务完成。

6

GroupCallback 回调用户url(非必须)

如果用户在调用接口时,传递了回调url,则在任务执行GroupComplete时标记组任务全部完成后,会分发到GroupCallback处理。通知调用者任务已经完成,以及任务的详细信息,类似于支付API里的notify_url功能。

7

补充说明

关于六个任务状态分别分配的进程数:

  1. req_distributor是纯IO的操作,所以分配了1个进程

  2. GroupDistributor 也是起到了任务分发操作,目前分配8个进程 

  3. TaskFetch 比较消耗时间,建议多一些,目前分配400个进程 

  4. TaskProc 也消耗一定时间,建议多一些,目前分配300个进程 

  5. GroupComplete 目前分配50个进程 

  6. GroupCallback 比较消耗时间,建议多一些,目前分配300个进程

总结

图床上传系统使用了开源软件cassandra、gearman、redis、nginx、lvs、php等,以上软件在互联网里有着广泛的用户,也证明了这些软件的稳定性是非常好的,也是保证图床上传系统能够保持持续稳定可扩展的原因之一。希望以上的知识对大家在平时学习工作中带来帮助。

扫描下方 二维码 了解更多内容

图床上传系统设计分析_第7张图片

你可能感兴趣的:(图床上传系统设计分析)