写点什么

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

作者:浩宇天尚
  • 2021 年 12 月 17 日
  • 本文字数:5489 字

    阅读完需:约 18 分钟

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

参考资料

  • https://hao123.blog.csdn.net/article/details/109065199?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.opensearchhbase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.opensearchhbase

  • https://github.com/krishnasrinivas/wikinotes/wiki/minio-scaling

  • https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/Welcome.html

  • Klaus Post 官网:https://klauspost.com/

  • https://github.com/klauspost/reedsolomon

  • https://developer.ibm.com/articles/cl-cloudstorage/

  • https://github.com/minio/dsync

  • https://github.com/minio/dsync/pull/22#issue-176751755

  • https://github.com/minio/minio/blob/master/cmd/xl-sets.go

  • https://min.io/resources/docs/MinIO-throughput-benchmarks-on-NVMe-SSD.pdf

  • https://github.com/minio/minio/blob/master/cmd/admin-heal-ops.go

  • https://github.com/klauspost/reedsolomon/blob/master/options.go

  • https://github.com/minio/dsync

  • https://min.io/resources/docs/CPG-MinIO-implementation-guide.pdf

  • https://github.com/minio/minio/issues/7986

  • https://docs.min.io/docs/minio-bucket-notification-guide.html

发布于: 19 小时前阅读数: 7
用户头像

浩宇天尚

关注

🏆 InfoQ写作平台-签约作者 🏆 2020.03.25 加入

【个人简介】酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“ 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、APM专题及微服务/分布式体系等

评论

发布
暂无评论
【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南