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都非常优秀,各自有各自的优势。准备使用对象存储软件的用户,应该根据自己单位的需求、技术储备等实际情况,选择适当的软件。

    参考资料

    极限就是为了超越而存在的
  • 相关阅读:
    03_ if 练习 _ little2big
    uva 11275 3D Triangles
    uva 12296 Pieces and Discs
    uvalive 3218 Find the Border
    uvalive 2797 Monster Trap
    uvalive 4992 Jungle Outpost
    uva 2218 Triathlon
    uvalive 3890 Most Distant Point from the Sea
    uvalive 4728 Squares
    uva 10256 The Great Divide
  • 原文地址:https://www.cnblogs.com/liboware/p/15703809.html
Copyright © 2011-2022 走看看