关于java:Ceph分布式存储实践应用之Ceph-Swift生产实践运用

47次阅读

共计 7172 个字符,预计需要花费 18 分钟才能阅读完成。

1. Ceph 封装与自动化拆卸

  1. 创立 ceph-starter 自动化工程:

     创立一个 spring boot 工程,作为一个专用组件。
  2. pom 文件依赖:

    <dependencies>
            <!-- Spring Boot 自定义启动器的依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-actuator-autoconfigure</artifactId>
            </dependency>
            <!-- ceph 依赖 -->
            <dependency>
                <groupId>com.ceph</groupId>
                <artifactId>rados</artifactId>
                <version>0.6.0</version>
            </dependency>
    
            <!-- ceph fs 操作依赖 -->
            <dependency>
                <groupId>com.ceph</groupId>
                <artifactId>libcephfs</artifactId>
                <version>0.80.5</version>
            </dependency>
    
            <!-- ceph swift 依赖 -->
            <dependency>
                <groupId>org.javaswift</groupId>
                <artifactId>joss</artifactId>
                <version>0.10.2</version>
            </dependency>
        </dependencies>
    

    间接采纳目前的最新版,退出 Ceph 相干的三个依赖。

  3. 代码实现

    封装 Ceph 操作接口,CephSwiftOperator 类:

    public class CephSwiftOperator {private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    
        /**
         * 用户名
         */
        private String username;   
        /**
         * 明码
         */
        private String password;
    
        /**
         * 认证接入地址
         */
        private String authUrl;
    
        /**
         * 默认容器名称
         */
        private String defaultContainerName;
    
        /**
         * Ceph 账户对象
         */
        private Account account;
    
        /**
         * Ceph 容器对象
         */
        private Container container;   
    
        public CephSwiftOperator(String username, String password, String authUrl, String defaultContainerName) {
            // 初始化配置信息
            this.username = username;
            this.password = password;
            this.authUrl = authUrl;
            this.defaultContainerName = defaultContainerName;
            init();}
    
        /**
         * 初始化建设连贯
         */
        public void init() {
            try {
                // Ceph 用户认证配置
                AccountConfig config = new AccountConfig();
                config.setUsername(username);
                config.setPassword(password);
                config.setAuthUrl(authUrl);
                config.setAuthenticationMethod(AuthenticationMethod.BASIC);
                account = new AccountFactory(config).createAccount();
                // 获取容器
                Container newContainer = account.getContainer(defaultContainerName);
                if (!newContainer.exists()) {container = newContainer.create();
                    log.info("account container create ==>" + defaultContainerName);
                } else {
                    container = newContainer;
                    log.info("account container exists!  ==>" + defaultContainerName);
                }
            }catch(Exception e) {
                // 做异样捕捉, 防止服务不能失常启动
                log.error("Ceph 连贯初始化异样:" + e.getMessage());
            }
        }   
    
        /**
         * 上传对象
         * @param remoteName
         * @param filepath
         */
        public void createObject(String remoteName, String filepath) {StoredObject object = container.getObject(remoteName);
            object.uploadObject(new File(filepath));
        }
    
        /**
         * 上传文件对象(字节数组模式)* @param remoteName
         * @param inputStream
         */
        public void createObject(String remoteName, byte[] inputStream) {StoredObject object = container.getObject(remoteName);
            object.uploadObject(inputStream);
        }
    
        /**
         * 获取指定对象
         * @param containerName
         * @param objectName
         * @param outpath
         */
        public void  retrieveObject(String objectName,String outpath){StoredObject object = container.getObject(objectName);
            object.downloadObject(new File(outpath));
        }
       /**
         * 下载文件,转为文件流模式
         * @param objectName
         * @return
         */
        public InputStream retrieveObject(String objectName){StoredObject object = container.getObject(objectName);
            return object.downloadObjectAsInputStream();}
       
        /**
         * 删除指定文件对象
         * @param containerName
         * @param objectName
         * @return
         */
        public boolean deleteObject(String objectName){
            try {StoredObject object = container.getObject(objectName);
                object.delete();
                return !object.exists();}catch(Exception e) {log.error("Ceph 删除文件失败:" + e.getMessage());
            }
            return false;
        }
    
        /**
         * 获取所有容器
         * @return
         */
        public List listContainer() {List list = new ArrayList();
            Collection<Container> containers = account.list();
            for (Container currentContainer : containers) {list.add(currentContainer.getName());
                System.out.println(currentContainer.getName());
    
            }
            return list;
        }   
    }   
    • 此封装接口将纳入 Spring 容器治理,即为单例,构造函数会初始化 Ceph 认证连贯等信息
    • 将 Account 与 Container 设为成员变量,便于复用,缩小开销。
    • 初始化会默认一个容器名称,个别每个服务设置一个容器名称,如果业务性能比拟庞杂,能够每个业务模块设置一个容器。
    • Swift Api 曾经做了较欠缺的封装,咱们外部应用比较简单,次要封装上传和下载接口,为便于调用解决,做了进一步封装。

    AutoCephSwiftConfiguration 自动化配置类:

    @Configuration
    @EnableAutoConfiguration
    @ConditionalOnProperty(name = "ceph.authUrl")
    public class AutoCephSwiftConfiguration {@Value("${ceph.username}")
        private String username;
        @Value("${ceph.password}")
        private String password;
        @Value("${ceph.authUrl}")
        private String authUrl;
        @Value("${ceph.defaultContainerName}")
        private String defaultContainerName;  
        @Bean
        public CephSwiftOperator cephSwiftOperator() {return new CephSwiftOperator(username, password, authUrl, defaultContainerName);
        }
    
    }
    

    ConditionalOnProperty 依据 ceph.authUrl 属性来决定是否加载配置,如果配置文件中没有设置 Ceph 相干属性,即便 maven 中援用,启动也不会报错。该自动化配置,负责初始化一个 Ceph Swift 接口操作实例。

  4. 自动化配置:

    要让自定义 Ceph Starter 真正失效,必须遵循 Spring boot 的 SPI 扩大机制,在 resources 环境中, META-INF 目录下,创立 spring.factories 文件:

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.ceph.starter.AutoCephSwiftConfiguration

    指定咱们下面所写的自动化配置类。

2. 创立用户治理工程

  1. 工程配置
    application.yml

    server:
      port: 10692
    spring:
      application:
        name: user-manager
      # 模板配置
      thymeleaf:
        prefix: classpath:/templates/
        suffix: .html
        mode: HTML
        encoding: utf-8
        servlet:
          content-type: text/html
    
      # 文件上传大小限度
      servlet:
        multipart:
          max-file-size: 100MB
          max-request-size: 100MB
    
    # ceph swift 认证信息配置
    ceph:
      username: cephtester:subtester
      password: 654321
      authUrl: http://10.10.20.11:7480/auth/1.0
      defaultContainerName: user_datainfo
  2. POM 依赖配置:

       <dependencies>
           <!-- spring boot 依赖 -->
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-web</artifactId>
           </dependency>   
           <!-- Spring boot thymeleaf 模板依赖 -->
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-thymeleaf</artifactId>
           </dependency>   
           <!-- Ceph 自动化封装组件 -->
           <dependency>
               <groupId>com.itcast.ceph</groupId>
               <artifactId>ceph-starter</artifactId>
               <version>1.0-SNAPSHOT</version>
           </dependency>   
       </dependencies>

    退出 Ceph 自动化封装组件配置依赖。

3. Ceph 文件上传实现

  1. 实现文件上传接口:

    /**
     * 上传用户文件
     * @return
     */
    public String uploadUserFile(MultipartFile file) throws Exception {
    
        // 获取惟一文件 ID 标识
        String remoteFileId = globalIDGenerator.nextStrId();
    
        // 上传文件至 CEPH
        cephSwiftOperator.createObject(remoteFileId, file.getBytes());
    
        return remoteFileId;
    }
  2. Controller 层实现:

    在 UserManagerController 上面,减少上传接口:

    /**
     * 用户文件上传
     * @param file
     * @return
     */
    @PostMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file) {
        String  result = null;
        try {
            // 通过 Ceph Swift 上传文件
            String userFileId = userManagerService.uploadUserFile(file);
            result = "上传的文件 ID:" + userFileId;
        }catch(Exception e) {log.error(e.getMessage(), e);
            result = "出现异常:" + e.getMessage();}
        return result;
    }   

    通过 Spring boot 实现文件上传,留神以表单 form 模式提交,类型为 multipart/form-data,参数名为 file,非间接数据流形式上传,不然服务不能失常辨认接管文件。

4. Ceph 文件下载实现

新增一个接口,依据上传的文件 ID 标识下载文件。

  1. Service 层:

    实现下载用户文件接口:

    /**
     * 下载用户文件
     * @param fileId
     * @return
     * @throws Exception
     */
    public InputStream downloadUserFile(String fileId) throws Exception {return cephSwiftOperator.retrieveObject(fileId);
    }
  2. Controller 层:

    /**
     * 依据文件 ID 下载用户文件信息
     * @param filename
     * @return
     */
    @RequestMapping(value = "/download")
    public String downloadFile(@NotBlank(message = "文件 ID 不能为空!") String filename, HttpServletResponse response){   
        String result = null;   
        // 文件流缓存
        BufferedInputStream bis = null;
        // 文件输入流
        OutputStream os = null;
        try {
            // 1. 从 Ceph 服务器上获取文件流
            InputStream inputStream = userManagerService.downloadUserFile(filename);
            // 2. 设置强制下载, 不间接关上
            response.setContentType("application/x-msdownload");
            // 3. 设置下载的文件名称
            response.addHeader("Content-disposition", "attachment; fileName=" + filename);
            // 4. 输入文件流
            byte[] buffer = new byte[1024];
            bis = new BufferedInputStream(inputStream);
            os = response.getOutputStream();
            int i = bis.read(buffer);
            while(i != -1) {os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
            os.flush();
            return null;
        }catch(Exception e) {log.error(e.getMessage(), e);
            result = "出现异常:" + e.getMessage();}finally {
            // 最初, 要记住敞开文件流
            if(bis != null) {
                try {bis.close();
                } catch (IOException e) {log.error(e.getMessage(), e);
                }
            }
        }   
        return result;
    
    }

5. 性能验证

  1. 拜访上传页面

    地址:http://127.0.0.1:10692/user/file

     ![file](/img/bVcQ8Xw)
    
  2. 上传胜利后,会返回文件 ID:

     ![file](/img/bVcQ8Xx)
    
  3. 下载文件:

    输出文件 ID 进行下载:

     ![file](/img/bVcQ8Xy)
    

本文由 mirson 创作分享,如需进一步交换,请加 QQ 群:19310171 或拜访 www.softart.cn

正文完
 0