使用FastDFS搭建分布式文件系统

一、简介和应用场景

1.1、简介

FastDFS是一款使用纯C语言实现的应用级别的分布式文件存储服务

1.2、架构

FastDFS系统由client(文件上传下载客户端)、tracker(协调服务器)、storage(存储服务器)三部分组成。
tracker和storage可以进行集群部署,多个tracker之间并无联系,所以tracker彼此间并不存在同步,仅仅是用做容灾,防止一台tracker宕机后无法继续提供存储服务。
storage服务启动后会向配置的tracker server注册自己,向其报告自己的状态信息,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息,这使得tracker可以协调多个storage共同工作。不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步。

同步方式
Storage server采用binlog文件记录文件上传、删除等更新操作。binlog中只记录文件名,不记录文件内容。
文件同步只在同组内的Storage server之间进行,采用push方式,即源头服务器同步给目标服务器。
 Storage server中由专门的线程根据binlog进行文件同步,Storage server对组内除自己以外的每台服务器都会启动一个线程来进行文件同步。
 文件同步采用增量同步方式,系统记录已同步的位置(binlog文件偏移量)到标识文件中。标识文件名格式:{dest storage IP}_{port}.mark,例如:192.168.1.1_23000.mark。

1.3、交互流程

上传:

   客户端通过API向 tracker  server发起请求,获取当前可用的storage server  地址( 注意:由于多个tracker间并无关联,此处的负载应由客户端去处理。客户端应获取所有可用的tracker server,按照一定的均衡策略从中选取一个可用链接,若此链接在一段时间内都不可用,应将其暂时移除并重新获取,而且要在一定条件下将其重新加入可选列表)。
   客户端从tracker server中成功获取到可用的storage server地址,然后向此storage server发起上传文件的请求。
   storage server向客户端返回此文件的path。
   storage server向组内其他兄弟发起文件同步。

下载:下载一般有两种,图片和PDF等浏览器自身支持的文件类型,可通过nginx代理直接使用path访问。其他关联了业务的附件下载操作应通过应用服务器中转下载。

    单机情况下若ng和storage server在同一台服务器,可以通过文件path直接反向映射到本地磁盘文件。
    集群情况下需安装fastdfs-nginx-module插件,此插件可以自动寻址到上传文件的源storage server上,防止同步时间差导致请求其他storage server 获取不到此文件。

1.4 、缺点及应用场景

FastDFS以简单、易用作为其设计原则,但这无法避免的产生了一些问题:
数据安全性

1.上传文件到源服务器即成功,若此时源服务器宕机且处于同步时间差,那么此文件数据会丢失。
2.同步未对文件做正确性校验,这种同步方式仅适用单个集群点的局部内部网络,如果在公网上使用,肯定会出现损坏文件的情况,需要自行添加文件校验机制(篡改和硬件损坏,几率极低)。

大文件处理

 FastDFS没有对文件做分块存储,因此不太适合分布式计算场景。(不适合存储大文件)

综上所述,fastDFS适用于处理以小文件为载体、文件安全性不是太苛刻的在线存储服务,如相册、视频等。

二、安装及部署

fastdfs

所有服务安装方式都为源码编译安装,提供两种方式下载源码——github和sourceforge,github上有最新的发布版本。
github
sourceforge

nginx 缓存插件

ngx_cache_purge

2.1、环境依赖

操作系统为4台64位CentOS Linux release 7.5.1804

192.168.152.136 nginx tracker
192.168.152.139 tracker
192.168.152.134 storage2 ngx_fastdfs_module
192.168.152.135 storage1 ngx_fastdfs_module

依赖第三方工具:

zlib zlib-devel pcre pcre-devel gcc gcc-c++ openssl openssl-devel libevent libevent-devel perl unzip net-tools wget

2.2、整体架构

线上架构应为:用户——》keepalived(虚拟IP)——》负载nginx(2+)——》tracker负载nginx(2+)——》
storage(2+),架构图如下


使用FastDFS搭建分布式文件系统_第1张图片
complete_fdfs.jpg

本次部署只为模拟,进行了简化,架构图如下


使用FastDFS搭建分布式文件系统_第2张图片
simple_fdfs.jpg

2.2、安装libfastcommon

解压并安装


tar -zxvf V1.0.7.tar.gz

cd libfastcommon-1.0.7

./make.sh

./make.sh isntall

软链接动态链接库到引用路径


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

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

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

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

2.3、安装FastDFS

解压并安装


tar -zxvf V5.05.tar.gz

cd fastdfs-5.05

./make.sh

./make.sh install

软连接到脚本到引用路径


ln -s /usr/bin/fdfs_trackerd  /usr/local/bin

ln -s /usr/bin/fdfs_storaged  /usr/local/bin

ln -s /usr/bin/stop.sh        /usr/local/bin

ln -s /usr/bin/restart.sh      /usr/local/bin

2.4、配置tracker和storage

192.168.152.135 192.168.152.134分别创建 tracker及storage目录


mkdir -p /home/fdfs/trackerd

mkdir -p /home/fdfs/storaged

配置tracker
  vi /etc/fdfs/tracker.conf

主要配置tracker的元数据和日志存储路径和均衡策略等

 #存储路径
  base_path=/home/fdfs/fdfs_trackerd
  #下载文件如何选择storage server
  #0表示轮询,1表示上传的源服务器(避免同步时间差)
  download_server=1
配置storage
  vi /etc/fdfs/storage.conf
#分组名称
group_name=group1
#数据及日志存储路径
base_path=/home/mandy/fdfs/fdfs_storaged
#数据存储路径,可以有多个(可以挂载多个磁盘)
store_path0=/home/fdfs/fdfs_storaged
#tracker server地址,多个写成列表形式
tracker_server=192.168.152.136:22122
tracker_server = 192.168.152.134:22122

配置完毕启动服务并验证

service fdfs_trackerd start
service fdfs_storaged start

查看服务是否已开启

netstat -unltp | grep fdfs

查看storage是否已经激活到tracker

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

storage sever状态

  # FDFS_STORAGE_STATUS_INIT      :初始化,尚未得到同步已有数据的源服务器

  # FDFS_STORAGE_STATUS_WAIT_SYNC :等待同步,已得到同步已有数据的源服务器

  # FDFS_STORAGE_STATUS_SYNCING   :同步中

  # FDFS_STORAGE_STATUS_DELETED   :已删除,该服务器从本组中摘除(注:本状态的功能尚未实现)

  # FDFS_STORAGE_STATUS_OFFLINE   :离线

  # FDFS_STORAGE_STATUS_ONLINE    :在线,尚不能提供服务

  # FDFS_STORAGE_STATUS_ACTIVE    :在线,可以提供服务

2.5、nginx配置

2.5.1、storage nginx配置

由于fastdfs在4.0.5之后的版本中将内置的http服务器移除,因此想通过http方式访问storage server,需要在每个storage上配置 nginx和fastdfs-nginx-module;

安装fastdfs-nginx-module
tar -zxf fastdfs-nginx-module-1.20.tar.gz
cd nginx-1.12.1
./configure --add-module=/home/download/fastdfs-nginx-module-master/src
make&&make install

安装有可能失败,如报如下错误 /usr/local/include/fastdfs/fdfs_define.h:15:27: 致命错误:common_define.h:没有那个文件或目录
解决方式

vim fastdfs-nginx-module-1.20/src/config

编辑
ngx_module_incs="/usr/include/fastdfs /usr/include/fastcommon/"
CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"



./nginx -V 查看fastDFS模块是否已经添加成功

配置nginx.conf

ng配置灰常简单,如下:

   server {
        listen       80;
        server_name  lcoalhost;
        location /M00 {
            ngx_fastdfs_module;
         }

    }

从解压的fastdfs-nginx-module-1.20.tar.gz src下拷贝 mod_fastdfs.conf到/etc/fdfs/下并编辑,目前只针对单个group进行配置

#日志存储路径
base_path=/home/fdfs
#url中是否需要组名,若未分组,可以置为false
url_have_group_name = false
#tracker地址,多个写成列表形式
tracker_server=192.168.152.134:22122
tracker_server = 192.168.152.136:22122,

重启nginx即可.

2.5.2 负载nginx配置

安装nginx插件 ngx_cache_purge
tracker添加一台负载nginx(此处进行了简化,实际线上应多加一层代理)

  #user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
     sendfile        on;
    tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    
    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;

    client_max_body_size 300m;

    proxy_redirect off;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_connect_timeout 90;
    proxy_send_timeout 90;
    proxy_read_timeout 90;

    proxy_buffer_size 16k;
    proxy_buffers 4 64k;
    proxy_busy_buffers_size 128k;
    proxy_temp_file_write_size 128k;
   
    proxy_cache_path /home/mandy/nginx/proxy_cache levels=1:2 
    keys_zone=http-cache:500m max_size=10g inactive=30d;
    proxy_temp_path /home/mandy/nginx/proxy_cache/tmp;

    upstream fdfs_group1 {
         server 192.168.152.134:80 weight=1 max_fails=2 fail_timeout=30s;
         server 192.168.152.135:80 weight=1 max_fails=2 fail_timeout=30s;
    }
    

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location /M00 {
            proxy_next_upstream http_502 http_504 error timeout invalid_header;
            proxy_cache http-cache;
            proxy_cache_valid  200 304 12h;
            proxy_cache_key $uri$is_args$args;
            proxy_pass http://fdfs_group1;
            expires 30d;
        }
        
        
        location ~/purge(/.*) {
            allow all;
            proxy_cache_purge http-cache  $1$is_args$args;
        }
    }     
  

}

2.6、防盗链

fastDFS内置防盗链是在服务端开启token验证,客户端根据文件名、当前unix时间戳、秘钥获取token,在地址中带上token参数即可通过http方式访问文件。
服务端开启认证
拷贝fastdfs安装包conf下的anti-steal.jpg mime.types http.conf文件到 /etc/fdfs/
编辑http.conf

#开启token
http.anti_steal.check_token=true
#token有效期,单位秒(意味着客户端时间要与服务器保持在此时间差以内)
http.anti_steal.token_ttl=900
#加密的key
http.anti_steal.secret_key=FastDFS1234567890

同时需要在 mod_fastdfs.conf中配置认证失败后跳转的403页面
客户端token生成

 public static void main(String[] args){
        #file_path不带分组名,时间为unix时间,key与服务器http.conf配置的key保持一致
        getToken("M00/00/00/wKiYhluV1heAAJDLAAnTlA5XnbM950.pdf",(int) Instant.now().getEpochSecond(),"FastDFS1234567890");
    }
 public static String md5(byte[] source) throws NoSuchAlgorithmException {
        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(source);
        byte[] tmp = md.digest();
        char[] str = new char[32];
        int k = 0;

        for(int i = 0; i < 16; ++i) {
            str[k++] = hexDigits[tmp[i] >>> 4 & 15];
            str[k++] = hexDigits[tmp[i] & 15];
        }

        return new String(str);
    }

    public static String getToken(String remote_filename, int ts, String secret_key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        final String charSet = "UTF-8";
        byte[] bsFilename = remote_filename.getBytes(charSet);
        byte[] bsKey = secret_key.getBytes(charSet);
        byte[] bsTimestamp = (new Integer(ts)).toString().getBytes(charSet);
        byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length];
        System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length);
        System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length);
        System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length);
        return md5(buff);
    }

三、客户端集成

目前客户端使用连接池方式进行调用,首先进行配置:
新建config类并继承GenericKeyedObjectPoolConfig,样例如下:

@Component
@ConfigurationProperties(prefix = "fastdfs.pool")
public class FastdfsPoolConfig extends GenericKeyedObjectPoolConfig {
}

FastDFSConfig添加对FastdfsExecutor的配置,样例如下:

@Configuration
public class FastDFSConfig {
    @Resource
    private FastdfsPoolConfig fastdfsPoolConfig;

    @Bean
    public FastdfsExecutor fastdfsExecutor() {
        FastdfsExecutor executor = new FastdfsExecutor();
        executor.setPoolConfig(fastdfsPoolConfig);
        return executor;
    }

    @Bean
    public SimpleFastdfsClient simpleFastdfsClient(FastdfsExecutor fastdfsExecutor, @Value("${fastdfs.tracker.host}") String trackerServerAddr) {
        return new SimpleFastdfsClient(fastdfsExecutor, trackerServerAddr);
    }
}

默认开启对每次获取的连接的校验testOnBorrow =true,若需改为轮询方式,在spring配置文件中添加如下配置:

 fastdfs.pool.testOnBorrow=false  
 fastdfs.pool.testWhileIdle=true #开启定时任务校验空闲连接
 fastdfs.pool.timeBetweenEvictionRunsMillis=30000 #任务间隙,单位为毫秒
 fastdfs.pool.maxTotalPerKey=10 #每个key最大连接数
 fastdfs.pool.minIdlePerKey=3 #每个key最小空闲连接
 fastdfs.pool.numTestsPerEvictionRun=3 #每次检测空闲连接数

上传样例:

  simpleFastdfsClient.upload(new File("E://data//1.pdf"));

四、遗留问题

使用nginx做文件下载服务器存在一个问题:
文件被删除后,由于nginx服务器对已经访问过的文件进行了缓存,那么此文件在一定时间内还是可以被成功下载,即便fastDFS服务已经关闭。
此文题已经解决,安装ngx_cache_purge 插件,访问“~/purge/资源”即可删除此文件

你可能感兴趣的:(使用FastDFS搭建分布式文件系统)