1.背景

面向海外用户设计图片类app的后端架构。

2. 目标

  1. 考虑跨地区访问图片列表。

  2. 图片容灾和备份服务。

  3. 用户访问突增的解决方案。

  4. 海外服务政策相关注意事项。

3. 方案(图片)

ps:这里先统一考虑图片的设计过程,图片解决后,再考虑业务后台过程

3.1 自研

方案设计

v1.drawio

  1. 分布式文件系统:采用开源系统进行搭建分布式文件系统
  2. 文件系统服务:可以新增图片时可以生成索引返回给业务,当业务只需要根据索引,就能查询到对应的文件内容
  3. 业务层:上传服务主要负责图片的上传、而列表服务则是需要根据请求,获取列表及数据
  4. 接入层:接收用户的请求,把请求代理到业务上

这么设计,可以实现一个图片类的应用。在实际中会有什么问题?

  1. 不同区域的用户,体验不一样,用户离部署的节点越近,用户体验更好

因为根据之前的经验,地域对于网络的延迟影响很大。大致从ping上就能体现

地区1地区2ping时间
上海广州30ms
上海上海10ms
上海美国100ms
  1. 从终端的成功率上看,由于网络上的丢包、延迟,成功率会低很多,特别是图片(目前图片1~3M都是比较正常的),这么大的图片,在过程中,发生丢包、延迟,失败率可想而知,会特别的高。

这种场景,我们可以考虑下,访问国外某些网站的时候,经常是失败,体验非常差

改进点

那么需要怎么改进呢?

比较容易想到的就是,既然是距离远,那么直接在对应的地方部署一个服务,不就行了么?

v2.1.drawio

这样各地的用户,通过dns的调度,访问对应的接入层,接入层只访问当前区域的服务(同一个区域),这样就减少了网络上的问题。解决了用户体验。但是,好像跟需求不是太耦合。。。需求是跨区域访问

那么要怎么样实现跨区域访问呢?

v2.3.drawio

从图上可以看出来,如果底层数据实现了数据同步,那么是不是就可以了?

比如亚洲用户发布内容,那么我们把数据同步给其他集群,这样其他集群就可以访问到亚洲用户的信息了

要怎么实现同步呢?目前了解到**FastDFS**可以实现分布式任务系统的,他是采用binlog进行同步,在log中有个标志位用户记录该条记录是C: 增加 D: 删除 A: 添加 M: 修改 U: 更新整个文件 T: 截断文件 等,当亚洲区域进行添加时,会发送日志给美洲、欧洲,他们也会根据binlog的日志添加,这里需要注意:同步数据采用的标识与写入的是不一样的,采用小写,目的是为了区别是否需要同步给其他集群。

这里还没有对FastDFS跨区同步进行测试过,还不确定具体的延迟能够到达多少(有待验证)。

理论上,上面的方案是可以实现的,那么我们会有什么问题呢?

  1. 所有图片数据,都存在多份,每个数据都需要进行公网的同步。
  2. 文件传入与数据传输需要保证一致,不能有数据了,没有文件

那么我们有没有其他方案进行呢?下面我们来看下

v2.4.drawio

对于图片,可以采用CDN加速。

对于API接口了解到市面上,有一种产品,叫做“全站加速”或者“动态加速”,也就是cdn不进行缓存,直接访问,这样的话,我们可以直接让用户访问,这样的话,所有数据都访问了中心区域的数据,通过“动态加速”把用户和源进行连接,核心是增加了数据传输的稳定性,降低失败率。

这种方式存在什么问题:

  1. 数据量问题:

    a) 扩容问题:这个也不算特别问题,是项目一般都会遇到

    b) 冷热数据:如果统一的采用一套文件系统,那么会导致数据积累越来越多,文件系统会不停的扩大

  2. 当然还有其他的一些需要考虑的点,比如高请求量下“文件内容缓存”、容灾备份等还没有详细讲

3.2 外部功能

既然自研中,考虑了外部功能,那么市面上是否有外部的功能,可以实现全球图片访问问题呢?

答案是肯定的:CDN+文件系统,这个方案目前也是比较成熟。

V3.drawio

该方案乍看一下,与自研的最早方案类似(主要关注左侧部分),都是全球直接接入“一层”,但实际上是不一样的:

  1. CDN会覆盖全球节点,当用户接入时,能够就近访问,通过CDN的内网,访问“文件系统”
  2. 文件系统可以自研,也可以采用云产品。

3.2.1 CDN

用一个图来说明下

V4-cdn.drawio

访问顺序:A -> B -> C -> D -> E

A. 边缘节点A(无数据,回源)-> 中心节点A (无数据,回源)-> 源站 -> 中心节点A(缓存)-> 边缘节点A(缓存)

B. 边缘节点A(有缓存,直接返回)

C. 边缘节点B(无数据,回源)-> 中心节点A(有缓存,直接返回)-> 边缘节点B(缓存)

D. 边缘节点C(无数据,回源)-> 中心节点B (无数据,回源)-> 源站 -> 中心节点B(缓存)-> 边缘节点C(缓存)

E. 边缘节点D(无数据,回源)-> 中心节点B(有缓存,直接返回)-> 边缘节点D(缓存)

所以第一次访问时,会比较慢,但是面向用户群体属于该区域,则大部分的体验,还是挺快的

3.2.2 源站

目前各大云厂商,都提供了存储,并且针对与存储,并且容量没有限制

3.3 对比

那么,我们对两种方案价格进行对比下(初步对比,数据可能不准确):

价格技术
自研+数据同步(机器500G(700元/月)+宽带10M(650元/月))* 2 =2700(元/月)1. 同步数据服务2. 文件系统服务3. 冷热数据分离服务
自研+CDN分发机器500G(700元/月)+宽带10M(650元/月)+CDN2T(900元/月)=2150(元/月)1. 文件系统服务2. 冷热数据分离服务
云厂商Bucket500G(200元/月)+回源流量1T(300元/月)+CDN2T(900元/月)=1400(元/月)

3.4 选型

他们的**优势**分别是什么呢?

自研+数据同步自研+CDN分发云厂商
价格
突发流量(发)自主扩容自主扩容自动
突发流量(查)提升带宽、降级自动自动
访问速度依赖于同步数据时间首次依赖于回源速度,后面速度较快首次依赖于回源速度,后面速度较快
是否支持容灾多地备份
容灾备份成本已经实现1. 需要把文件系统全量同步到不同的区域,来实现容灾,整体成本,存储量 * 22. 需要实现文件同步服务代码bucket异地备份,存储成本*2,(2M以下的内容,一般分钟级别的延迟)
冷热数据处理需要开发冷热数据分离(大致方案在最下面问题中)需要开发冷热数据分离已提供设置
负载均衡自主控制自动自动

从上面的对比得出:

  1. 初期项目+公司内没有对应的技术栈

    选择**云厂商**

  2. 公司已经有成熟的技术栈

    选择**自研+CDN分发**

  3. 公司在全球的节点部署比较成熟,并且有相应的稳定网络

    选择**全部自研**

4. 后台

从图片的架构选型过程,对于后台的架构选型可以有两种

4.1 方案

4.1.1 全球加速访问同一个区域

V4.1.drawio

该方案整体上没有特别的点,主要是采用了全球加速来提升访问服务的稳定性,当访问用户离服务区域远,延迟会比较高

4.1.2 多区域提供服务

V4.2.drawio

这里的核心问题,其实就是数据如何同步。

接下来就对这块进行拆解

问1:如果能够控制一个人,都是在同一个区域,那么是不是就会简单很多呢?

答案是肯定的。如果一个人只是在同一个区域操作,那么只需要把这个人的数据直接同步到其他区域即可。

所以就需要在人身上,打上对应的标记,那么怎么标记?(在我的概念中,95%的人在一个相对的时间段内,都是在同一个地方。具体哪里看到,有点忘记了)

答1:我们把用户注册,当作该用户所在的区域

这样做,我们基本上可以让95%的用户能够就近访问。那么还有5%的人怎么办的?这个问题后面再讲。

问2:各个区域,要如何实现同步呢?

答2:可以同步的有多个地方:

存储:直接使用存储自带的,比如MySQL、Redis都有自己的复制思路

业务:用中间件方式,比如kafka这种,多区域采用不同的消费者,来实现数据同步

存储同步,相对技术比较成熟,可以直接使用,可能出现的问题:

  1. A->B->A这种情况
  2. 如果我关系链同步到了,但是元数据还未同步,这个情况怎么办?

业务同步,由于本身是业务触发,所以对于业务上来说,所以可以解决存储同步的问题

  1. 当A发起同步B时,B接受到同步,则不用发起再像A的同步
  2. 可以先同步用户信息变更、再同步元数据变更、最后同步关系链

当然同步也会存在一些共性问题:

  1. 网络问题:这个目前来说,我了解的主要有两种:

    a)打专线,让各个区域的网络联通起来。

    b)利用云厂商的内部网络,在他们的基础上进行处理

    这两个办法,都是增加网络的稳定性,来减少丢包重传这种情况

  2. 数据延迟:比如分钟内的延迟

    延迟本身受区域距离、业务复杂度的影响,当前需求的场景下,我觉得如果能够达到这样的延迟,也是可以接受的。

问3: 是否需要全部区域进行复制?

答3: 这个我觉得可以优化

当一个用户所有的订阅者,所属的区域是同一个区域,那么分发到其他区域,是没有意义的。因为其他区域不会找你的信息(没有关系链找到该用户),除非通过搜索这种,对于这种跨区需求,频率非常少,当本地不存在该信息时,可以远程拉取(可能首次出现了没有内容),但是如果存在了订阅该用户,则该用户就会往该区域同步信息。

问4: 上面提到的,假设用户归属是新加坡,但是去欧洲出差半年,那么该用户每次都会需要从接入层转发到另外区域去获取数据么?(图上接入层的虚线部分)

答4: 答案是否定的。如果每次都要从另外区域去拉取,那么这部分用户基本上体验会很差,为了改善这个问题,用户上是可以挂在**交集属性** 就是说,本来用户属于区域A,但是这个时候用户挂在了交集属性“区域B”,那么,在后续有关注的用户有操作,也会同步到该区域。

问5:如果数据修改顺序是A->B->C,但是同步后,到区域B的数据为A->C->B 那么数据就会造成不一致吧?

答5: 该问题也比较容易解决,可以在数据上增加版本好,同步后,用版本号大的覆盖版本号小的

4.2 选型

做一个简单的对比

方案1方案2
技术简单,只考虑单区域即可方案复杂
容灾方案单区域多AZ多区域多AZ
突发流量扩机器(设计上服务无状态)突发集群扩机器(设计上服务无状态)
体验非服务所在区域,访问会有延迟相对于1,服务会更好

那么该选哪个方案呢?

这个还是跟实际场景有关系

方案1:刚开始推广,能够覆盖大部分的用户。

方案2:产品相对比较成熟,用户在全球范围内,都有比较大的一个占比,团队也有一定的规模。或者在现有一个比较大群体下,新增的一个业务

5 接口设计

5.1 发布

流程图.drawio

接口定义

PublishRsp publish(long uid, PublishReq post);

注: 图片基于CDN的分发,用户上传后,给后台的只是一个URI地址。

5.2 订阅列表

订阅查看.drawio

接口定义

// 获取用户自己的时间线
TimelineRsp get(long uid, TimelineReq req);

// 获取某个用户(target)的时间线(这里会有两种情况,一个是target本人获取,一个是其他用户查看target的时间线,主要权限区别)
TimelineRsp getOneUser(long uid, long target, TimelineReq req);

6. 关于海外政策相关

  1. 预设标签分类,对于每个图片进行分类打标,不同的政策可以根据不同的标签进行过滤

  2. 对于访问的图片(服务端吐出去的图片地址),可以进行异步检测。这种场景适用于:

    a)为了弥补预设标签后,又增加了某些标签,历史数据特别大,如果全量扫,可能需要几个月。

    b)临时性的政策,比如国家突然规定封杀某个信息。

7. 问题

  1. 如果一个用户,被很多人关注,应该分发模式会导致服务压力过大

    如果这种情况,其实不适合写扩散,因为量大容易引起服务异常,所以在这种情况下,就需要推拉结合的模式

  2. 关于对发表后的内容操作(如评论)如何同步

    这块的操作,大体上应该和内容分发类似,但是细节可能不一样,因为这里存在多地写的情况,可以考虑因果关系,全量评论同步。当然同步后,需要做到去重复。(这里相对复杂点)

  3. 如何预防黑产利用漏洞,盗用流量资源

    a) 对上传资源的监控,通过回掉来确认资源是否被使用

    b) 对资源进行加密,CDN侧增加鉴权,鉴权不通过,则直接拒绝

  4. 冷热数据分离

    根据日期进行分离,可以分成1天、7天、30天、半年、一年以上的数据进行统计,根据占比和访问请求,来规划冷热数据分离,冷数据可以采用更低的资源进行存储,减少成本

其他资料

海外服务政策相关注意事项

​ a) 采集信息:IP信息及地区信息(为了让用户更快速的访问)

​ b) 用户内容:昵称等(用户之间的必要通信信息)

​ c) 日志数据:订阅关系、元数据、访问日期、时间戳等(确保安全的提供服务)

各个区域公网的ping延迟

美国西部 1 (硅谷)

image-20220610005713371

美国东部 1 (弗吉尼亚)

image-20220610005808192