分布式文件系统:FastDFS

本文详细介绍了分布式文件系统FastDFS的原理、优缺点及其与通用和专用分布式文件系统的对比。FastDFS是一个轻量级开源分布式文件系统,适用于大中型网站存储资源文件。文中还阐述了FastDFS的架构、环境搭建步骤、文件上传流程,并提供了相关配置和代码示例。此外,针对FastDFS不支持图片访问的问题,提出了利用nginx作为代理实现HTTP访问的解决方案。
网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容
如果右边没有就找找左边
分布式文件系统分类
  1. 通用分布式文件系统

和传统的本地文件系统(ext3,NTFS等)相对应。典型代表Iustre、MooseFS

  1. 优点:表中文件系统操作方式,对开发者门槛较低
  2. 缺点:系统复杂性较高,需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等。复杂性更高,系统整体性能有所降低,因为要支持POSIX标准(表示可移植操作系统接口,POSIX标准定义了操作系统应该为应用程序提供的接口标准)
  1. 专用分布式文件系统

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

  1. 优点:系统复杂性较低,不需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等,系统比较简洁。系统整体性能较高,因为无需支持POSIX标准,可以省去支持POSIX引入的环节,系统更加高效
  2. 缺点:采用专用API,对开发者门槛较高(直接封装成工具类)
Google FS 体系结构
  1. 两个角色
  1. 名字服务器(索引服务器)
  2. 存储服务器
  1. 架构特点
  1. 不支持文件修改功能
  2. 文件分块存储,需要索引服务器
  3. 一个文件可以存储多份,一个文件存储到哪些存储服务器,通常采用动态分配的方式
FastDFS简介
  1. FastDFS是一个轻量级的开源分布式文件系统。2008年4月份开始启动。类似google FS的一个轻量级分布式文件系统,存C语言实现,支持Linux、FreeBSD、AIX等UNIX系统
  2. 主要解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。实现了软件方式的磁盘阵列(RAID),可以使用廉价IDE硬盘进行存储。并且支持存储服务器在线扩容。支持相同内容的文件只保存一份,节约磁盘空间。
  3. FastDFS只能通过ClientAPI访问,不支持POSIX访问方式。
  4. FastDFS特别适合大中型网站使用,用来存储资源文件(如:图片、文档、音频、视频等等)
FastDFS文档和下载地址
  1. FastDFS没有官网,但是作者happy_fish100余庆担任chinaunix中FastDFS板块版主。并且会不定期更新板块中内容。http://bbs.chinaunix.net
    在这里插入图片描述
  2. FastDFS软件可以在sourceforge中下载https://sourceforge.net/projects/fastdfs/files/(不推荐使用这种下载方式,具体下载请看下面环境搭建内容)
    在这里插入图片描述在这里插入图片描述
FastDFS架构

在这里插入图片描述

  1. Client:客户端。使用java语言编写的项目属于客户端
  2. Tracker Server:跟踪服务器,主要做调度工作,在访问上起均衡负载的作用。在内存中记录集群中group和storage server的状态信息,是连接Client和Storage server的枢纽
  3. Storage Server:存储服务器,文件和文件属性(meta data)都保存到存储服务器上,按组group存储,就是上图白色的框框,有若干组,不同组之间不会互相通信
  1. 架构解读
  1. 只有两个角色,tracker server和storage server,不需要存储文件索引信息。
  2. 所有服务器都是对等的,不存在Master-Slave关系。
  3. 不同组中storage server之间不会相信通信。
  4. 由storage server主动向tracker server报告状态,tracker server之间不会互相通信。

一、环境搭建

先下载好相关资源
  1. libfastcommon 下载地址: https://github.com/happyfish100/libfastcommon/releases
  1. libfastcommon是FastDFS官方提供的,libfastcommon包含了FastDFS运行所需要的一些c语言基础库
    在这里插入图片描述
  1. FastDFS 下载地址 https://github.com/happyfish100/fastdfs/releases

注意:请大家下载6.4以上版本,推荐6.4,因为和nginx做整合时,低版本会发生冲突问题
在这里插入图片描述

开始搭建环境
  1. 打开Linux,安装c语言环境,不会Linux请参考文章https://blog.youkuaiyun.com/grd_java/article/details/115676099

FastDFS 是C语言开发的应用。安装必须使用make,cmake和gcc编译器
在这里插入图片描述

//安装
yum install -y make cmake gcc gcc-c++
yum -y install libevent
  1. 安装libfastcommon
  1. 将两个包传过去
    在这里插入图片描述
  2. 解压
    在这里插入图片描述
 tar -zxvf libfastcommon-1.0.49.tar.gz -C /opt/module/
  1. 编译
    在这里插入图片描述
  2. 执行安装(有固定的默认安装位置。在/usr/lib64和/usr/include/fastcommon两个目录中)
    在这里插入图片描述
  3. 创建软连接(因为FastDFS主程序设置的lib目录是/usr/local/lib所以需要创建软连接,就是快捷方式)
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
  1. 解压FastDFS
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  1. 文件解析
    在这里插入图片描述
  1. 配置tracker
  1. 复制配置文件(进入/etc/fdfs中,把tracker配置文件复制一份)
    在这里插入图片描述
cp tracker.conf.sample tracker.conf
  1. 创建数据目录(创建放置tracker数据的目录)
    在这里插入图片描述
mkdir -p /usr/local/fastdfs/tracker
  1. 修改配置文件(修改tracker.conf设置tracker内容存储目录)
    在这里插入图片描述
    在这里插入图片描述
  2. 启动服务(启动成功后,配置文件中指定的目录出现data目录和logs目录)
    在这里插入图片描述
service fdfs_trackerd start
  1. 查看服务运行状态
    在这里插入图片描述
service fdfs_trackerd status
  1. 配置storage(可以和tracker不在同一台服务器)
  1. 复制配置文件(进入/etc/fdfs,把storage配置文件复制一份)
[root@hadoop105 tracker]# cd /etc/fdfs/
[root@hadoop105 fdfs]# cp storage.conf.sample storage.conf
  1. 创建目录(创建两个,base用于存储基础数据和日志,store用于存储上传数据)
[root@hadoop105 fdfs]# mkdir -p /user/local/fastdfs/storage/base
[root@hadoop105 fdfs]# mkdir -p /user/local/fastdfs/storage/store
  1. 修改配置文件(storage.conf配置文件用于描述存储服务的行为,需要进行如下修改)
[root@hadoop105 fdfs]# vim /etc/fdfs/storage.conf
# 基础路径,用于保存storage server基础数据内容和日志内容的目录
base_path=/user/local/fastdfs/storage/base
#存储路径,用于保存FastDFS中存储文件的目录,就是要创建256*256个子目录位
store_path0=/user/local/fastdfs/storage/store
# tracker服务的ip和端口,注意ip写你自己虚拟机或服务器ip,22122是tracker的默认端口,可以在上面tracker.conf文件中配置
tracker_server=192.168.10.105:22122
  1. 启动(成功后,指定目录出现data和logs,path0指定目录,只出现data,第一次启动需要创建256*256子目录,会比较慢)
[root@hadoop105 fdfs]# service fdfs_storaged start
[root@hadoop105 fdfs]# service fdfs_storaged status

二、文件上传

流程说明
  1. 客户端访问Tracker
  2. Tracker返回Storage的ip和端口
  3. 客户端直接访问Storage,把文件内容和元数据发送过去
  4. Storage返回文件存储id,包含了组名和文件名
    在这里插入图片描述
  1. 创建maven项目添加依赖
    在这里插入图片描述
<dependencies>
    <!--这个依赖,作者没有写,所以很多人提供了很多版本,内容都一样,如果这个失效,请去maven仓库换一个fastdfs-client-java-->
    <dependency>
        <groupId>net.oschina.zcx7878</groupId>
        <artifactId>fastdfs-client-java</artifactId>
        <version>1.27.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
    </dependency>
</dependencies>
  1. 编写配置文件
    在这里插入图片描述
connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.10.105:22122
  1. 工具类
    在这里插入图片描述
import org.apache.commons.lang3.StringUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import java.io.*;

/**
 * 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 trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
            //创建trackerServer 
            TrackerServer trackerServer = trackerClient.getConnection();
            //获取storageServer 
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            //获取storageClient 
            storageClient = new StorageClient(trackerServer,storageServer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    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_appender_file(file_buff,getFileExt(fileName),meta_list);
            return fileids;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String[] uploadFile(File file,String fileName){
        FileInputStream fis = null;
        try {
            NameValuePair[] meta_list = null;
            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 e) {
            e.printStackTrace();
            return null;
        }finally {
            if(fis != null){
                try{
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 根据组名和远程文件名来删除一个文件
     * @param groupName
     * @param remoreFileName
     * @return
     */
    public static int deleteFile(String groupName,String remoreFileName){
        try {
            int result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoreFileName);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            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 e){
            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 e) {
            return null;
        }
    }

    public static NameValuePair[] getMetaDate(String groupName,String remoteFileName){
        try{
            NameValuePair[] nvp = storageClient.get_metadata(groupName, remoteFileName);
            return nvp;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取文件后缀名,不带点
     */
    private static String getFileExt(String fileName){
        if(StringUtils.isBlank(fileName)||!fileName.contains(".")){
            return "";
        }else{
            return fileName.substring(fileName.lastIndexOf(".")+1);//不带最后的点
        }
    }
}
  1. 编写客户端代码,上传一张图片测试(上传后的文件名,是FastDFS自动生成,和我们代码设置的文件名没啥关系)
    在这里插入图片描述
import com.yzpnb.utils.FastDFSClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.UUID;
public class MyClient {
    public static void main(String[] args) {
        try{
            File file = new File("D:/a.jpg");
            InputStream inputStream = new FileInputStream(file);
            String fileName = UUID.randomUUID().toString()+ ".jpg";

            String[] result = FastDFSClient.uploadFile(inputStream, fileName);

            System.out.println(Arrays.toString(result));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

三、下载

  1. 编写代码并运行
    在这里插入图片描述在这里插入图片描述
import com.yzpnb.utils.FastDFSClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.UUID;
public class MyClient {
    public static void main(String[] args) {
        try{
            //[group1, M00/00/00/wKgKaWENAOWEMeDCAAAAAOFj66Y127.jpg]下载这张图片
            InputStream is = FastDFSClient.downloadFile("group1", "M00/00/00/wKgKaWENAOWEMeDCAAAAAOFj66Y127.jpg");
            FileOutputStream os = new FileOutputStream(new File("D:/b.jpg"));
            int index = 0;
            while((index = is.read())!=-1){
                os.write(index);
            }
            os.flush();
            os.close();
            is.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

四、图片访问

问题
  1. FastDFS没有提供图片访问功能
  2. 所以我们需要使用代理来实现
  3. nginx提供的代理功能可以实现图片访问,但只支持http请求代理(只要支持http协议访问的内容,nginx都可以代理),当接受到外部http请求,返回本地文件资源给客户端
  4. 我们需要借助nginx来实现,外部请求图片,把图片信息响应给请求方
  1. 上传到虚拟机并安装:
  1. fastdfs-nginx-module模块 :https://github.com/happyfish100/fastdfs-nginx-module
    在这里插入图片描述
  2. nginx: http://nginx.org/en/download.html
    在这里插入图片描述
  3. 上传两个文件,解压
    在这里插入图片描述
    在这里插入图片描述
  1. 修改fastdfs-nginx-module配置文件,在文件中src目录下的config文件,因为配置文件中指定的是fastDFS核心代码位置,我们需要指定
    在这里插入图片描述在这里插入图片描述
/usr/include/fastdfs /usr/include/fastcommon/
  1. 安装nginx
  1. 安装一些c++之类的运行库
    在这里插入图片描述
 yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel
  1. 修改配置文件之前,创建一个目录(修改配置文件中好多位置都使用了/var/temp/nginx目录,我们需要手动创建这个目录)
    在这里插入图片描述
  2. 给nginx加权限
    在这里插入图片描述
  3. 进入nginx目录,添加配置(你需要修改最后一行,指定路径到你安装fastdfs-nginx-module的src路径)
    在这里插入图片描述
./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/local/nginx/nginx.pid \
--lock-path=/var/lock/nginx/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi \
--add-module=/opt/module/fastdfs-nginx-module-master/src
  1. 编译并安装(在nginx目录下,执行make命令编译安装,make install运行)
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
  1. 复制fastdfs-nginx-module/src/mod-fastdfs.conf配置文件到/etc/fdfs目录,并修改它
    在这里插入图片描述
cp /opt/module/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/

在这里插入图片描述
在这里插入图片描述

connect_timeout=10 # 连接超时时间,单位秒
tracker_server=192.168.10.105:22122# tracker服务结点
url_have_group_name = true# URL是否包含group名称
store_path0=/user/loacl/fastdfs/storage/store#storage服务结点的存储位置,配置与storage结点一致
  1. 提供FastDFS需要的HTTP配置文件(复制FastDFS安装包中两个配置文件http.conf和mine.types到/etc/fdfs目录中)
    在这里插入图片描述
  1. 创建网络访问存储服务的软连接(在上传文件到FastDFS后,FastDFS会返回group1/M00/00/00/xxxxx/xx其中group1是卷名,在mod_fastdfs.conf配置文件中已配置了url_have_group_name,以保证URL解析正确。其中的M00是FastDFS保存数据时使用的虚拟目录,需要将这个虚拟目录定位到真实数据目录上)
    在这里插入图片描述
ln -s /user/local/fastdfs/storage/store/data/ /user/local/fastdfs/storage/store/data/M00
  1. 修改nginx配置文件
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 启动nginx
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

到此,已经可以通过http请求获取资源了

在这里插入图片描述

使用vue工程测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ydenergy_殷志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值