FastDFS文件系统单机环境搭建并使用

FastDFS文件系统单机环境搭建


项目中使用,又把原来忘记的东西重新捡起来,整理一份

FastDFS基本介绍

FastDFS 系统三个角色

Tracker Server


Tracker Server: 跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 storage server
和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。

Storage Server


Storage Server:存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。

Client

Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。



fastDFS优点:1.高可靠性:无单点故障 2.高吞吐性:只要 Group 足够多,数据流量将足够分散。

搭建fastDFS(阿里服务器)

安装 libfastcommon 和 FastDFS

一、安装 libfastcommon


这里是通过wget下载:

wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz

解压 libfastcommon,命令:

tar -zxvf V1.0.7.tar.gz

移动解压目录到/usr/local下:

mv libfastcommon-1.0.7 /usr/local/

进入libfastcommon-1.0.7目录,命令:

cd /usr/local/libfastcommon-1.0.7

编译

./make.sh

安装:

./make.sh install

安装 libfastcommon 成功:


#### 二、安装 FastDFS ---- 这里也是通过wget下载: ```java wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz ``` 解压 FastDFS: ```java tar -zxvf V5.05.tar.gz ``` 移动解压目录到/usr/local下: ```java mv fastdfs-5.05/ /usr/local/ ``` 进入fastfds-5.05目录,命令: ```java cd /usr/local/fastdfs-5.05 ``` 编译: ```java ./make.sh ``` 安装: ```java ./make.sh install ``` 安装成功: ![](https://img2020.cnblogs.com/blog/2026387/202012/2026387-20201209155332130-1517220266.jpg)

配置

配置 Tracker 服务


上述安装成功后,在/etc/目录下会有一个fdfs的目录,进入它。会看到三个.sample后缀的文件,这是作者给我们的示例文件,我们需要把其中的tracker.conf.sample文件改为tracker.conf配置文件并修改它。参照以下命令:

cd /etc/fdfs/
cp tracker.conf.sample tracker.conf
vim tracker.conf
// 编辑tracker.conf文件,找到你需要修改的两个参数就可以了,我这里不做修改,用默认的
# the base path to store data and log files // 数据和日志的存放目录
base_path=/home/yuqing/fastdfs

# HTTP port on this tracker server // http服务端口
http.server_port=8070

修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录)

mkdir -p /home/yuqing/fastdfs

启动tracker服务:

/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start

查看监听:

ps -ef|grep fdfs 
#或
netstat -lnpt|grep fdfs

Tracker服务安装成功,启动成功:


配置 Storage 服务


现在开始配置 Storage 服务,由于我这是单机器测试,你把 Storage 服务放在多台服务器也是可以的,它有 Group(组)的概念,同一组内服务器互备同步,这里不再演示。直接开始配置,依然是进入/etc/fdfs的目录操作,首先进入它。会看到三个.sample后缀的文件,我们需要把其中的storage.conf.sample文件改为storage.conf配置文件并修改它。参照以下命令:

cd /etc/fdfs/
cp storage.conf.sample storage.conf
vim storage.conf

打开storage.conf文件后,找到下面参数进行修改:

# the base path to store data and log files // 数据和日志的存放目录
base_path=/home/yuqing/fastdfs

# path(disk or mount point) count, default value is 1 // storage在存储文件时支持多路径,默认只设置一个(下面store_path0配置的数量个数)
store_path_count=1

# store_path#, based 0, if store_path0 not exists, it's value is base_path // 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在
# the paths must be exist
// 此处可以配置多个路径,如:store_path0=xx, store_path1=xx,store_path2=xx
// 多个项目的文件按照文件夹分割,通过/usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index] 选择storage服务器和store_path角标上传到相应的项目下
store_path0=/home/yuqing/fastdfs

# tracker_server can ocur more than once, and tracker_server format is // 设置tracker_server换成自己的IP端口
#  "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122

修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录和store_path0配置的目录)

mkdir -p /home/yuqing/fastdfs

启动storage服务:

/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start

查看监听:

ps -ef|grep fdfs 或 netstat -lnpt|grep fdfs

22122 和 23000端口都在监听:(/home/yuqing/fastdfs文件夹下看的话,会出现一大堆文件夹)



到这里,我们安装配置并启动了 Tracker 和 Storage 服务,也没有报错了。那他俩是不是在通信呢?我们可以监视一下:

/usr/bin/fdfs_monitor /etc/fdfs/storage.conf

红线处ACTIVE成功:

配置 fdfs_upload_file 上传文件

依然是进入/etc/fdfs的目录操作,首先进入它。会看到三个.sample后缀的文件,我们需要把其中的client.conf.sample文件改为client.conf配置文件并修改它。参照以下命令:

cd /etc/fdfs/
cp client.conf.sample client.conf
vim client.conf

进行修改:

# the base path to store log files // 日志的存放目录
base_path=/home/yuqing/fastdfs

# tracker_server can ocur more than once, and tracker_server format is // tracker_server服务的地址和端口号
#  "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122

修改完成后我们就可以通过/usr/bin/fdfs_upload_file命令上传文件了,/usr/bin/fdfs_upload_file 命令用法:

[root@localhost fdfs]# /usr/bin/fdfs_upload_file
Usage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]
// 命令 配置文件 上传的文件 storage_ip和端口 store_path角标

上传成功:


附带 tracker.conf 和 storage.conf 的具体配置注释

tracker.conf 配置文件分析:


// 配置tracker.conf这个配置文件是否生效,因为在启动fastdfs服务端进程时需要指定配置文件,所以需要使次配置文件生效。false是生效,true是屏蔽。
disabled=false

// 程序的监听地址,如果不设定则监听所有地址
bind_addr=

// tracker监听的端口
port=22122

// 链接超时设定
connect_timeout=30

// tracker在通过网络发送接收数据的超时时间
network_timeout=60

// 数据和日志的存放地点
base_path=/opt/fdfs

// 服务所支持的最大链接数
max_connections=256

// 工作线程数一般为cpu个数
work_threads=4

// 在存储文件时选择group的策略,0:轮训策略 1:指定某一个组 2:负载均衡,选择空闲空间最大的group
store_lookup=2

// 如果上面的store_lookup选择了1,则这里需要指定一个group
// store_group=group2

// 在group中的哪台storage做主storage,当一个文件上传到主storage后,就由这台机器同步文件到group内的其他storage上,0:轮训策略 1:根据ip地址排序,第一个 2:根据优先级排序,第一个
store_server=0

// 选择那个storage作为主下载服务器,0:轮训策略 1:主上传storage作为主下载服务器
download_server=0

// 选择文件上传到storage中的哪个(目录/挂载点),storage可以有多个存放文件的base path 0:轮训策略 2:负载均衡,选择空闲空间最大的
store_path=0

// 系统预留空间,当一个group中的任何storage的剩余空间小于定义的值,整个group就不能上传文件了
reserved_storage_space = 4GB

// 日志信息级别
log_level=info

// 进程以那个用户/用户组运行,不指定默认是当前用户
run_by_group=
run_by_user=

// 允许那些机器连接tracker默认是所有机器
allow_hosts=*

// 设置日志信息刷新到disk的频率,默认10s
sync_log_buff_interval = 10

// 检测storage服务器的间隔时间,storage定期主动向tracker发送心跳,如果在指定的时间没收到信号,tracker人为storage故障,默认120s
check_active_interval = 120

// 线程栈的大小,最小64K
thread_stack_size = 64KB

// storage的ip改变后服务端是否自动调整,storage进程重启时才自动调整
storage_ip_changed_auto_adjust = true

// storage之间同步文件的最大延迟,默认1天
storage_sync_file_max_delay = 86400

// 同步一个文件所花费的最大时间
storage_sync_file_max_time = 300

// 是否用一个trunk文件存储多个小文件
use_trunk_file = false

// 最小的solt大小,应该小于4KB,默认256bytes
slot_min_size = 256

// 最大的solt大小,如果上传的文件小于默认值,则上传文件被放入trunk文件中
slot_max_size = 16MB

// trunk文件的默认大小,应该大于4M
trunk_file_size = 64MB

// http服务是否生效,默认不生效
http.disabled=false

// http服务端口
http.server_port=8080

// 检测storage上http服务的时间间隔,<=0表示不检测
http.check_alive_interval=30

// 检测storage上http服务时所用请求的类型,tcp只检测是否可以连接,http必须返回200
http.check_alive_type=tcp

// 通过url检测storage http服务状态
http.check_alive_uri=/status.html

// if need find content type from file extension name
http.need_find_content_type=true

// 用include包含进http的其他设置
// include http.conf

storage.conf配置文件

storage.conf配置文件分析:

// 同tracker.conf
disabled=false

// 这个storage服务器属于那个group
group_name=group1

// 同tracker.conf
bind_addr=

// 连接其他服务器时是否绑定地址,bind_addr配置时本参数才有效
client_bind=true

// 同tracker.conf
port=23000
connect_timeout=30
network_timeout=60

// 主动向tracker发送心跳检测的时间间隔
heart_beat_interval=30

// 主动向tracker发送磁盘使用率的时间间隔
stat_report_interval=60

// 同tracker.conf
base_path=/opt/fdfs
max_connections=256

// 接收/发送数据的buff大小,必须大于8KB
buff_size = 256KB

// 同tracker.conf
work_threads=4

// 磁盘IO是否读写分离
disk_rw_separated = true

// 是否直接读写文件,默认关闭
disk_rw_direct = false

// 混合读写时的读写线程数
disk_reader_threads = 1
disk_writer_threads = 1

// 同步文件时如果binlog没有要同步的文件,则延迟多少毫秒后重新读取,0表示不延迟
sync_wait_msec=50

// 同步完一个文件后间隔多少毫秒同步下一个文件,0表示不休息直接同步
sync_interval=0

// 表示这段时间内同步文件
sync_start_time=00:00
sync_end_time=23:59

// 同步完多少文件后写mark标记
write_mark_file_freq=500

// storage在存储文件时支持多路径,默认只设置一个
store_path_count=1

// 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在
store_path0=/opt/fdfs
// store_path1=/opt/fastdfs2

// subdir_count  * subdir_count个目录会在store_path下创建,采用两级存储
subdir_count_per_path=256

// 设置tracker_server
tracker_server=x.x.x.x:22122

// 同tracker.conf
log_level=info
run_by_group=
run_by_user=
allow_hosts=*

// 文件在数据目录下的存放策略,0:轮训 1:随机
file_distribute_path_mode=0

// 当问及是轮训存放时,一个目录下可存放的文件数目
file_distribute_rotate_count=100

// 写入多少字节后就开始同步,0表示不同步
fsync_after_written_bytes=0

// 刷新日志信息到disk的间隔
sync_log_buff_interval=10

// 同步storage的状态信息到disk的间隔
sync_stat_file_interval=300

// 线程栈大小
thread_stack_size=512KB

// 设置文件上传服务器的优先级,值越小越高
upload_priority=10

// 是否检测文件重复存在,1:检测 0:不检测
check_file_duplicate=0

// 当check_file_duplicate设置为1时,次值必须设置
key_namespace=FastDFS

// 与FastDHT建立连接的方式 0:短连接 1:长连接
keep_alive=0

// 同tracker.conf
http.disabled=false
http.domain_name=
http.server_port=8888
http.trunk_size=256KB
http.need_find_content_type=true
// include http.conf

nginx简单整合fastDFS


这里小编只是简单的整合,只是为了访问方便,
配置nginx的配置文件,nginx.conf文件:

server {
        listen       89;#监听89端口
        server_name  localhost;#ip
        
        location /group1/M00 {## 当访问89端口并且/group1/M00开头的请求,都有映射到fastDFS的存储数据区
         alias /usr/local/fastdfs-5.05/files_music/data;# 该地址就是文件存储的数据文件夹
        }
}

FastDFS项目中实现文件上传下载


了解FastDFS的工作流程

一、pom文件中加jar包

 <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-io</artifactId>
  <version>1.3.2</version>
</dependency>

二、配置FastDFS基本信息:

在项目的resource目录下面新建fdfs_client.conf配置文件,配置信息如下:

connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8070//搭建时的fastDFS的根据配置
tracker_server = 192.168.200.128:22122

二、工具类

封装文件的实体类

public class FastDFSFile {
    //文件名字
    private String name;
    //文件内容
    private byte[] content;
    //文件扩展名
    private String ext;
    //文件MD5摘要值
    private String md5;
    //文件创建作者
    private String author;

    public FastDFSFile(String name, byte[] content, String ext, String height,
                       String width, String author) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
        this.author = author;
    }

    public FastDFSFile(String name, byte[] content, String ext) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }

    public String getExt() {
        return ext;
    }

    public void setExt(String ext) {
        this.ext = ext;
    }

    public String getMd5() {
        return md5;
    }

    public void setMd5(String md5) {
        this.md5 = md5;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

上传工具类(上传、下载和删除等等操作)

import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author Administrator
 * @date 2020/12/9 10:58
 **/

public class FastDFSClient {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);

    /***
     * 初始化加载FastDFS的TrackerServer配置
     */
    static {
        try {
            String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            logger.error("FastDFS Client Init Fail!",e);
        }
    }

    /***
     * 文件上传
     * @param file
     * @return 1.文件的组名  2.文件的路径信息
     */
    public static String[] upload(FastDFSFile file) {
        logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
        //获取文件的作者
        NameValuePair[] meta_list = new NameValuePair[1];
        meta_list[0] = new NameValuePair("author", file.getAuthor());

        long startTime = System.currentTimeMillis();
        //接收返回数据
        String[] uploadResults = null;
        StorageClient storageClient=null;
        try {
            //创建StorageClient客户端对象
            storageClient = getTrackerClient();

            /***
             * 文件上传
             * 1)文件字节数组
             * 2)文件扩展名
             * 3)文件作者
             */
            uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
        } catch (Exception e) {
            logger.error("Exception when uploadind the file:" + file.getName(), e);
        }

        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        //获取组名
        String groupName = uploadResults[0];
        //获取文件存储路径
        String remoteFileName = uploadResults[1];
        logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);

        return uploadResults;
    }

    /***
     * 获取文件信息
     * @param groupName:组名
     * @param remoteFileName:文件存储完整名
     * @return
     */
    public static FileInfo getFile(String groupName, String remoteFileName) {
        try {
            StorageClient storageClient = getTrackerClient();
            return storageClient.get_file_info(groupName, remoteFileName);
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件下载
     * @param groupName
     * @param remoteFileName
     * @return
     */
    public static InputStream downFile(String groupName, String remoteFileName) {
        try {
            //创建StorageClient
            StorageClient storageClient = getTrackerClient();

            //下载文件
            byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
            InputStream ins = new ByteArrayInputStream(fileByte);
            return ins;
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件删除
     * @param groupName
     * @param remoteFileName
     * @throws Exception
     */
    public static void deleteFile(String groupName, String remoteFileName)
            throws Exception {
        //创建StorageClient
        StorageClient storageClient = getTrackerClient();

        //删除文件
        int i = storageClient.delete_file(groupName, remoteFileName);

        logger.info("delete file successfully!!!" + i);
    }

    /***
     * 获取Storage组
     * @param groupName
     * @return
     * @throws IOException
     */
    public static StorageServer[] getStoreStorages(String groupName)
            throws IOException {
        //创建TrackerClient
        TrackerClient trackerClient = new TrackerClient();
        //获取TrackerServer
        TrackerServer trackerServer = trackerClient.getConnection();
        //获取Storage组
        return trackerClient.getStoreStorages(trackerServer, groupName);
    }

    /***
     * 获取Storage信息,IP和端口
     * @param groupName
     * @param remoteFileName
     * @return
     * @throws IOException
     */
    public static ServerInfo[] getFetchStorages(String groupName,
                                                String remoteFileName) throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
    }

    /***
     * 获取Tracker服务地址
     * @return
     * @throws IOException
     */
    public static String getTrackerUrl() throws IOException {
       // return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+"/";
        return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":89"+"/";//89为nginx监听的端口,有nginx代理监听映射到文件,进行访问
    }

    /***
     * 获取Storage客户端
     * @return
     * @throws IOException
     */
    private static StorageClient getTrackerClient() throws IOException {
        TrackerServer trackerServer = getTrackerServer();
        StorageClient storageClient = new StorageClient(trackerServer, null);
        return  storageClient;
    }

    /***
     * 获取Tracker
     * @return
     * @throws IOException
     */
    private static TrackerServer getTrackerServer() throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return  trackerServer;
    }
}

测试前端页面,提交表上

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="上传">
</form>

测试后端控制台,上传文件

@Controller
public class UploadController {
    private static Logger logger = LoggerFactory.getLogger(UploadController.class);
 
 	@PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
            return "redirect:uploadStatus";
        }
        try {
            // Get the file and save it somewhere
            String path=saveFile(file);
            redirectAttributes.addFlashAttribute("message",
                    "You successfully uploaded '" + file.getOriginalFilename() + "'");
            redirectAttributes.addFlashAttribute("path",
                    "file path url '" + path + "'");
        } catch (Exception e) {
            logger.error("upload file failed",e);
        }
        return "上传成功";
    }


    /*
     * fastdfs上传到服务器
     */
    public String saveFile(MultipartFile multipartFile) throws IOException {
        String[] fileAbsolutePath={};
        String fileName=multipartFile.getOriginalFilename();
        String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
        byte[] file_buff = null;
        InputStream inputStream=multipartFile.getInputStream();
        if(inputStream!=null){
            int len1 = inputStream.available();
            file_buff = new byte[len1];
            inputStream.read(file_buff);
        }
        inputStream.close();
        FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
        try {
            fileAbsolutePath = FastDFSClient.upload(file);  //upload to fastdfs
        } catch (Exception e) {
            logger.error("upload file Exception!",e);
        }
        if (fileAbsolutePath==null) {
            logger.error("upload file failed,please upload again!");
        }
        String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
        logger.info("文件上传成功,地址:"+path);
        return path;
    }
}

访问:

访问成功:


结束语


至此我们的文件存储系统就搭建完成啦。

posted @ 2020-12-09 16:04  Mr*宇晨  阅读(408)  评论(0编辑  收藏  举报