统一存储接入方案分析

2021-02-01 598 0

背景

在项目的实施过程中,遇到越来越多的第三方私有或公有化存储云服务,有阿里OSS、华为云OBS、七牛云Kodo等,各存储厂商对接标准备和SDK尽不相同,这对项目的实施和运维带来了很大的挑战,所以急需为各项目开发一套统一的存储接入标准,屏蔽各厂商接入差异,增强业务快速实施及产品化的能力。

项目痛点分析

  • 项目中附件上传下载实现五花八门,代码难以维护
  • 存储参数配置凌乱,无法统一维护和配置,运维头大
  • 项目无法快速接入不同第三方云存储,业务要大改一通且维护不同的代码分支
  • 上传下载功能(前后端)常被业务重复开发,浪费资源
  • 通过网关、nginx等简单包装代理方式处理第三方存储,丢失CDN及高可用性,浪费流量、计算时间等资源

目的

  • 简单统一存储接入标准,为业务屏蔽存储技术细节
  • 统一的存储配置管理中心,达到快速运维能力
  • 可复用,节省项目重复开发成本
  • 可扩展性,可接入更多类型第三方或自有存储服务

现市场上主要对象存储服务类型

他们有一个共同的特性,都兼容亚马逊S3存储协议

华为云存储

权限机制

  • 取永久访问密钥(AK/SK)
  • 临时访问密钥场景(AK/SK/securitytoken)
    file

Java SDK上传

TempTokenDO tempTokenDO = getTempToken();
ObsClient obsClient = new ObsClient(tempTokenDO.getAk(), tempTokenDO.getSk(), tempTokenDO.getSt(), thirdpartyEndpoint);
 PutObjectResult putObjectResult = obsClient.putObject(bucketName, key, fileInputStream);

//获取token
 private TempTokenDO getTempToken(String ak, String sk){

        StopWatch sw = new StopWatch();
        TempTokenDO tempTokenDO = null;
        try{
            sw.start("getTempToken");
            ICredential auth = new GlobalCredentials().withAk(ak).withSk(sk);
            IamClient client = IamClient.newBuilder().withCredential(auth).withRegion(IamRegion.valueOf(this.region)).build();
            CreateTemporaryAccessKeyByTokenRequest request = new CreateTemporaryAccessKeyByTokenRequest();
            CreateTemporaryAccessKeyByTokenRequestBody body = new CreateTemporaryAccessKeyByTokenRequestBody();
            List<TokenAuthIdentity.MethodsEnum> listIdentityMethods = new ArrayList<>();
            listIdentityMethods.add(TokenAuthIdentity.MethodsEnum.fromValue("token"));
            TokenAuthIdentity identityAuth = new TokenAuthIdentity();
            identityAuth.withMethods(listIdentityMethods);
            TokenAuth authBody = new TokenAuth();
            authBody.withIdentity(identityAuth);
            body.withAuth(authBody);
            request.withBody(body);

            CreateTemporaryAccessKeyByTokenResponse response = client.createTemporaryAccessKeyByToken(request);
            Credential credential = response.getCredential();
            if(null == credential){
                throw new BaseException(StorageMessage.B4101);
            }
            tempTokenDO = new TempTokenDO().setAk(credential.getAccess()).setSk(credential.getSecret())
                    .setSt(credential.getSecuritytoken()).setExpiresAt(credential.getExpiresAt());

        }catch (ServiceResponseException e) {
            log.error("获取临时凭证异常:"+e.getMessage(), e);
            throw new BaseException(StorageMessage.B4101, e);
        }finally {
            if(sw.isRunning()){
                sw.stop();
            }
            log.debug("sw_getTempToken:"+sw.toString());
        }
        return tempTokenDO;

    }

JS SDK上传

  • 永久访问秘钥(AK/SK)创建OBS客户端代码如下:

    //未引入AMD,直接通过构造函数创建ObsClient实例
    var obsClient = new ObsClient({access_key_id: '*** Provide your Access Key ***',secret_access_key: '*** Provide your Secret Key ***',server : 'https://your-endpoint'});
  • 临时访问秘钥(AK/SK/SecurityToken)创建OBS客户端代码如下

    //未引入AMD,直接通过构造函数创建ObsClient实例
    var obsClient = new ObsClient({access_key_id: '*** Provide your Access Key ***',secret_access_key: '*** Provide your Secret Key ***',security_token: '*** Provide you Security Token ***',server : 'https://your-endpoint'});

阿里云存储

权限机制

file

Java SDK上传

JS SDK上传

七牛云存储

Kodo 是七牛云提供的高可靠、强安全、低成本、可扩展的存储服务。可通过控制台、API、SDK 等方式简单快速地接入七牛存储服务,实现海量数据的存储和管理。对文件下载进行加速,智能多媒体 人脸技术、场景物体识别、OCR 文字识别和内容审核等

请参阅对象存储文档:https://developer.qiniu.com/kodo

接入流程

注册七牛云帐号-->创建空间-->绑定域名-->上传下载等资源管理

参考文档:https://developer.qiniu.com/kodo/1233/console-quickstart

空间(bucket)

空间是资源的组织管理单位,一个资源必然位于某个空间中。可以为每个空间设置一系列的属性,以对资源提供合理的管理动作

公开空间:可通过文件对象的 URL 直接访问。
私有空间:文件对象的访问则必须获得拥有者的授权才能访问。

空间设置功能

  • 访问控制
  • 默认首页设置
  • 404 页面设置
  • 文件客户端缓存 maxAge
  • 空间日志
  • 内容审核
  • 标签管理
  • 空间授权
  • Referer 防盗链
  • 跨域设置
  • 生命周期设置
  • 事件通知
  • 镜像回源
  • 删除空间
  • 原图保护

编程模型

file

安全机制

七牛云存储服务的过程中,需要考虑安全机制的三种场景对应三种凭证:

https://github.com/qiniu/java-sdk/blob/master/src/main/java/com/qiniu/util/Auth.java

推荐的安全模型如下所示:
file

上传资源

https://developer.qiniu.com/kodo/1234/upload-types
上传文件的名称中,不支持\0字符,若文件名中存在\0字符,则会返回 400 Bad Request 和 error message “key must not contain null byte”。
上传文件名 utf-8 编码字符,长度不超过 750 字节 。

  • 表单上传
    表单上传是指在一个单一的 HTTP POST 请求中完成一个文件的上传,比较适合简单的应用场景和尺寸较小的文件
  • 分片上传
    分片上传是将一个文件分为多个小数据块,每个小数据块以一个独立的 HTTP 请求分别上传。所有小数据块都上传完成后,再发送一个请求给服务端将这些小数据块组织成一个逻辑资源,以完成上传过程
  • 上传后续动作
    在上传时开发者可以指定上传完成后服务端的后续动作,例如回调通知(callback)、自定义响应内容、303重定向等

下载资源

https://developer.qiniu.com/kodo/1232/download-process

  • 公开资源下载
    公开资源下载通过 HTTP GET 的方式访问资源 URL 即可。资源 URL 的构成如下:

    http://<domain>/<key>

    其中有两种形态:七牛子域名和自定义域名。默认分配测试域名为七牛子域名,形式类似于 78re52.com1.z0.glb.clouddn.com,用户可以通过以下 URL 下载名为 resource/flower.jpg的资源

    http://78re52.com1.z0.glb.clouddn.com/resource/flower.jpg

您也可以为某特定空间,绑定自定义域名,例如i.example.com,您就可以通过以下 URL 访问同样的资源:

http://i.example.com/resource/flower.jpg
  • 私有资源下载

当您将空间设置成私有时,必须获得授权,才能对空间内的资源进行访问。
私有资源下载是通过HTTP GET的方式访问特定的 URL。私有资源URL与公开资源URL相比只是增加了两个参数e和token,分别表示过期时间和下载凭证。一个完整的私有资源 URL 如下所示:

http://<domain>/<key>?e=<deadline>&token=<downloadToken>

SDK

https://developer.qiniu.com/sdk#official-sdk

file

AWS S3 兼容

为了使众多基于AWS S3接口协议开发的各类应用及服务能够轻便的接入七牛对象存储,七牛对象存储兼容了AWS S3常用接口。接口的具体兼容情况,在下文中做了详细叙述。

七牛云存储兼容S3协议接口,是为了尽可能的方便基于 AWS S3 而开发的应用接入到七牛对象存储。如果您刚刚开始着手开发新的应用,为了更好的使用七牛对象存储丰富的产品功能,更推荐使用原生接口进行开发。

实用工具

https://developer.qiniu.com/sdk#official-tool

file

实例演示

后端上传

https://github.com/qiniu/java-sdk


@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest
@Slf4j
class UploadTest {
/**
 * 空间
 */
private static String bucketName;

/**
 * 下载自定义域名
 */
private String endpoint;

/**
 * 上传管理器
 */
UploadManager uploadManager;

/**
 * 空间管理器
 */
private BucketManager bucketManager;

/**
 * 验证管理器
 */
Auth auth = null;

@BeforeAll
void init(){
    this.bucketName = "hushow";
    this.endpoint = "http://qnvs9yac3.hn-bkt.clouddn.com";
    String accessKey = "PheIieVFKcYRmdn6mFDNZ9uIO6NJN36Q4fM1HudC";
    String secretKey = "OZWR8o20c0OrLryl2TJoH8JAu8LifyTo3rto-OjC";
    this.auth = Auth.create(accessKey, secretKey);
    this.uploadManager = new UploadManager(new Configuration());
    bucketManager = new BucketManager(auth, new Configuration());
}

@Test
void uploadTest() throws Exception{
    String token = auth.uploadToken(bucketName);
    String fileUrl = "classpath:images/1.jpg";
    File file = ResourceUtils.getFile(fileUrl);
    String key = file.getName();
    Response response = uploadManager.put(file, key, token);
    log.info("response:{}", JSONObject.toJSONString(response));
    Assertions.assertTrue(response.isOK(), "上传失败");
}

@Test
void deleteTest() throws Exception{
    String key = "1.jpg";
    Response response = bucketManager.delete(bucketName, key);
    log.info("response:{}", JSONObject.toJSONString(response));
    Assertions.assertTrue(response.isOK(), "删除失败");
}

}


上传结果响应
```json
{
    "address": "upload-z2.qiniup.com/14.29.110.7:443", 
    "duration": 2.352, 
    "info": "POST https://upload-z2.qiniup.com/  
{ResponseInfo:com.qiniu.http.Response@1f9c6ea,status:200, reqId:Y40AAADoLc02HWAW, xlog:X-Log, xvia:, adress:upload-z2.qiniup.com/14.29.110.7:443, duration:2.352000 s, error:null}  
{\"hash\":\"Fm1LTYXuHg2xpqJ0-lTcPE11zUcY\",\"key\":\"1.jpg\"}", 
    "json": true, 
    "method": "POST", 
    "networkBroken": false, 
    "oK": true, 
    "reqId": "Y40AAADoLc02HWAW", 
    "serverError": false, 
    "statusCode": 200, 
    "xlog": "X-Log", 
    "xvia": ""
}

查看上传附件

http://qnvs9yac3.hn-bkt.clouddn.com/1.jpg

前端上传

https://github.com/qiniu/js-sdk

http://jssdk-v2.demo.qiniu.io/

需求梳理

  • 统一前端附件上传下载接口标准及SDK封半
  • 统一后端附件管理接口标准及SDK封装
  • 支持快速切换存储方案,对业务无感知,调整相关参数和endPoint即可
  • 支持自有的分存式存储和普通单机存储方案接入
  • 保证第三方存储的优势,避免浪费资源
  • 前端代码devOps接入OSS存储中
    file

初步方案

file

具体场景设计

上传资源

  • 华为云JS SDK接入场景
    file

  • 本地普通存储JS SDK接入场景
    file

下载资源

  • 公开资源
    file

  • 私有资源
    file

相关文章

线上PostgreSQL锁表故障分析
PostgreSQL创建外部表场景及使用
vim常用命令
navicat15 for linux桌面的破解激活
快速实现通用的办公文档在线预览方案
自建流媒体服务,快速打造自己的短视频点播平台

发布评论