1.文件上传之痛
在没有这个基础能力之前,客户端要实现文件上传有这么几种方式:
-
业务服务端通过对接OSS SDK搭建文件上传的能力,通过高德网关(或SNS网关)暴露对端的接口实现文件上传
-
客户端直接本地写死AK、SK对接OSS的SDK实现文件上传
这两种方式都有很明显的弊端。
-
网关在设计上本身不适合做大文件的上传,影响网关的吞吐量,容易给网关带来很大的稳定性风险。另外对于业务服务内部也是不友好的,大文件长时间阻塞线程,造成系统吞吐量下降,如果是C端大流量应用的话,这个影响更加明显,高耗时和低耗时接口应该做隔离。
-
容易造成AK泄漏,有很大的安全性风险。OSS资源一般都是后端维护的资源,把密钥暴露给前端,也是不太合理的做法
-
同时也无法做大文件上传,断点续传这样的能力
高德作为一个日活过亿的国民级APP,有很多场景都有文件上传的诉求,每天都在经历着类似如上的各种问题,有没有一种更高效的方式实现文件上传,为业务方提效?
2.方案调研
经过调研发现,OSS提供了一种临时授权的方案,可以让客户端在保证安全的情况下和OSS做直连,这样就不需要服务端提供上传接口同时客户端很容易使用OSS提供的大文件上传、断点续传等能力。方案如下图所示:
您可以通过STS服务给其他用户颁发一个临时访问凭证。该用户可使用临时访问凭证在规定时间内访问您的OSS资源。临时访问凭证无需透露您的长期密钥,使您的OSS资源访问更加安全。
这个方案的优点也很明显:
-
服务端持有AK,加密存储防止泄漏
-
服务端下发临时Token,安全性更高
-
客户端直连OSS,避免服务端性能瓶颈,可以很方便做大文件上传,断点续传等能力
这个方案里最大的不确定性就是在诺曼底走通STS认证的完整流程。刚入职不太清楚内部申请阿里云资源都是通过诺曼底,就直接在阿里云上申请,一路走下来很顺利。后来发现需要在诺曼底申请资源,才发现这里面的坑相当多,各种账号权限的不通,花了整整一周才搞定整个流程。
根据踩过的坑,整理了一个OSS对接STS认证模式接入的文档。有需要的可以看下(看过的都说靠谱^^)
3.设计目标
满足各多场景的用户资源(视频、图片等)上传诉求,为客户端以及业务服务提供统一的平台化文件上传服务。架构设计上联合客户端,对上层业务屏蔽资源上传的技术细节,提供统一的上传方式,同时平台能力上提供断点续传、回调通知等系统能力,以及 提供业务维度的准入管控、资源管控等资源维护能力。
4.方案设计
4.1.方案概述
我们需要搭建一个文件上传的服务,处理STS授权相关的事情,比如临时token的生成、下发以及缓存更新的事情。还需要搭建一个文件上传的后台,进行业务方注册申请,oss相关的配置。后期提供基于bizId的统计分析功能。
对端SDK能力的支持,提供多种上传下载的能力包括
-
简单上传
-
断点续传上传及下载
-
分片上传
4.2.上下游整体链路
主要分为三大部分:
-
客户端基础SDK。主要做凭证获取、Token过期自动刷新、断连重试、文件上传下载等能力
-
文件上传服务。承载凭证下发的核心能力,通过读取业务配置,从OSS获取临时Token,过期自动刷新,保证Token的可用性
-
服务管控平台。主要做管控相关的能力,业务方的接入、配置上下线。对接MTEE,风险挖掘识别。
4.3.文件上传交互流程
一个文件上传的完整流程。这个流程是基础SDK和服务端的交互流程,对于业务客户端来说是无感的。
4.4.AK托管如何保证安全性?
在这套方案里,需要业务方申请OSS资源,提供AK、SK给到文件服务进行配置,分配对应的bizId。所以需要保证AK的安全性。集团有提供无AK化托管的方案。但是经过调研发现没法满足当前的场景。集团无AK化方案只能跟应用绑定,但是在文件服务里,资源都是业务方申请的,绑定的应用各不相同,文件服务也不好提供统一的应用供业务方绑定。所以这条路走不通。最终采用最常规的方案,利用KeyCenter实现AK的托管。
业务方申请配置中保存ak的密文,在获取token时调用KeyCenter接口解密获取明文,再调用OSS的认证接口获取临时token。实际测试发现KeyCenter解密的耗时在200ms左右,对接口的吞吐量影响很大。因此做了一些优化:在项目启动时,通过对密文解密,生成明文AK,加载到内存中,后续使用ak时直接从内存中获取。大大降低了接口RT。
4.5.临时Token缓存方案设计
获取临时Token,OSS限制了一个账号每秒最多只能请求100次。如果客户端频繁请求临时令牌,会导致oss封禁接口。
解决方案,服务端缓存token。缓存过期时间可配置,要比真实的过期时间早过期。这样可以保证客户端拿到的token的最小可用时间(比如10分钟)。同时为了避免缓存失效时所有请求都打到OSS的情况,加入了分布式锁的控制,同一时刻只能有一个请求生成临时token,其他请求排队等待。
4.6. 性能方面的一些优化
其实这些优化点在上面都已经提到了。主要有三点
-
为了解决AK密文解密耗时的问题,采用项目启动时并发解密获取明文AK、SK,常驻内存。
-
为了减少缓存Token过期重新获取临时的Token带来的毛刺波动,采用定时任务异步刷新Token,这样能保证所有请求都能从内存直接获取临时Token,能保证很好的接口响应表现
-
获取临时Token时的分布式锁控制,避免触发OSS的限流规则。
通过以上的动作,客户端SDK获取Token就变成了纯内存的查询操作,RT是非常低的。
4.7 其他概念介绍
在设计上为每个租户生成一个bizId,bizId对应子账号、角色、权限、endpoint、bucket这些概念,他们之间的关系如下图所示
每个账号生成临时token都可以设置单独的权限policy(比如控制生成的Token只有可读、可写,某一目录的权限),在进行业务配置时可以指定。配置示例如:
"policy":{
"Version":"1",
"Statement":[
{
"Effect":"Allow",
"Action":"oss:*",
"Resource":[
"acs:oss:*:*:amap-architecture",
"acs:oss:*:*:amap-architecture/*"
]
}
]
},
CDN加速
对于大流量的场景,需要配置CDN加速访问,具体可参考
https://yuque.antfin.com/eio17z/iq5svr/ar43cd 《如何开启CDN加速》
4.8. 压测数据分享
单机(4C8G)可承载2500QPS,RT在55ms以内,CPU利用率峰值36%
https://yuque.antfin.com/eio17z/iq5svr/xgfuxb 《压测报告》
5. 说了这么多,如何使用呢
详细的使用文档见:https://yuque.antfin.com/docs/share/3e28b8c5-5ce5-4025-834f-519cb849670f?# 《OSS直传-业务接入流程》
总结一下接入步骤:
-
在诺曼底完成账号申请,资源申请
-
提供资源信息由平台分配bizId(目前通过发邮件申请,后期会提供后台能力自行申请创建)
-
拿到bizId,发起调用。
-
客户端SDK接入的流程
客户端调用代码示例:
重要提示:由于文件在OSS服务端是通过 ossSaveDir + 本地文件名的方式存储的,相同的文件名会覆盖,因此接入前辛苦业务方确认下,是否需要保证文件的唯一性。可以在 ossSaveDir 中加入类似UID,或者时间戳来保证文件的唯一性。
上传方案一:AJX切面接入
/// 上传
const uploadObj = {bizId: (申请的bizId,例如: 111000_fileservice-read_aos_c1), fileLocalPath: (本地文件绝对路径,例如: file:///sdcard/xxx/yyy/demo.txt), ossSaveDir: (远端存储路径,以"/"结尾,例如:a/b/)};
requestId = natives.oss.uploadFile(uploadObj, (error, result) => {
if (result) {
const res = JSON.parse(result);
ajx.app.toast(`result:${res.msg}`)
}
}, (progress) => {
const prog = JSON.parse(progress);
if (prog) {
ajx.app.toast(`${prog.progress}`)
}
});
/// 取消上传
natives.oss.cancel(requestId);
上传方案二:Native接入
/// 1、创建upload对象
GDOSSUploadRequest *uploadRequest = [[GDOSSUploadRequest alloc] init];
/// 2、申请的bizId,例如:111000_fileservice_aos_c1
uploadRequest.bizId = @"111000_fileservice_aos_c1";
/// 3、本地文件绝对路径
uploadRequest.fileLocalPath = [[NSBundle mainBundle] pathForResource:@"LargeFile" ofType:@"zip"];
/// 4、远端存储路径,以"/"结尾,例如:a/b/
uploadRequest.ossSaveDir = @"a/b/";
/// 5、上传
NSString *requestId = [AMapNetworkService uploadWithRequest:uploadRequest progress:^(NSInteger progress, int64_t current, int64_t total) {
} completion:^(GDOSSUploadResponse * _Nonnull response) {
}];
/// 取消上传
[AMapNetworkService cancelOSSRequestWithId:requestId];
注意:如果上传的文件名有特殊字符(空格,加号等),返回的 objectName 也会带有特殊字符。业务方自己拼接 URL 做下载时,需对 URL 进行 urlEncode 处理,否则 URL 无效。
下载方案一:AJX切面接入
/// 下载
const downloadObj = {objectName: `下载文件名`, localPath: `下载文件的绝对路径.文件后缀名,例如:file://documents/oss/media/tmp/a.file`, bizId: `申请的bizId,例如:111000_fileservice_aos_c1`};
downloadTaskId = natives.oss.downloadFile(downloadObj, (error, result) => {
if (error) {
ajx.log.print(error);
} else {
if (result) {
const res = JSON.stringify(result);
ajx.log.print(`${downloadTaskId} result:${res}`);
} else {
ajx.log.print(`download result is empty`)
}
}
}, (progress) => {
if (progress) {
ajx.log.print(JSON.stringify(progress))
}
});
/// 取消下载
natives.oss.cancel(downloadTaskId);
下载方案二:img标签接入
/// img标签加载图片
<img src="oss://111000_fileservice_aos_c1/test/33333.jpg" style={{ 'width': "500px", 'height': "500px", 'border-radius': '70px' }} />
下载方案三:Native接入
/// 1、创建download对象
GDOSSDownloadRequest *request = [[GDOSSDownloadRequest alloc] init];
/// 2、申请的bizId,例如:111000_fileservice_aos_c1
request.bizId = @"111000_fileservice-read_aos_c1";
/// 3、下载的文件名
request.objectName = [NSString stringWithFormat:@"test/bigfile.zip"];
/// 4、下载的本地绝对路径
request.destinationPath = @"AbsolutePath/bigfile_0.zip";
/// 5、下载
NSString *downloadRequestId = [AMapNetworkService downloadWithRequest:request progress:^(NSInteger progress, int64_t current, int64_t total) {
} completion:^(GDOSSDownloadResponse * _Nonnull response) {
}];
/// 取消下载
[AMapNetworkService cancelOSSRequestWithId:downloadRequestId];
就先介绍到这里。
整理了一些常见的问题,需要时自取
https://yuque.antfin.com/docs/share/b047d959-7adc-46bb-a47d-c5514fde4050?# 《OSS直传能力接入常见问题》
6.接入情况
目前已有足迹、停车点记录、用反、评论,趣游等九个业务方接入,每日业务传文件量5w+。欢迎更多的业务场景接入,为大家提效。
7.后期规划
-
自动化接入平台建设。目前申请流程比较繁琐,需要很多道申请审批流程,配置也较多,很容易出错。终极目标是提供自动化接入的能力,把诺曼底上的申请流程实现自动化对接,让业务方的申请过程变得丝滑
-
web端文件上传能力建设。目前的上传支持AMAP里的上传诉求,对于WEB端的上传还不支持,后期需要补齐这一块能力
-
统计分析&风控能力。提供基于bizId 维度的风控配置、统计分析等能力。
开发团队:
移动架构部:郝仁杰、呼唤、陈腾蛟、任涛
技术服务平台自主出行服务端:魏国兵、闻钟
有相关业务场景接入,欢迎加钉钉群沟通: 33253097