美文网首页
使用FastDFS搭建分布式文件系统

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

作者: 那么的帅为什么 | 来源:发表于2018-09-10 17:21 被阅读0次

    一、简介和应用场景

    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+),架构图如下


    complete_fdfs.jpg

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


    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搭建分布式文件系统

          本文链接:https://www.haomeiwen.com/subject/ycjsgftx.html