【分布式技術專題】「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都非常優秀,各自有各自的優勢。准備使用對象存儲軟件的用戶,應該根據自己單位的需求、技術儲備等實際情況,選擇適當的軟件。

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM