• 【Java互联网技术】MinIO分布式文件存储服务


    应用场景

    互联网海量非结构化数据的存储

    基本概念

    Object:存储的基本对象,如文件、字节流

    Bucket:存储Object的逻辑空间,相当于顶层文件夹

    Drive:存储数据的磁盘,在MinIO启动时,以参数的方式传入

    Set:多个Drive的集合。分布式部署时划分一个或多个Set,一个Object存在Set上,Set中的每个Drive中都会有相同的Object

    特点

    • 部署简单,整体只有一个二进制文件,还可支持各种平台
    • 支持海量存储,单个对象最大可达5TB
    • 兼容Amazon S3接口,充分考虑开发人员的需求和体验
    • 低冗余且磁盘损坏高容忍
    • 读写性能优异(HDD在16个结点的MinIO集群的读速度可达10GB/s,写速度可达8.5GB/s)

    纠删码

    Erasure Code(EC)。MinIO使用纠删码来保证高可靠性,使用highwayhash来处理数据损坏。n份原始数据保存后会生成m份纠删码算法转换后的数据,当有任意小于等于m份的数据损坏,就能通过剩下的额数据还原出来。

    存储形式

    文件上传到MinIO后,存储在对应的磁盘中,以Bucket名称为目录,文件名为下一级目录,这个目录下保存文件的编码数据块、校验块、元数据文件(json格式)

    Docker部署MinIO(普通模式)

    1、拉取镜像

    docker pull minio/minio
    
    • 1

    2、创建挂载目录

    mkdir /wanfeng/minio/data
    mkdir /wanfeng/minio/config
    
    • 1
    • 2

    3、运行容器

    docker run -p 9000:9000 -p 9001:9001 --name wanfeng_minio --privileged=true \
    -e "MINIO_ROOT_USER=登录用户名" \
    -e "MINIO_ROOT_PASSWORD=登录密码" \
    -v /wanfeng/minio/data:/data \
    -v /wanfeng/minio/config:/root/.minio \
    -d minio/minio server /data --console-address ":9001"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • -p 9000:9000 -p 9001:9001:必须暴露出两个端口用户客户端和服务端
    • MINIO_ROOT_USER:用户名
    • MINIO_ROOT_PASSWORD:密码
    • /wanfeng/minio/data:/data:数据文件映射目录
    • /wanfeng/minio/config:/root/.minio:配置文件映射目录
    • –console-address “:9001”:指定服务端端口号(通过该端口访问minio服务)

    Docker部署MinIO(纠删码模式)

    纠删码的作用就是在上传一份数据后,会生成n个原始数据块和n个奇偶校验块,若任意丢失不超过n块盘,都可以进行数据恢复。

    1、在docker启动时增加多块盘

    docker run -p 9000:9000 -p 9001:9001 --name wanfeng_minio --privileged=true \
    -e "MINIO_ROOT_USER=登录用户名" \
    -e "MINIO_ROOT_PASSWORD=密码" \
    -v /wanfeng/minio/config:/root/.minio \
    -v /wanfeng/minio/data1:/data1 \
    -v /wanfeng/minio/data2:/data2 \
    -v /wanfeng/minio/data3:/data3 \
    -v /wanfeng/minio/data4:/data4 \
    -v /wanfeng/minio/data5:/data5 \
    -v /wanfeng/minio/data6:/data6 \
    -d minio/minio server /data{1...6} --console-address ":9001"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Docker部署MinIO(分布式集群)

    分布式集群可以避免单台服务器故障带来的影响(单点故障),将minio

    Java API整合MinIO

    1、引入依赖

    <dependency>
        <groupId>io.miniogroupId>
        <artifactId>minioartifactId>
        <version>8.3.0version>
    dependency>
    
    
    <dependency>
        <groupId>me.tongfeigroupId>
        <artifactId>progressbarartifactId>
        <version>0.5.3version>
    dependency>
    
    <dependency>
        <groupId>com.squareup.okhttp3groupId>
        <artifactId>okhttpartifactId>
        <version>4.10.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2、API使用

    @SpringBootTest
    public class MinioApplicationTests {
    
        private MinioClient minioClient;
    
        public static final String IMAGE_FILE_NAME = "wallhaven01.jpg";
        public static final String TEXT_FILE_NAME = "笔记.txt";
    
        public static final String UPLOAD_FOLDER_PATH = "src/test/resources/upload";
        public static final String DOWNLOAD_FOLDER_PATH = "src/test/resources/download";
    
        @BeforeEach
        public void connectMinio(){
            minioClient = MinioClient.builder().endpoint(ServerConstant.CENTOS_IP_ADDRESS, 9000, false)
                    .credentials(LoginConstant.MINIO_ROOT_USERNAME, LoginConstant.MINIO_ROOT_PASSWORD)
                    .build();
        }
    
        /**
         * 本地文件上传
         */
        @Test
        public void uploadFile(){
            try {
                //判断bucket是否存在
                boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket("ectest").build());
                if(!bucketExists){
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket("ectest").build());
                }else{
                    System.out.println("Bucket[ectest] already exists");
                }
    
                /**
                 * bucket:指定上传的bucket
                 * object:上传到minio的路径(到文件名)
                 * filename:上传的本地文件路径(到文件名)
                 */
                minioClient.uploadObject(UploadObjectArgs.builder()
                        .bucket("ectest")
                        .object(IMAGE_FILE_NAME)
                        .filename(UPLOAD_FOLDER_PATH + File.separator + IMAGE_FILE_NAME).build());
    
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 上传流
         */
        @Test
        public void uploadInputStream() throws Exception {
            File file = new File(UPLOAD_FOLDER_PATH + File.separator + IMAGE_FILE_NAME);
            FileInputStream fileInputStream = new FileInputStream(file);
    
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket("ectest")
                    .object("image_" + NumberFactory.getRandomNumber(5) + ".jpg" )
                    .stream(fileInputStream, fileInputStream.available(), -1)
                    .build());
    
            fileInputStream.close();
        }
    
        /**
         * 下载文件
         */
        @Test
        public void downloadFile(){
            try {
                //判断bucket是否存在
                boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket("ectest").build());
                if(!bucketExists){
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket("ectest").build());
                }else{
                    System.out.println("Bucket[ectest] already exists");
                }
    
                /**
                 * bucket:指定上传的bucket
                 * object:minio中的文件路径
                 * filename:下载到本地的路径(到文件名)
                 */
                minioClient.downloadObject(DownloadObjectArgs.builder()
                        .bucket("ectest")
                        .object(IMAGE_FILE_NAME)
                        .filename(DOWNLOAD_FOLDER_PATH + File.separator + "minio_image_" + NumberFactory.getRandomNumber(8) + ".jpg").build());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    
        /**
         * 下载流
         */
        @Test
        public void downloadInputStream() throws Exception{
            //获取流
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket("ectest")
                    .object(IMAGE_FILE_NAME)
                    .build());
            File file = new File(DOWNLOAD_FOLDER_PATH + File.separator + "image_aaa.jpg");
            FileUtils.copyInputStreamToFile(inputStream, file);
            inputStream.close();
    
            //直接把流保存到本地文件
            minioClient.downloadObject(DownloadObjectArgs.builder()
                    .bucket("ectest")
                    .object(IMAGE_FILE_NAME)
                    .filename(DOWNLOAD_FOLDER_PATH + File.separator + "image_bbb.jpg").build());
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    SpringBoot整合MinIO

    本质就是把连接minio服务的参数放在配置文件中,并通过Bean对象在SpringBoor应用启动时生成MinioClient对象。

    1、配置文件application.yml

    minio:
      endpoint: http://ip:port
      rootUserName: 用户名
      rootPassword: 密码
      bucketName: bucket名称
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、读取配置属性的类

    @Data
    @Component
    @ConfigurationProperties(prefix = "minio")
    public class MinioProperties {
        private String endpoint;
        private String rootUserName;
        private String rootPassword;
        private String bucketName;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、生成MinioClient对象的类

    @Configuration
    public class MinioClientFactory {
    
        @Resource
        private MinioProperties minioProperties;
    
        @Bean
        public MinioClient getDefaultMinioClient(){
            return MinioClient.builder()
                    .endpoint(minioProperties.getEndpoint())
                    .credentials(minioProperties.getRootUserName(), minioProperties.getRootPassword())
                    .build();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4、编写controller测试

    @RestController
    @Slf4j
    @RequestMapping("/minio")
    public class MinioController {
    
        @Resource
        private MinioClient minioClient;
    
        @Value("${minio.bucketName}")
        private String bucketName;
    
        @GetMapping("/list_object_name")
        public List<String> listObject() throws Exception {
            Iterable<Result<Item>> objectResults = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName).build());
    
            List<String> objectNameList = new ArrayList<>();
            for (Result<Item> objectResult : objectResults) {
                Item item = objectResult.get();
                objectNameList.add(item.objectName());
            }
            return objectNameList;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    显卡T600和RTX3050哪个好
    SAP S4 BC 关于ABAP write 方式展示的清单 导出excel的问题
    【C++代码】平衡二叉树,二叉树的所有路径,左叶子之和--代码随想录
    深度优化数据库性能:Linux 内核参数调整解析
    【OpenCV DNN】Flask 视频监控目标检测教程 08
    SV-7042VP sip广播4G无线网络号角
    Eureka使用及原理详解
    7.(高级示例篇)cesium漫游飞行
    JVM外部调试工具:JMXTerm
    为Mkdocs网站添加评论系统(以giscus为例)
  • 原文地址:https://blog.csdn.net/Dae_Lzh/article/details/133613758