java笔记:分布式文件系统+FastDFS+Nginx

java笔记:分布式文件系统+FastDFS+Nginx

分布式文件系统概述

什么是文件系统?

众所周知, 数据是存储在磁盘上的,但是磁盘只是提供了一个空间,那么文件系统就是管理磁盘空间的软件,同时还给用户提供人性化的操作接口.也就是说,文件系统解决了用户使用磁盘存储数据的问题.

什么是分布式文件系统?

分布式文件系统是相对本地文件系统而言的,都是用于用户操作存储空间的软件,但本地文件系统是在本地操作本地存储空间,而分布式文件系统是允许文件在多台主机上分享,让多台主机上的多用户分享和操作存储空间的文件系统软件

为什么需要分布式文件系统

在这里插入图片描述

过往我们采用的上面的方式进行图片存储。图片在哪个项目中上传的图片就存储到哪个项目所在的服务器。其他项目模块通过HTTP请求进行获取图片,这种项目架构有如下问题:

1.图片存储过于分散。

2.图片多的服务器压力比较大,可能会影响其他功能。

3.存储到项目路径中,重启会丢失。存储到外部文件中,I/O操作性能低。

针对上面问题可以搭建单独的图片服务器,专门做图片存储及图片访问的。而想要搭建文件服务器就需要应用文件存储技术/工具。
在这里插入图片描述

专用分布式文件系统(GFS)

基于google File System的思想,文件上传后不能修改。需要使用专有API对文件进行访问,也可称作分布式文件存储服务。典型代表:MogileFS、FastDFS、TFS、HDFS

GFS结构
两个角色

1.名字服务器(索引服务器)

2.存储服务器

架构特点

1.不支持文件修改功能。

2.文件分块存储,需要索引服务器

3.一个文件可以存储多份,一个文件存储到哪些存储服务器,通常采用动态分配的方式。

FastDFS

FastDFS是一个轻量级的开源分布式文件系统。主要解决了海量文件存储和高并发访问的问题,文件存取时实现了负载均衡。

适用:以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。

角色

1.Client:客户端。使用java语言编写的项目属于客户端。

2.Storage Server:存储服务器,文件和文件属性(meta data)都保存到存储服务器上

3.Tracker Server:跟踪服务器,主要做调度工作,在访问上起负载均衡的作用。在内存中记录集群中group和storage server的状态信息,是连接Client和Storage server的枢纽。

img

架构特点

1.轻量级(相比GFS简化了master角色,不再管理meta数据信息)。只有两个角色,tracker server和storage server,不需要存储文件索引信息。

2.所有服务器都是对等的,不存在Master-Slave关系。

3.存储服务器采用分组方式,同组内存储服务器上的文件完全相同(RAID 1)。不同组的storage server之间不会相互通信。

4.由storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。

文件上传流程

时序图

在这里插入图片描述

流程说明

1.客户端访问Tracker

2.Tracker 返回Storage的ip和端口

3.客户端直接访问Storage,把文件内容和元数据发送过去。

4.Storage返回文件存储id。包含了组名和文件名
在这里插入图片描述

文件下载流程

时序图

在这里插入图片描述

流程说明

1.client询问tracker下载文件的storage,参数为文件标识(组名和文件名);

2.tracker返回一台可用的storage;

3.client直接和storage通讯完成文件下载。

上传下载代码示例

依赖

<dependencies>
        <dependency>
            <groupId>cn.bestwu</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

工具类

package com.ppl.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.lang3.StringUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
/**
 * FastDFS分布式文件系统操作客户端.
 */
public class FastDFSClient {
    private static final String CONF_FILENAME = Thread.currentThread().getContextClassLoader().getResource("").getPath() + "fdfs_client.conf";

    private static StorageClient storageClient = null;
    static {
        try {
            ClientGlobal.init(CONF_FILENAME);
            TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
            TrackerServer trackerServer = trackerClient.getConnection();
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            storageClient = new StorageClient(trackerServer, storageServer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     *
     * @param inputStream
     *    上传的文件输入流
     * @param fileName
     *    上传的文件原始名
     * @return
     */
    public static String[] uploadFile(InputStream inputStream, String fileName) {
        try {
            // 文件的元数据
            NameValuePair[] meta_list = new NameValuePair[2];
            // 第一组元数据,文件的原始名称
            meta_list[0] = new NameValuePair("file name", fileName);
            // 第二组元数据
            meta_list[1] = new NameValuePair("file length", inputStream.available()+"");
            // 准备字节数组
            byte[] file_buff = null;
            if (inputStream != null) {
                // 查看文件的长度
                int len = inputStream.available();
                // 创建对应长度的字节数组
                file_buff = new byte[len];
                // 将输入流中的字节内容,读到字节数组中。
                inputStream.read(file_buff);
            }
            // 上传文件。参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据
            String[] fileids = storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);
            return fileids;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**
     *
     * @param file
     *            文件
     * @param fileName
     *            文件名
     * @return 返回Null则为失败
     */
    public static String[] uploadFile(File file, String fileName) {
        FileInputStream fis = null;
        try {
            NameValuePair[] meta_list = null; // new NameValuePair[0];
            fis = new FileInputStream(file);
            byte[] file_buff = null;
            if (fis != null) {
                int len = fis.available();
                file_buff = new byte[len];
                fis.read(file_buff);
            }

            String[] fileids = storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);
            return fileids;
        } catch (Exception ex) {
            return null;
        }finally{
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据组名和远程文件名来删除一个文件
     *
     * @param groupName
     *            例如 "group1" 如果不指定该值,默认为group1
     * @param remoteFileName
     *            例如"M00/00/00/wKgxgk5HbLvfP86RAAAAChd9X1Y736.jpg"
     * @return 0为成功,非0为失败,具体为错误代码
     */
    public static int deleteFile(String groupName, String remoteFileName) {
        try {
            int result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);
            return result;
        } catch (Exception ex) {
            return 0;
        }
    }

    /**
     * 修改一个已经存在的文件
     *
     * @param oldGroupName
     *            旧的组名
     * @param oldFileName
     *            旧的文件名
     * @param file
     *            新文件
     * @param fileName
     *            新文件名
     * @return 返回空则为失败
     */
    public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {
        String[] fileids = null;
        try {
            // 先上传
            fileids = uploadFile(file, fileName);
            if (fileids == null) {
                return null;
            }
            // 再删除
            int delResult = deleteFile(oldGroupName, oldFileName);
            if (delResult != 0) {
                return null;
            }
        } catch (Exception ex) {
            return null;
        }
        return fileids;
    }

    /**
     * 文件下载
     *
     * @param groupName 卷名
     * @param remoteFileName 文件名
     * @return 返回一个流
     */
    public static InputStream downloadFile(String groupName, String remoteFileName) {
        try {
            byte[] bytes = storageClient.download_file(groupName, remoteFileName);
            InputStream inputStream = new ByteArrayInputStream(bytes);
            return inputStream;
        } catch (Exception ex) {
            return null;
        }
    }

    public static NameValuePair[] getMetaDate(String groupName, String remoteFileName){
        try{
            NameValuePair[] nvp = storageClient.get_metadata(groupName, remoteFileName);
            return nvp;
        }catch(Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * 获取文件后缀名(不带点).
     *
     * @return 如:"jpg" or "".
     */
    private static String getFileExt(String fileName) {
        if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
            return "";
        } else {
            return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
        }
    }
}


测试类

package com.ppl;

import com.ppl.utils.FastDFSClient;
import org.junit.Test;

import java.io.*;
import java.util.Arrays;
import java.util.UUID;

public class MyTest {
    @Test
    public void upload(){
        try {
            File file = new File("D:/a.jpg" );
            InputStream is = new FileInputStream(file);
            String fileName = UUID.randomUUID().toString()+".png";
            String[] result = FastDFSClient.uploadFile(is, fileName);
            System.out.println(Arrays.toString(result));//结果输出:[group1, M00/00/00/wKiGZF8Lh9aALWxEAAAYFk1wNbw269.png]
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void download(){
        try {
            InputStream is = FastDFSClient.downloadFile("group1", "M00/00/00/wKiGZF8Lh9aALWxEAAAYFk1wNbw269.png");
            OutputStream os = new FileOutputStream(new File("D:/b.png"));
            int index = 0 ;
            while((index = is.read())!=-1){
                os.write(index);
            }
            os.flush();
            os.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Nginx+FastDFS-Nginx-module

Nginx (engine x) 是一个高性能的反向代理服务,能实现HTTP访问FastDFS.Nginx可以监听所安装的主机的某个端口,对外支持这个端口的HTTP访问。当接收到外部HTTP请求后把本机中资源返回给客户端。

FastDFS-Nginx-module模块是同组存储服务器之间的数据复制同步完成之前,为了保证用户访问不出错误,通过重定向的方式保证能够避免复制延迟的模块.

代理方式

正向代理

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

在这里插入图片描述

反向代理

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

在这里插入图片描述

区别总结

正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端

反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分布式文件系统架构说明 - fastdfs-client(FastDFS 客户端) fastdfs提供的java客户端api,java相关功能都基于这个基础上封装,扩展,第三方应用不需要关心该接口. - fastdfs-core(HTTP服务器) 基于spring boot实现,提供http接口服务. 提供http服务器信息获取,http上传,http下载,删除上报,该服务会记录文件的基本信息,其中服务器信息获取,上传上报都由fastdfs-app自动完成,第三方应用不需要关心. - fastdfs-app(Apply SDK) 初始化 APIConfigure config = new APIConfigure("appKey", "httpServerUrl"); DFSAppClient.instance().initAPIConfigure(config); 实现执行初化操作,从fastdfs-core获取trackers服务器信息,及appKey对应的groupName, 这些动作都由SDK自动完成,第三方应用不需要关心. 上传文件 String fileId = DFSAppClient.instance().uploadFile(new File("文件绝对路径")); fileId:返回的fileId字符串,示例:group1/M00/00/00/wKgABFuOVJyEPGKEAAAAADUuUeE339.png fileId是后续对文件进行操作的基本参数,第三方应用拿到该值后应本地做好保存. 下载文件 FileOutputStream fos = new FileOutputStream(new File("文件绝对路径")); DFSAppClient.instance().downloadFile(fileId, fos, true); fileId:上传文件成功后返回的fileId字符串. 删除文件 int result = DFSAppClient.instance().deleteFile(fileId); fileId:上传文件成功后返回的fileId字符串. result:该方法会返回0表示删除成功,其他表示失败. fastdfs 下载示例说明 http://127.0.0.1:8808/dfs/v1/download?fileId=group1/M00/00/00/wKgABFuQ2PWEbNsOAAAAADUuUeE667.png&direct=true fileId:上传文件成功后返回的fileId字符串. direct:表示是否直接显示,非直接显示会提示下载,默认是非直接显示.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值