MinIO分布式专题(第一章带你认识MinIO)

Posted 风清扬逍遥子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MinIO分布式专题(第一章带你认识MinIO)相关的知识,希望对你有一定的参考价值。

        市面上有很多的分布式存储方案,比如FastDFS,MongoDB,或者目前云厂商的存储

比如阿里云,腾讯云,华为云等等,但是对于很多企业不愿意上云,于是业内就出现了很多

的方案,而本章讲述的MinIO,一定是你看了就绝壁拍手称赞的!

目录

1、分布式文件系统应用场景

1.1、MinIO介绍

1.2、MinIO基础概念

1.3、纠删码

2、MinIO环境搭建

2.1、单机部署

3、Springboot集成MinIO基础实现


1、分布式文件系统应用场景

        互联网下海量的非结构化存储的需求背景下,比如:

  • 电商网站,存储海量的商品图片
  • 视频网站,海量的视频文件
  • 网盘,海量的文件
  • 社交网站等等

        在这样的背景下,传统的FastDFS部署太过于繁琐,动不动就是来个nginx,然后配置一

堆参数和设置,尤其是做分布式的时候,那维护成本一下就上来了,从维护和部署的角度,

FastDFS不是一个好的选择,而从迭代的角度,FastDFS早就不维护了,有很多需求是无法

支持到的,那么就需要你自己思考写源码打包了。

        同理HDFS部署也不简单,而且Hadoop适合超大文件的存储,并且文件都需要分片,应

用场景更多是计算处理,实时数据分析,并且其实HDFS比较吃硬件设备,因为偏于计算,

所以对CPU的要求比较高,对于中小企业的业务量并没有这么大,所以应用场景这块也比较

难接触到,但是HDFS的功能还是十分强大的!!还是根据业务进行选型。

1.1、MinIO介绍

        MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云

存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份

数据和容器/虚拟机镜像等,而 一个对象文件可以是任意大小,从几kb到最大5T不等。

        MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或

mysql

官网:http://minio.org.cn/overview.shtml

        对于中小型企业,如果不选择存储上云,那么 Minio 是个不错的选择,麻雀虽小,五脏

俱全。当然 Minio 除了直接作为对象存储使用,还可以作为云上对象存储服务的网关层,无

缝对接到 Amazon S3、MicroSoft Azure。

        在中国:阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在

使用MinIO产品。

优点:

  • 部署简单: 一个single二进制文件即是一切,还可支持各种平台。
  • minio支持海量存储,可按zone扩展(原zone不受任何影响),支持单个对象最大5TB; 兼容Amazon S3接口,充分考虑开发人员的需求和体验;
  • 低冗余且磁盘损坏高容忍,标准且最高的数据冗余系数为2(即存储一个1M的数据对象,实际占用 磁盘空间为2M)。但在任意n/2块disk损坏的情况下依然可以读出数据(n为一个纠删码集合(Erasure Coding Set)中的disk数量)。并且这种损坏恢复是基于单个对象的,而不是基于整个存储卷的。
  • 读写性能优异

        体现在下图,HDD硬盘下,运行16个节点的Minio集群,读性能在10GB/s,写性能在

8GB/s,这个是非常高的。

上述的优点先过个眼缘,后面我会一一去解释并演示出来。

1.2、MinIO基础概念

  • Object:存储到 Minio 的基本对象,如文件、字节流,Anything...
  • Bucket:用来存储 Object 的逻辑空间。每个 Bucket 之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
  • Drive:即存储数据的磁盘,在 MinIO 启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在 Drive 里。
  • Set :即一组 Drive 的集合,分布式部署根据集群规模自动划分一个或多个 Set ,每个 Set 中的 Drive 分布在不同位置。一个对象存储在一个 Set 上。(For example: {1...64} is divided into 4 sets each of size 16.)
    • 一个对象存储在一个Set上
    • 一个集群划分为多个Set
    • 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出 一个SET中的Drive尽可能分布在不同的节点上

1.3、纠删码

        什么是纠删码呢?纠删码(Erasure Code)简称EC,是一种数据保护方法,它将数据

分割成片段,把冗余数据块扩展、编码,并将其存储在不同的位置,比如磁盘、存储节点或

者其它地理位置。

        从数据函数角度来说,纠删码提供的保护可以用下面这个简单的公式来表示:n = k +

m。变量“k”代表原始数据或符号的值。变量“m”代表故障后添加的提供保护的额外或冗余符号

的值。变量“n”代表纠删码过程后创建的符号的总值。

        举个例子,我有16块磁盘,就是n,有10份原始文件,一模一样,称为k,16 = 10 +

m,这个m就是可以恢复的校验块个数,所以m是6,任意6个不可用,原始文件都可以恢

复,极端情况,10个原始文件坏掉6个,靠4个原始的加上6个校验块,可以把坏掉的6个原始

文件恢复,这个用到数学行列式矩阵知识,后面再说。

2、MinIO环境搭建

2.1、单机部署

环境:Centos7.x

        在/usr下建立一个文件夹,我这边建立了叫做chenxin。

         在/usr/chenxin下我区分两个目录,一个是my-minio,一个是data,my-minio是来存放

minio的安装包的,data存放上传的存储数据;

        创建目录:sudo mkdir my-minio

        然后下载安装包:

wget https://dl.min.io/server/minio/release/linux-amd64/minio

        如果命令不存在,记得yum -y install wget 安装下

        如果你在使用命令下载的时候,下载不下来。可以多试几次,或者直接用别的机器访问

上面的url直接进行下载,然后上传到服务器上去。

        赋予执行权限:chmod +x minio

        启动minio写了个脚本,后面有需要可以直接自己搞个脚本启动,命名为start.sh

nohup /usr/chenxin/my-minio/minio server /usr/chenxin/data > /usr/chenxin/data/minio.log 2>&1 &

        执行启动后打印

API: http://10.12.105.15:9000  http://127.0.0.1:9000 

Console: http://10.12.105.15:34387 http://127.0.0.1:34387 

Documentation: https://docs.min.io

WARNING: Console endpoint is listening on a dynamic port (34387), please use --console-address ":PORT" to choose a static port.
WARNING: Detected default credentials 'minioadmin:minioadmin', we recommend that you change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables

        这里面的信息,API是你将来用上传的时候,调用的上传接口所在的ip+端口

Minio中存在服务端的控制台,地址就是Console的地址,当然下面会有警告,建议Console

控制台的地址ip你可以固定住,通过--console-address:":port"固定,不然你每次启动,这个

端口都会随机变更一个;下面还有一个警告,默认的服务端控制台账号密码是

minioadmin/minioadmin,如果你想修改的话,在linux里执行这两条命令就行了

export MINIO_ROOT_USER=随便你的账号
export MINIO_ROOT_PASSWORD=你的密码

运行起来后,minio会在/root目录下生成一个配置目录.minio

        如果我们需要https,证书就需要放在这里面。默认的证书目录${HOME}/.minio/certs。 

        这里我们暂时用不到;

        那我们的start.sh命令就改成这样即可

nohup /usr/chenxin/my-minio/minio server --console-address ":50000" /usr/chenxin/data > /usr/chenxin/data/minio.log 2>&1 &

        访问控制台ip+50000端口,发现一直在转圈,那检查下防火墙,并将其关闭

        systemctl stop firewalld.service  关闭防火墙

        进来后是这样的界面就表示已经成功了。

         进入系统后,我们先要点击菜单的Bucket右上角创建一个Bucket

        点击菜单的Object Browser (对象浏览),选中后就可以上传文件

         还可以删除文件等操作,这些后面我再通过客户端代码演示。

        来看下服务器上这个数据是怎么存放的,会在data目录下,Bucket名字作为目录名称,

目录下存放你上传的文件。

        单机部署就先到这,这样我们的minio服务端基本的配置算是ok了。

3、Springboot集成MinIO基础实现

        我想很多人应该都比较在意的是这些基础功能的使用,所以我在第一章就先让大家用起

来,后面章节会继续延伸;

        新建一个项目springboot-minio,我放下主代码:

        

@RestController
@Slf4j
public class FileController {

    @Autowired
    private MinioClient minioClient;
    @Value("${minio.bucketName}")
    private String bucketName;

    @GetMapping("/list")
    public List<Object> list() throws Exception {
        //获取bucket列表
        Iterable<Result<Item>> myObjects = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        Iterator<Result<Item>> iterator = myObjects.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while (iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSON.parse(String.format(format, item.objectName(),
                    formatFileSize(item.size()))));
        }
        return items;
    }

    @PostMapping("/upload")
    public ResultBean upload(@RequestParam(name = "file", required = false)
                              MultipartFile[] file) {
        ResultBean resultBean = ResultBean.newInstance();
        if (file == null || file.length == 0) {
            return resultBean.error("上传文件不能为空");
        }
        List<String> orgfileNameList = new ArrayList<>(file.length);
        for (MultipartFile multipartFile : file) {
            String orgfileName = multipartFile.getOriginalFilename();
            orgfileNameList.add(orgfileName);
            try {
                //文件上传
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(
                        PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(
                                in, multipartFile.getSize(), -1)
                                .contentType(multipartFile.getContentType())
                                .build());
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
                return resultBean.error("上传失败");
            }
        }
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("bucketName", bucketName);
        data.put("fileName", orgfileNameList);
        return resultBean.ok("上传成功", data);
    }

    @RequestMapping("/download/{fileName}")
    public void download(HttpServletResponse response, @PathVariable("fileName")
            String fileName) {
        InputStream in = null;
        try {
            // 获取对象信息
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(fileName, "UTF-8"));
            //文件下载
            in = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build());
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
    }

    @DeleteMapping("/delete/{fileName}")
    public ResultBean delete(@PathVariable("fileName") String fileName) {
        ResultBean resultBean = ResultBean.newInstance();
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            log.error(e.getMessage());
            return resultBean.error("删除失败");
        }
        return resultBean.ok("删除成功", null);
    }

    private static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }

}

        yml配置

minio:
  endpoint: http://10.12.105.15:9000
  accesskey: minioadmin
  secretKey: minioadmin
  bucketName: chenxin-bucket

        启动Springboot项目后,调用localhost:8080/list

        用postman上传试下,很明显上传成功了。

         同理下载也是一样,看下接口就清楚了,接口后面跟着文件名就可以下载了,当然这个

后面要做成存储到数据库里,这个后面我拓展出来,先会使用再说。

        代码的地址我贴出来:https://gitee.com/cx_gitee/springboot-minio

        本章主要介绍了最快认识MinIO的用法和基础结合,后面章节将继续扩展分布式存储的

部署方案和业务场景,希望多多支持!!

以上是关于MinIO分布式专题(第一章带你认识MinIO)的主要内容,如果未能解决你的问题,请参考以下文章

分布式技术专题「OSS中间件系列」Minio的Server端服务的架构和实战搭建

分布式技术专题「OSS中间件系列」Minio的Server端服务的架构和实战搭建

#私藏项目实操分享#分布式技术专题「OSS中间件系列」Minio的Server端服务的架构和实战搭建

分布式技术专题「OSS中间件系列」从0到1的介绍一下开源对象存储MinIO技术架构

分布式技术专题「OSS中间件系列」从0到1的介绍一下开源对象存储MinIO技术架构

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