1.背景
面向海外用户设计图片类app的后端架构。
2. 目标
考虑跨地区访问图片列表。
图片容灾和备份服务。
用户访问突增的解决方案。
海外服务政策相关注意事项。
3. 方案(图片)
ps:这里先统一考虑图片的设计过程,图片解决后,再考虑业务后台过程
3.1 自研
方案设计

- 分布式文件系统:采用开源系统进行搭建分布式文件系统
- 文件系统服务:可以新增图片时可以生成索引返回给业务,当业务只需要根据索引,就能查询到对应的文件内容
- 业务层:上传服务主要负责图片的上传、而列表服务则是需要根据请求,获取列表及数据
- 接入层:接收用户的请求,把请求代理到业务上
这么设计,可以实现一个图片类的应用。在实际中会有什么问题?
- 不同区域的用户,体验不一样,用户离部署的节点越近,用户体验更好
因为根据之前的经验,地域对于网络的延迟影响很大。大致从ping上就能体现
| 地区1 | 地区2 | ping时间 |
|---|---|---|
| 上海 | 广州 | 30ms |
| 上海 | 上海 | 10ms |
| 上海 | 美国 | 100ms |
- 从终端的成功率上看,由于网络上的丢包、延迟,成功率会低很多,特别是图片(目前图片1~3M都是比较正常的),这么大的图片,在过程中,发生丢包、延迟,失败率可想而知,会特别的高。
这种场景,我们可以考虑下,访问国外某些网站的时候,经常是失败,体验非常差
改进点
那么需要怎么改进呢?
比较容易想到的就是,既然是距离远,那么直接在对应的地方部署一个服务,不就行了么?

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

从图上可以看出来,如果底层数据实现了数据同步,那么是不是就可以了?
比如亚洲用户发布内容,那么我们把数据同步给其他集群,这样其他集群就可以访问到亚洲用户的信息了
要怎么实现同步呢?目前了解到**FastDFS**可以实现分布式任务系统的,他是采用binlog进行同步,在log中有个标志位用户记录该条记录是C: 增加 D: 删除 A: 添加 M: 修改 U: 更新整个文件 T: 截断文件 等,当亚洲区域进行添加时,会发送日志给美洲、欧洲,他们也会根据binlog的日志添加,这里需要注意:同步数据采用的标识与写入的是不一样的,采用小写,目的是为了区别是否需要同步给其他集群。
这里还没有对FastDFS跨区同步进行测试过,还不确定具体的延迟能够到达多少(有待验证)。
理论上,上面的方案是可以实现的,那么我们会有什么问题呢?
- 所有图片数据,都存在多份,每个数据都需要进行公网的同步。
- 文件传入与数据传输需要保证一致,不能有数据了,没有文件
那么我们有没有其他方案进行呢?下面我们来看下

对于图片,可以采用CDN加速。
对于API接口了解到市面上,有一种产品,叫做“全站加速”或者“动态加速”,也就是cdn不进行缓存,直接访问,这样的话,我们可以直接让用户访问,这样的话,所有数据都访问了中心区域的数据,通过“动态加速”把用户和源进行连接,核心是增加了数据传输的稳定性,降低失败率。
这种方式存在什么问题:
数据量问题:
a) 扩容问题:这个也不算特别问题,是项目一般都会遇到
b) 冷热数据:如果统一的采用一套文件系统,那么会导致数据积累越来越多,文件系统会不停的扩大
当然还有其他的一些需要考虑的点,比如高请求量下“文件内容缓存”、容灾备份等还没有详细讲
3.2 外部功能
既然自研中,考虑了外部功能,那么市面上是否有外部的功能,可以实现全球图片访问问题呢?
答案是肯定的:CDN+文件系统,这个方案目前也是比较成熟。

该方案乍看一下,与自研的最早方案类似(主要关注左侧部分),都是全球直接接入“一层”,但实际上是不一样的:
- CDN会覆盖全球节点,当用户接入时,能够就近访问,通过CDN的内网,访问“文件系统”
- 文件系统可以自研,也可以采用云产品。
3.2.1 CDN
用一个图来说明下

访问顺序: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以下的内容,一般分钟级别的延迟) |
| 冷热数据处理 | 需要开发冷热数据分离(大致方案在最下面问题中) | 需要开发冷热数据分离 | 已提供设置 |
| 负载均衡 | 自主控制 | 自动 | 自动 |
从上面的对比得出:
初期项目+公司内没有对应的技术栈
选择**
云厂商**公司已经有成熟的技术栈
选择**
自研+CDN分发**公司在全球的节点部署比较成熟,并且有相应的稳定网络
选择**
全部自研**
4. 后台
从图片的架构选型过程,对于后台的架构选型可以有两种
4.1 方案
4.1.1 全球加速访问同一个区域

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

这里的核心问题,其实就是数据如何同步。
接下来就对这块进行拆解
问1:如果能够控制一个人,都是在同一个区域,那么是不是就会简单很多呢?
答案是肯定的。如果一个人只是在同一个区域操作,那么只需要把这个人的数据直接同步到其他区域即可。
所以就需要在人身上,打上对应的标记,那么怎么标记?(在我的概念中,95%的人在一个相对的时间段内,都是在同一个地方。具体哪里看到,有点忘记了)
答1:我们把用户注册,当作该用户所在的区域
这样做,我们基本上可以让95%的用户能够就近访问。那么还有5%的人怎么办的?这个问题后面再讲。
问2:各个区域,要如何实现同步呢?
答2:可以同步的有多个地方:
存储:直接使用存储自带的,比如MySQL、Redis都有自己的复制思路
业务:用中间件方式,比如kafka这种,多区域采用不同的消费者,来实现数据同步
存储同步,相对技术比较成熟,可以直接使用,可能出现的问题:
- A->B->A这种情况
- 如果我关系链同步到了,但是元数据还未同步,这个情况怎么办?
业务同步,由于本身是业务触发,所以对于业务上来说,所以可以解决存储同步的问题
- 当A发起同步B时,B接受到同步,则不用发起再像A的同步
- 可以先同步用户信息变更、再同步元数据变更、最后同步关系链
当然同步也会存在一些共性问题:
网络问题:这个目前来说,我了解的主要有两种:
a)打专线,让各个区域的网络联通起来。
b)利用云厂商的内部网络,在他们的基础上进行处理
这两个办法,都是增加网络的稳定性,来减少丢包重传这种情况
数据延迟:比如分钟内的延迟
延迟本身受区域距离、业务复杂度的影响,当前需求的场景下,我觉得如果能够达到这样的延迟,也是可以接受的。
问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 发布

接口定义
PublishRsp publish(long uid, PublishReq post);
注: 图片基于CDN的分发,用户上传后,给后台的只是一个URI地址。
5.2 订阅列表

接口定义
// 获取用户自己的时间线
TimelineRsp get(long uid, TimelineReq req);
// 获取某个用户(target)的时间线(这里会有两种情况,一个是target本人获取,一个是其他用户查看target的时间线,主要权限区别)
TimelineRsp getOneUser(long uid, long target, TimelineReq req);
6. 关于海外政策相关
预设标签分类,对于每个图片进行分类打标,不同的政策可以根据不同的标签进行过滤
对于访问的图片(服务端吐出去的图片地址),可以进行异步检测。这种场景适用于:
a)为了弥补预设标签后,又增加了某些标签,历史数据特别大,如果全量扫,可能需要几个月。
b)临时性的政策,比如国家突然规定封杀某个信息。
7. 问题
如果一个用户,被很多人关注,应该分发模式会导致服务压力过大
如果这种情况,其实不适合写扩散,因为量大容易引起服务异常,所以在这种情况下,就需要推拉结合的模式
关于对发表后的内容操作(如评论)如何同步
这块的操作,大体上应该和内容分发类似,但是细节可能不一样,因为这里存在多地写的情况,可以考虑因果关系,全量评论同步。当然同步后,需要做到去重复。(这里相对复杂点)
如何预防黑产利用漏洞,盗用流量资源
a) 对上传资源的监控,通过回掉来确认资源是否被使用
b) 对资源进行加密,CDN侧增加鉴权,鉴权不通过,则直接拒绝
冷热数据分离
根据日期进行分离,可以分成1天、7天、30天、半年、一年以上的数据进行统计,根据占比和访问请求,来规划冷热数据分离,冷数据可以采用更低的资源进行存储,减少成本
其他资料
海外服务政策相关注意事项
a) 采集信息:IP信息及地区信息(为了让用户更快速的访问)
b) 用户内容:昵称等(用户之间的必要通信信息)
c) 日志数据:订阅关系、元数据、访问日期、时间戳等(确保安全的提供服务)
各个区域公网的ping延迟
美国西部 1 (硅谷)

美国东部 1 (弗吉尼亚)
