zoukankan      html  css  js  c++  java
  • 【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南

    Minio的元数据

    数据存储

    MinIO对象存储系统没有元数据数据库,所有的操作都是对象级别的粒度的,这种做法的优势是:

    • 个别对象的失效,不会溢出为更大级别的系统失效。

    • 便于实现“强一致性”这个特性。此特性对于机器学习与大数据处理非常重要。

    数据管理

    元数据与数据一起存放在磁盘上:数据部分纠删分片以后存储在磁盘上,元数据以明文形式存放在元数据文件里(xl.json)。假定对象名字为obj-with-metadata, 它所在的桶的名字是bucket_name, disk是该对象所在纠删组的任一个磁盘的路径,如下目录:

    disk/bucket_name/obj-with-metadata 
    

    记录了这个对象在此磁盘上的信息。其中的内容如下:

    xl.json

    xl.json即是此对象的元数据文件。对象的元数据文件xl.json的内容是如下这种形式的json字符串:

    字段说明
    format字段

    该字段指明了这个对象的格式是xl,MinIO内部存储数据主要有两种数据格式:xl与fs。使用如下命令启动的MinIO使用的存储格式是fs:

    这种模式主要用于测试, 对象存储很多API都是并没有真正实现的桩函数。在生产环境所用的部署方式(本地分布式集群部署、联盟模式部署、云网关模式部署)中,存储格式都是xl。

    part.1 :对象的第一个数据分片

    stat字段

    记录了此对象的状态,包括大小与修改时间,如下图:

    erasure字段

    这个字段记录此对象与纠删码有关的信息,如下图:

    其中的algorithm指明了此对象采用的是Klaus Post实现的纠删码,生成矩阵是范德蒙矩阵。

    • data,parity指明了纠删组中数据盘、校验盘的个数。

    • blockSize 指明了对象被分块的大小,默认是5M(请参见上一节“数据分布与均衡”)。

    • index指明了当前磁盘在纠删组中的序号。

    • distribution:每个纠删组的数据盘、校验盘的个数是固定的,但是不同的对象的分片写入这个纠删组的不同磁盘的顺序是不同的。这里记录了分布顺序。

    • checksum:它下面的字段个数跟此对象的分片数量有关。在旧版本的MinIO对象存储系统,每一个分片经过hash函数计算出的checksum会记录在元数据文件的这个位置。最新版的MinIO会把checksum直接计入分片文件(即part.1等文件)的前32个字节。

    此字段之下algorithm的值是”highwayhash256S”表明checksum值是写入分片文件的。

    Minio的整合Java客户端

    文件服务器在用minio,没有独立成微服务也没有抽取starter,所以简单测试一下集成和抽取starter,创建springboot项目集成minio把文件上传成功

    Maven环境的pom依赖

    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>6.0.11</version>
    </dependency>
    

    spring的yml配置:

    minio:
      endpoint: http://192.168.8.50:9000
      accessKey: admin
      secretKey: 123123123
    

    配置类 MinioProperties :

    @Data
    @ConfigurationProperties(prefix = "minio")
    public class MinioProperties {
        //连接url
        private String endpoint;
        //用户名
        private String accessKey;
        //密码
        private String secretKey;
    }
    

    工具类 MinioUtil

    import cn.hutool.core.util.StrUtil;
    import com.team.common.core.constant.enums.BaseResultEnum;
    import com.team.common.core.exception.BusinessException;
    import io.minio.MinioClient;
    import lombok.AllArgsConstructor;
    import lombok.SneakyThrows;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    import java.io.InputStream;
    @AllArgsConstructor
    @Component
    public class MinioUtil {
        private final MinioClient minioClient;
        private final MinioProperties minioProperties;
    
        /**
         * http文件上传
         * @param bucketName
         * @param file
         * @return 访问地址
         */
        public String putFile(String bucketName,MultipartFile file) {
            return this.putFile(bucketName,null,file);
        }
    
        /**
         * http文件上传(增加根路径)
         * @param bucketName
         * @param folder
         * @param file
         * @return 访问地址
         */
        public String putFile(String bucketName,String folder,MultipartFile file) {
            String originalFilename = file.getOriginalFilename();
            if (StrUtil.isNotEmpty(folder)){
                originalFilename = folder.concat("/").concat(originalFilename);
            }
            try {
                InputStream in = file.getInputStream();
                String contentType= file.getContentType();
                minioClient.putObject(bucketName,originalFilename,in,null, null, null, contentType);
            } catch (Exception e) {
                e.printStackTrace();
               throw new BusinessException(BaseResultEnum.SYSTEM_EXCEPTION.getCode(),"文件上传失败");
            }
            String url = minioProperties.getEndpoint().concat("/").concat(bucketName).concat("/").concat(originalFilename);
            return url;
        }
    
        /**
         * 创建bucket
         * @param bucketName
         */
        public void createBucket(String bucketName){
            try {
                minioClient.makeBucket(bucketName);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessException(BaseResultEnum.SYSTEM_EXCEPTION.getCode(),"创建bucket失败");
            }
        }
    
        @SneakyThrows
        public String getBucketPolicy(String bucketName){
            return minioClient.getBucketPolicy(bucketName);
        }
    }
    

    装配类:

    import io.minio.MinioClient;
    import io.minio.errors.InvalidEndpointException;
    import io.minio.errors.InvalidPortException;
    import lombok.AllArgsConstructor;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @AllArgsConstructor
    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioAutoConfiguration {
        private final MinioProperties minioProperties;
    
        @Bean
        public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
            MinioClient  client = new MinioClient(minioProperties.getEndpoint(),minioProperties.getAccessKey(),minioProperties.getSecretKey());
            return  client;
        }
    
        @ConditionalOnBean(MinioClient.class)
        @Bean
        public MinioUtil minioUtil(MinioClient minioClient,MinioProperties minioProperties) {
            return new MinioUtil(minioClient,minioProperties);
        }
    }
    
    spring.factories配置文件

    去掉主入口函数,去掉application.properties配置文件(新建一个测试用的springboot项目,把配置文件拿过去)
    剩下最重要的一步:在resources下创建META-INF/spring.factories文件,配置文件中加入需要自动装配的类

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.*(你的路径).MinioAutoConfiguration
    

    demo:

    import com.team.common.core.web.Result;
    import com.team.common.minio.MinioUtil;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.ApiParam;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    @Api(value = "uploadFile", tags = "文件上传")
    @RequestMapping("uploadFile")
    @RestController
    public class UploadFileController {
    
        @Autowired
        private MinioUtil minioUtil;
    
        @ApiOperation(value = "通用文件上传")
        @PutMapping("/upload")
        public Result uploadFile(@ApiParam("存储桶名称") String bucketName,@ApiParam("文件") MultipartFile file) {
            String url = null;
            try {
               url =  minioUtil.putFile(bucketName,file);
            } catch (Exception e) {
                e.printStackTrace();
            }
           return Result.success(url);
        }
    }
    

    打包安装到maven仓库,本地测试用的同一仓库地址的话可以直接maven install,新建一个springboot项目,填入application.properties,pom中增加starter的依赖。

    <dependency>
                <groupId>com.jxwy</groupId>
                <artifactId>minio-starter</artifactId>
                <version>0.0.1-SNAPSHOT</version>
    </dependency>
    

    其他OSS服务对比

    厂商支持

    国内使用Ceph的厂商、基于Ceph进行自研的存储厂商都比较多,在使用过程中遇到的问题(有些时候,甚至需要修改、增强乃至重新实现Ceph本身的功能),可以向相关厂商寻求支持。国际方面,Ceph早已被红帽收购,而红帽近期又被IBM收购。

    MinIO开发与支持的厂商只有MinIO公司。由于架构比较先进,语言高级,MinIO本身的程序比较容易读懂、修改。招聘Golang程序员来 维护MinIO所花费的成本,显然低于招聘c++程序员来维护Ceph。

    多语言客户端SDK

    二者均有常见编程语言的客户端,比如:python, java等。MinIO对象存储软件的开发SDK另外支持纯函数式的语言Haskell。

    技术文档

    内部实现的文档MinIO基本不存在。想要了解内部实现乃至参与开发的技术人员,只能到如下社区:http://minio.slack.com/ ,与MinIO的开发人员直接交流,或者自己阅读代码。Ceph的各种实现文档、算法说明文档非常丰富。这方面Ceph要比MinIO成熟很多。

    Ceph和MinIO的对比

    开源对象存储软件以MinIO,Ceph为典型代表。为帮助相关人员在选择对象存储系统之时选择合适的产品,此处对二者的特点、特性做一定讨论。

    MinIO优势

    部署极其简单

    MinIO系统的服务程序仅有minio一个可执行文件,基本不依赖其它共享库或者rpm/apt包。minio的配置项很少(大部分都是内核之类系统级的设置),甚至不配置也可以正常运行起来。百度、google、bing等搜索引擎上基本没有关于MinIO部署问题的网页,可见在实践中,很少有使用者遇到这方面的问题。

    相比之下,Ceph系统的模块,相关的rpm、apt包众多,配置项非常多,难以部署,难调优。某些Linux发行版的Ceph安装包甚至有bug,需要使用者手动改动Ceph的python脚本,才能安装完毕。

    二次开发容易

    MinIO对象存储系统除了极少数代码使用汇编实现以外,全部使用Golang语言实现。Ceph系统是使用业界闻名的难学难用的c++语言编写的。Golang语言由于产生较晚,吸收了很多语言尤其是c++的教训,语言特性比较现代化。

    相对而言,MinIO系统的维护、二次开发比较容易。

    网管模式支持多种其他存储

    通过网关模式,MinIO对象存储后端,可以对接各种现有的常见其它存储类型,比如的NAS系统,微软Azure Blob 存储、Google 云存储、HDFS、阿里巴巴OSS、亚马逊S3等,非常有利于企业复用现有资源,有利于企业低成本(硬件成本约等于零,部署MinIO对象存储软件即可)地从现有系统平滑升级到对象存储。

    Ceph优势

    数据冗余策略更加丰富,Ceph同时支持副本、纠删码,而MinIO只支持纠删码。对于个别的对于数据可靠性要求极高的单位,Ceph对象存储更加合适。

    参考硬件

    MinIO是符合软件定义存储SDS理念的,兼容主流X86服务器以及ARM/飞腾平台,同时也可以移植到诸如申威(Alpha架构)和龙芯(Mips架构)等硬件平台。

    下面这些符合工业标准的、广泛采用的服务器是经过MinIO inc.优化测试过的、MinIO对象存储软件表现优异的服务器:

    结论

    由以上讨论,可见作为对象存储软件来说,MinIO, Ceph都非常优秀,各自有各自的优势。准备使用对象存储软件的用户,应该根据自己单位的需求、技术储备等实际情况,选择适当的软件。

    参考资料

    极限就是为了超越而存在的
  • 相关阅读:
    JS数组及操作方法,堆和栈的概念
    JS变量作用域,浏览器预解析
    JS函数的基本概念
    JS循环语句
    JS程序三大结构及语法语句
    src与href
    JS概念及基本语法
    图片整合技术
    hdu6395 Sequence(分段矩阵快速幂)
    hdu6396 Swordsman(贪心)
  • 原文地址:https://www.cnblogs.com/liboware/p/15703809.html
Copyright © 2011-2022 走看看