【Spring Cloud】新闻头条微服务项目:分布式文件系统MinIO实现文章页面存取

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 主要介绍了MinIO的功能特点以及为什么要用MinIO,并且还介绍了如何封装MinIO为工具快速进行使用

 一:概述

       MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档。由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令可以运行起来。

       MinIO兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

二:为何选用MinIO

       其实我们也可以通过文章id到数据库中查询到该文章然后再通过模板引擎生成静态页面展示给用户,这种方式开发便捷,成本较低,但是将来要扩展业务的话比较困难。除此之外还可以采用第三方存储技术,比如阿里云、七牛云等,这种方案开发简单,功能强大且不用自己维护,缺点就是要收费。最后,还可以选用分布式文件系统来进行存储,其优点是容易实现扩容,但是复杂度较高,常用的分布式文件系统有FastDFS,其有如下优点:

    • 主从备份,高可用
    • 支持主从文件,支持自定义扩展名
    • 支持动态扩容

    其缺点就是没有完备的官方文档,近几年都没有进行过更新,而且环境搭建起来比较麻烦。

    而另外一个就是我们用到的MinIO,其优点如下

      • 性能高,准硬件条件下他能达到55GB/s的读,35GB/s的写速率
      • 部署自带管理页面
      • 它是MinIO.Inc运营的开源项目,社区活跃度高
      • 提供了所有主流开发语言的SDK

      当然其缺点就是不支持动态增加节点。

      三:MinIO特点

        • 数据保护
          Minio使用Minio Erasure Code(纠删码)来防止硬件故障。即便损坏一半以上的driver,但是仍然可以从中恢复。
        • 高性能
          作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率
        • 可扩容
          不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
        • SDK支持
          基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持
        • 有操作页面
          面向用户友好的简单操作界面,非常方便的管理Bucket及里面的文件资源
        • 功能简单
          这一设计原则让MinIO不容易出错、更快启动
        • 丰富的API
          支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等。
        • 文件变化主动通知
          存储桶(Bucket)如果发生改变,比如上传对象和删除对象,可以使用存储桶事件通知机制进行监控,并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等。

        四:实现方案

        image.gif编辑

               首先根据文章内容通过模板引擎技术生成静态html文件之后,把生成的html文件访问路径存入到文章数据库中,并同时把文件存入分布式文件系统MinIO,当用户访问某一篇文章时候就会根据文章id查询到数据库中该文章的URL地址,然后再根据该地址从MinIO获取文件并返回。

        五:功能实现

        1.创建容器

        (1)拉取镜像

        docker pull minio/minio server

        image.gif

        (2)启动容器

        docker run -p 9000:9000 -p 9090:9090 --name minio -d --restart=always -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data --console-address ":9090"

        image.gif

        (3)管理控制台

        打开浏览器输入http://你的ip:9090/login即可进行登录,账号为minio,密码为minio123

        image.gif编辑

        (4)创建桶

        image.gif编辑

        点击Create Bucket创建一个名为headlines的桶

        2.封装MinIO为Start

        (1)模块创建

        image.gif编辑

        导入如下依赖:

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
            </dependency>
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>7.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>

        image.gif

        (2)配置类

        MinIOConfigProperties

        package com.my.file.config;
        import lombok.Data;
        import org.springframework.boot.context.properties.ConfigurationProperties;
        import java.io.Serializable;
        @Data
        @ConfigurationProperties(prefix = "minio")  // 文件上传 配置前缀file.oss
        public class MinIOConfigProperties implements Serializable {
            private String accessKey;
            private String secretKey;
            private String bucket;
            private String endpoint;
            private String readPath;
        }

        image.gif

        MinIOConfig

        package com.my.file.config;
        import com.my.file.service.FileStorageService;
        import io.minio.MinioClient;
        import lombok.Data;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
        import org.springframework.boot.context.properties.EnableConfigurationProperties;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        @Data
        @Configuration
        @EnableConfigurationProperties({MinIOConfigProperties.class})
        //当引入FileStorageService接口时
        @ConditionalOnClass(FileStorageService.class)
        public class MinIOConfig {
            @Autowired
            private MinIOConfigProperties minIOConfigProperties;
            @Bean
            public MinioClient buildMinioClient() {
                return MinioClient
                        .builder()
                        .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                        .endpoint(minIOConfigProperties.getEndpoint())
                        .build();
            }
        }

        image.gif

        (3)封装操作MinIO类

        FileStorageService

        package com.my.file.service;
        import java.io.InputStream;
        /**
         * @author itheima
         */
        public interface FileStorageService {
            /**
             *  上传图片文件
             * @param prefix  文件前缀
             * @param filename  文件名
             * @param inputStream 文件流
             * @return  文件全路径
             */
            public String uploadImgFile(String prefix, String filename,InputStream inputStream);
            /**
             *  上传html文件
             * @param prefix  文件前缀
             * @param filename   文件名
             * @param inputStream  文件流
             * @return  文件全路径
             */
            public String uploadHtmlFile(String prefix, String filename,InputStream inputStream);
            /**
             * 删除文件
             * @param pathUrl  文件全路径
             */
            public void delete(String pathUrl);
            /**
             * 下载文件
             * @param pathUrl  文件全路径
             * @return
             *
             */
            public byte[]  downLoadFile(String pathUrl);
        }

        image.gif

        MinIOFileStorageService

        package com.my.file.service.impl;
        import com.my.file.config.MinIOConfig;
        import com.my.file.config.MinIOConfigProperties;
        import com.my.file.service.FileStorageService;
        import io.minio.GetObjectArgs;
        import io.minio.MinioClient;
        import io.minio.PutObjectArgs;
        import io.minio.RemoveObjectArgs;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.context.properties.EnableConfigurationProperties;
        import org.springframework.context.annotation.Import;
        import org.springframework.stereotype.Service;
        import org.springframework.util.StringUtils;
        import java.io.ByteArrayOutputStream;
        import java.io.IOException;
        import java.io.InputStream;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        @Slf4j
        @EnableConfigurationProperties(MinIOConfigProperties.class)
        @Import(MinIOConfig.class)
        @Service
        public class MinIOFileStorageService implements FileStorageService {
            @Autowired
            private MinioClient minioClient;
            @Autowired
            private MinIOConfigProperties minIOConfigProperties;
            private final static String separator = "/";
            /**
             * @param dirPath
             * @param filename  yyyy/mm/dd/file.jpg
             * @return
             */
            public String builderFilePath(String dirPath,String filename) {
                StringBuilder stringBuilder = new StringBuilder(50);
                if(!StringUtils.isEmpty(dirPath)){
                    stringBuilder.append(dirPath).append(separator);
                }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                String todayStr = sdf.format(new Date());
                stringBuilder.append(todayStr).append(separator);
                stringBuilder.append(filename);
                return stringBuilder.toString();
            }
            /**
             *  上传图片文件
             * @param prefix  文件前缀
             * @param filename  文件名
             * @param inputStream 文件流
             * @return  文件全路径
             */
            @Override
            public String uploadImgFile(String prefix, String filename,InputStream inputStream) {
                String filePath = builderFilePath(prefix, filename);
                try {
                    PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                            .object(filePath)
                            .contentType("image/jpg")
                            .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                            .build();
                    minioClient.putObject(putObjectArgs);
                    StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
                    urlPath.append(separator+minIOConfigProperties.getBucket());
                    urlPath.append(separator);
                    urlPath.append(filePath);
                    return urlPath.toString();
                }catch (Exception ex){
                    log.error("minio put file error.",ex);
                    throw new RuntimeException("上传文件失败");
                }
            }
            /**
             *  上传html文件
             * @param prefix  文件前缀
             * @param filename   文件名
             * @param inputStream  文件流
             * @return  文件全路径
             */
            @Override
            public String uploadHtmlFile(String prefix, String filename,InputStream inputStream) {
                String filePath = builderFilePath(prefix, filename);
                try {
                    PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                            .object(filePath)
                            .contentType("text/html")
                            .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1)
                            .build();
                    minioClient.putObject(putObjectArgs);
                    StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
                    urlPath.append(separator+minIOConfigProperties.getBucket());
                    urlPath.append(separator);
                    urlPath.append(filePath);
                    return urlPath.toString();
                }catch (Exception ex){
                    log.error("minio put file error.",ex);
                    ex.printStackTrace();
                    throw new RuntimeException("上传文件失败");
                }
            }
            /**
             * 删除文件
             * @param pathUrl  文件全路径
             */
            @Override
            public void delete(String pathUrl) {
                String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
                int index = key.indexOf(separator);
                String bucket = key.substring(0,index);
                String filePath = key.substring(index+1);
                // 删除Objects
                RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
                try {
                    minioClient.removeObject(removeObjectArgs);
                } catch (Exception e) {
                    log.error("minio remove file error.  pathUrl:{}",pathUrl);
                    e.printStackTrace();
                }
            }
            /**
             * 下载文件
             * @param pathUrl  文件全路径
             * @return  文件流
             *
             */
            @Override
            public byte[] downLoadFile(String pathUrl)  {
                String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/","");
                int index = key.indexOf(separator);
                String bucket = key.substring(0,index);
                String filePath = key.substring(index+1);
                InputStream inputStream = null;
                try {
                    inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
                } catch (Exception e) {
                    log.error("minio down file error.  pathUrl:{}",pathUrl);
                    e.printStackTrace();
                }
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buff = new byte[100];
                int rc = 0;
                while (true) {
                    try {
                        if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    byteArrayOutputStream.write(buff, 0, rc);
                }
                return byteArrayOutputStream.toByteArray();
            }
        }

        image.gif

        (4)对外加入自动配置

        在resources中新建META-INF/spring.factories  

        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
          com.my.file.service.impl.MinIOFileStorageService

        image.gif


        相关文章
        |
        21天前
        |
        Dubbo Java 应用服务中间件
        Spring Cloud Dubbo:微服务通信的高效解决方案
        【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
        45 2
        |
        2月前
        |
        Java 对象存储 开发者
        解析Spring Cloud与Netflix OSS:微服务架构中的左右手如何协同作战
        Spring Cloud与Netflix OSS不仅是现代微服务架构中不可或缺的一部分,它们还通过不断的技术创新和社区贡献推动了整个行业的发展。无论是对于初创企业还是大型组织来说,掌握并合理运用这两套工具,都能极大地提升软件系统的灵活性、可扩展性以及整体性能。随着云计算和容器化技术的进一步普及,Spring Cloud与Netflix OSS将继续引领微服务技术的发展潮流。
        49 0
        |
        24天前
        |
        Dubbo Java 应用服务中间件
        Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
        尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
        |
        21天前
        |
        JSON Java 数据格式
        【微服务】SpringCloud之Feign远程调用
        本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
        49 1
        |
        24天前
        |
        人工智能 文字识别 Java
        SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
        尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
        SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
        |
        1月前
        |
        监控 Java 对象存储
        监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
        监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
        41 1
        |
        2月前
        |
        负载均衡 Java 网络架构
        实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
        实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
        91 5
        |
        2月前
        |
        前端开发 API 微服务
        SpringCloud微服务之间使用Feign调用不通情况举例
        SpringCloud微服务之间使用Feign调用不通情况举例
        447 2
        |
        1月前
        |
        负载均衡 算法 Nacos
        SpringCloud 微服务nacos和eureka
        SpringCloud 微服务nacos和eureka
        55 0
        |
        2月前
        |
        Java 对象存储 开发者
        微服务世界的双雄争霸:Spring Cloud与Netflix OSS——谁将引领下一次企业级应用变革的风暴?
        Spring Cloud与Netflix OSS是微服务架构的核心组件集,分别以其与Spring Boot的紧密集成及为大规模分布式系统设计的特性,在Java开发社区中广受青睐。前者通过Eureka提供服务发现机制,简化服务注册与定位;后者借助Hystrix增强系统弹性和可靠性,避免雪崩效应。此外,二者还包含负载均衡(Ribbon)、声明式HTTP客户端(Feign)及API网关(Zuul)等功能,共同构建强大微服务体系,助力开发者聚焦业务逻辑,提升系统灵活性与性能。
        44 0
        下一篇
        无影云桌面