FastDFS + Nginx

普通的存储方式,图片存储过于分散

  • 图片存储过于分散
  • 图片多的服务器压力比较大,可能影响其他功能
  • 存储到项目路径中,重启会丢失。存储到外部文件中,IO操作性能低

搭建图片服务器

FastDFS + Nginx

分布式文件系统概述

通用分布式文件系统

FastDFS + Nginx

专用分布式文件系统

FastDFS + Nginx

Google FS 体系结构

FastDFS + Nginx

FastDFS简介

FastDFS + Nginx

FastDFS + Nginx

架构图

FastDFS + Nginx

FastDFS + Nginx

角色

FastDFS + Nginx

架构解读

FastDFS + Nginx

FastDFS安装

安装C依赖

yum install -y make cmake gcc gcc-c++

安装C的一个库,我这边直接GitHub下载

cd /usr/local/tmp
git clone https://github.com/happyfish100/libfastcommon.git

编译并安装

cd libfastcommon-master
./make.sh
./make.sh install

默认的固定安装位置

FastDFS + Nginx

  • /usr/lib64
  • /usr/lib
  • /usr/include/fastcommon

创建软链接

ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so

上传并解压FastDFS主程序

git clone https://github.com/happyfish100/fastdfs.git

编译并安装

cd fastdfs
./make.sh
./make.sh install

FastDFS + Nginx

  • /usr/bin 可执行文件所在的位置
  • /etc/fdfs 配置文件所在的位置
  • /usr/bin 主程序代码所在位置
  • /usr/include/fastdfs 包含一些插件所在的位置

配置tracker

复制配置文件

cd /etc/fdfs
cp tracker.conf.sample tracker.conf

创建数据目录

创建防止tracker数据的目录

mkdir -p /usr/local/fastdfs/tracker

修改配置文件,修改tracker.conf设置tracker内容存储目录

base_path=/usr/local/fastdfs/tracker
vim tracker.conf

默认端口 22122 不需要修改

启动服务

service fdfs_trackerd start

启动成功后,配置文件base_path指向的目录出现FastDFS服务相关数据目录(data目录,logs目录)

查看服务运行状态

service fdfs_trackerd status

如果显示 is running 表示正常运行

关闭防火墙

service iptables stop
chkconfig iptables off

配置Storage

storage可以和tracker不在同一台服务器中,示例中把storage和tracker安装在同一台服务器

复制配置文件

进入到/etc/fdfs,把storage配置文件复制一份

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

创建目录

把base用于存储基础数据和日志,store用于存储上传数据

mkdir -p /usr/local/fastdfs/storage/base
mkdir -p /usr/local/fastdfs/storage/store

修改配置文件

storage.conf配置文件用于描述存储服务的行为,需要进行下述修改

vim /etc/fdsf/storage.conf

配置内容如下

base_path=/usr/local/fastdfs/storage/base
store_path0=/usr/local/fastdfs/storage/store
tracker_server=tracker 服务IP:22122
  • base_path,基础路径
  • store_path0,
  • base_path和store_path可以使用同一目录
  • tracker_server 跟踪服务器位置,就是跟踪服务器的IP和端口

启动服务

service fdfs_storaged start

启动成功后,配置文件base_path指向的目录中出现FastDFS服务相关数据目录(data目录,logs目录库)

配置文件中的store_path0指向的目录同样出现了FastDFS存储相关数据目录(data目录)

其中$store_path0/data 目录中默认创建若干子孙目录(两层一共256*256),用于存储具体文件数据

Storage服务器启动比较慢,第一次启动的时候,需要创建256*256个目录

查看启动状态

service fdfs_storaged status

文件上传流程

时序图

FastDFS + Nginx

流程说明

  • 客户端访问Tracker
  • Tracker返回Storage的ip和端口
  • 客户端直接访问Storage,把文件内容和元数据发送过去
  • Storage返回文件存储id,包含了组名和文件名

FastDFS + Nginx

Fastdfs-java-client

添加依赖,官方地址:https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java

<!-- https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java -->
<dependency>
    <groupId>cn.bestwu</groupId>
    <artifactId>fastdfs-client-java</artifactId>
    <version>1.27</version>
</dependency>

编写配置文件

fdfs_client.conf,修改成自己的tracker服务器ip

connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.93.10:22122

导入工具类

在com.utils.FastDFSClient下粘贴配置工具类

/**
 * 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); // 不带最后的点
        }
    }
}

Nginx

FastDFS + Nginx

 

 

 代理方式

正向代理

FastDFS + Nginx

 

 

反向代理

FastDFS + Nginx

 

 

二者区别

FastDFS + Nginx

 

 

Nginx作用

HTTP协议代理

FastDFS + Nginx

 

 

搭建虚拟主机

FastDFS + Nginx

负载均衡

FastDFS + Nginx

 

 

Nginx安装

下载fastdfs-nginx-module到/usr/local/tmp中

cd /usr/local/tmp
git clone https://github.com/happyfish100/fastdfs-nginx-module

修改配置文件

cd fastdfs-nginx-module/src
vim config

修改第四行路径,参数用于配置安装nginx中的FastDFS组件的时候,在什么位置查找FastDFS核心代码

git clone https://github.com/nginx/nginx.git

安装ngin依赖

yum install -y gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel

创建临时目录

mkdir -p /var/temp/nginx

修改配置文件参数

./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/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=/usr/local/tmp/fastdfs-nginx-module/src

修改权限为777

chmod 777 nginx -R

编译和安装

cd nginx
make
make install

配置fastdfs-nginx-module模块配置文件

复制 fastdfs-nginx-module/src/mod_fastdfs.conf 配置文件到/etc/fdfs目录中

cp /usr/local/tmp/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/

修改 mod_fastdfs.conf

cd /etc/fdfs
vim mod_fastdfs.conf

文件内容修改

connect_timeout=10
tracker_server=192.168.93.10:22122 // 自己的ip和port
url_have_group_name=true
store_path0=/usr/local/fastdfs/storage/store // 自己的目录

提供FastDFS需要的HTTP配置文件

cp /usr/local/tmp/FastDFS/conf/http.conf /etc/fdfs/
cp /usr/local/tmp/FastDFS/conf/mime.types /etc/fdfs/

创建网络访问存储服务的软链接

ln -s /usr/local/fastdfs/storage/store/data/ /usr/local/fastdfs/storage/store/data/M00

进入nginx安装目录,修改配置文件

cd/ usr/local/nginx/conf
vim nginx.conf

修改两处

用户

user root;

单个group的配置

location /M00 {
      ngx_fastdfs_module;
}

多个group的配置

在server_name下面修改

location ~/group([0-9]/M00){
    ngx_fastdfs_module;
}

启动nginx

进入目录,然后

cd /usr/local/nginx/sbin
./nginx

关闭nginx

./nginx -s quit
上一篇:虚拟机异常关机,导致FastDFS服务启动异常


下一篇:用FastDFS一步步搭建文件管理系统