fastdfs部署+nginx实时缩略图

d_hero
发布于 2023-7-3 11:52
浏览
0收藏

基本原理

术语

FastDFS

FastDFS是一个开源的轻量级分布式文件系统,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合中小文件(建议范围:4KB < file_size <500MB),对以文件为载体的在线服务,如相册网站、视频网站等。


Tracker Server

跟踪服务器, 主要做调度工作, 起负载均衡的作用。

负责管理所有的 storage server 和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。tracker 根据 storage 的心跳信息,建立 group ->[storage serverlist] 的映射表。

Tracker需要管理的元信息很少,会全部存储在内存中;另外tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务。

Storage Server

存储服务器( 又称:存储节点或数据服务器) , 文件和文件属性( metadata) 都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件。

以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。以group为单位组织存储能方便的进行应用隔离、负载均衡、副本数定制(group内storage server数量即为该group的副本数),比如将不同应用数据存到不同的group就能隔离应用数据,同时还可根据应用的访问特性来将应用分配到不同的group来做负载均衡;缺点是group的容量受单机存储容量的限制,同时当group内有机器坏掉时,数据恢复只能依赖group内地其他机器,使得恢复时间会很长。

group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录,比如有10块磁盘,分别挂载在/data/disk1-/data/disk10,则可将这10个目录都配置为storage的数据存储目录。storage接受到写文件请求时,会根据配置好的规则选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,总共65536个文件,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据作为本地文件存储到该目录中。


Client

主要是上传、下载数据的服务器。每个客户端服务器都需要安装Nginx。

单机文件系统的对比


文件系统

高可用

扩展

部署复杂程度

性能

单机文件系统

低,依赖于单机服务器,只要服务器崩溃,完全不可用。

低,要扩容只能停机增加硬盘。

当文件数量多到一定的程度,磁盘IO寻址操作将会成为瓶颈

分布式文件系统

高,一个group内的服务器崩溃后,group内的其他storage将接管服务。

高,可以不停机增加group机器。

高,部署较复杂

高,通过集群或者分布式的方式分担服务器的压力。

其他文件系统的对比


指标

适合类型

文件分布

系统性能

复杂度

FUSE

POSIX

备份机制

通讯协议接口

社区支持

开发语言

FastDFS

4KB~500MB

小文件合并存储不分片处理

很高

简单

不支持

不支持

组内冗余备份

Api HTTP

国内用户群

C语言

TFS

所有文件

小文件合并,以block组织分片


复杂

不支持


Block存储多份,主辅灾备

API http

C++

MFS

大于64K

分片存储

Master占内存多


支持

支持

多点备份动态冗余

使用fuse挂在

较多

Perl

HDFS

大文件

大文件分片分块存储


简单

支持

支持

多副本

原生api

较多

Java

Ceph

对象文件块

OSD一主多从


复杂

支持

支持

多副本

原生api

较少

C++

MogileFS

海量小图片


复杂

可以支持

不支持

动态冗余

原生api

文档少

Perl

ClusterFS

大文件



简单

支持

支持



C

文件上传流程

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用。

  1. 选择tracker server

当集群中不止一个tracker server时,由于tracker之间是完全对等的关系,客户端在upload文件时可以任意选择一个trakcer。

  1. 选择存储的group

当tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group,支持如下选择group的规则:1. Round robin,所有的group间轮询 2. Specified group,指定某一个确定的group 3. Load balance,剩余存储空间多的group优先

  1. 选择storage server

当选定group后,tracker会在group内选择一个storage server给客户端,支持如下选择storage的规则:1. Round robin,在group内的所有storage间轮询 2. First server ordered by ip,按ip排序 3. First server ordered by priority,按优先级排序(优先级在storage上配置)

  1. 选择storage path

当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则:1. Round robin,多个存储目录间轮询 2. 剩余存储空间最多的优先

  1. 生成Fileid

选定存储目录之后,storage会为文件生一个Fileid,由storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。

  1. 选择两级目录

当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。

  1. 生成文件名

当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

文件同步流程

写文件时,客户端将文件写至group内一个storage server即认为写文件成功,storage server写完文件后,会由后台线程将文件同步至同group内其他的storage server。

每个storage写文件后,同时会写一份binlog,binlog里不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,storage会记录向group内其他storage同步的进度,以便重启后能接上次的进度继续同步;进度以时间戳的方式进行记录,所以最好能保证集群内所有server的时钟保持同步。

storage的同步进度会作为元数据的一部分汇报到tracker上,tracke在选择读storage的时候会以同步进度作为参考。

比如一个group内有A、B、C三个storage server,A向C同步到进度为T1 (T1以前写的文件都已经同步到B上了),B向C同步到时间戳为T2(T2 > T1),tracker接收到这些同步进度信息时,就会进行整理,将最小的那个做为C的同步时间戳,本例中T1即为C的同步时间戳为T1(即所有T1以前写的数据都已经同步到C上了);同理,根据上述规则,tracker会为A、B生成一个同步时间戳。

文件下载流程

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

客户端上传文件成功后,会拿到一个storage生成的文件名,接下来客户端根据这个文件名即可访问到该文件。

跟上传文件一样,在下载文件时客户端可以选择任意tracker server。

tracker发送download请求给某个tracker,必须带上文件名信息,tracke从文件名中解析出文件的group、大小、创建时间等信息,然后为该请求选择一个storage用来服务读请求。由于group内的文件同步时在后台异步进行的,所以有可能出现在读到时候,文件还没有同步到某些storage server上,为了尽量避免访问到这样的storage,tracker按照如下规则选择group内可读的storage。

  1. 该文件上传到的源头storage - 源头storage只要存活着,肯定包含这个文件,源头的地址被编码在文件名中。
  2. 文件创建时间戳==storage被同步到的时间戳 且(当前时间-文件创建时间戳) > 文件同步最大时间(如5分钟) - 文件创建后,认为经过最大同步时间后,肯定已经同步到其他storage了。
  3. 文件创建时间戳 < storage被同步到的时间戳。- 同步时间戳之前的文件确定已经同步了 4. (当前时间-文件创建时间戳) > 同步延迟阀值(如一天)。- 经过同步延迟阈值时间,认为文件肯定已经同步了。

安装、部署

单机版(docker)

拉取镜像

docker pull delron/fastdfs
构建Tracker容器

docker run --restart=always -d --network=host --name fastdfs_tracker -v /opt/docker/fastdfs/tracker:/var/fdfs delron/fastdfs tracker
构建Storage容器

docker run --restart=always -d --network=host --name fastdfs_storage -e TRACKER_SERVER=10.100.232.172:22122 -v /opt/docker/fastdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage

Storage的默认请求端口是8888

配置 nginx

# 下载地址,用于fastdfs
location /download/ {
    proxy_pass http://127.0.0.1:8888/;
    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_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-NginX-Proxy true;
}
对fastdfs图片进行实时缩略

说明 fastdfs的storage容器中有nginx的,只是里面没有http_image_filter_module和fastdfs-nginx-module-master

  • http_image_filter_module是nginx自带的,只是在这个storage容器中没有编译进去,需要重新编译
  • fastdfs-nginx-module-master这个模块是需要自己下载,手动编译进去的,我这边提供了一个压缩包,包含了常用的工具
    压缩包下载地址:

链接:https://pan.baidu.com/s/1BT1a_xSVZ5kBcAsgABcIow
提取码:ssyd

下载下来后,需要上传到linux服务器上,我的是上传到 /opt/docker/nginx上

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

下载下来后,需要上传到linux服务器上,我的是上传到 /opt/docker/nginx上

docker cp ./src.zip fastdfs_storage:/usr/local
fastdfs_storage:是刚刚部署的镜像名字,意思是把当前目录下的src.zip文件 copy 到 fastdfs_storage镜像的/usr/local目录下

再进入 fastdfs_storage 对 nginx镜像重新编译

进入镜像的命令:

docker exec -it fastdfs_storage /bin/bash

再依赖安装

cd /usr/local

解压缩src.zip

unzip src.zip

进入src目录

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

重新编译nginx

先停掉正在运行的nginx

/usr/local/nginx/sbin/nginx -s stop

进入Nginx目录

cd /tmp/nginx/nginx-1.12.2

再编译前先更新两个插件库

yum install -y gcc gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel gd-devel

yum install -y libpng libjpeg libpng-devel libjpeg-devel ghostscript libtiff libtiff-devel freetype freetype-devel readline-devel ncurses-devel

完成后重新编译ng,覆盖掉原先的nginx,但是不会覆盖的nginx.conf。记住一定要再 /tmp/nginx/nginx-1.12.2 下

这里面有 configure 命令,或者你也可以再nginx的configure 所在目录下执行就行,因为我的这个docker镜像里的nginx在/tmp/nginx/nginx-1.12.2下

所以要在 /tmp/nginx/nginx-1.12.2 这个文件夹下执行如下命令

./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_flv_module \
--with-http_image_filter_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--add-module=/usr/local/src/fastdfs-nginx-module-master/src \
--with-pcre \
--with-stream

make

make install

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

在修改 /usr/local/nginx/conf下的nginx.conf配置文件,配置缩略的逻辑

未修改前的nginx.conf

#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;

    server {
        listen       8888;
        server_name  localhost;
        location ~/group[0-9]/ {
            ngx_fastdfs_module;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root html;
        }
    }

}

修改后的nginx.conf


#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;

    server {
        listen       8888;
        server_name  localhost;

        #这段往下为生成缩略图的配置
        location ~ group[1-9]/M00/(.+)_([0-9]+)x([0-9]+)\.(JPG|GIF|PNG|JPEG|jpg|gif|png|jpeg) {  
            ngx_fastdfs_module;
            set $w $2;
            set $h $3;

            if ($w != "0") {
                rewrite group1/M00(.+)_(\d+)x(\d+)\.(JPG|GIF|PNG|JPEG|jpg|gif|png|jpeg)$ group1/M00$1.$4           break;
            }

            if ($h != "0") {
                rewrite group1/M00(.+)_(\d+)x(\d+)\.(JPG|GIF|PNG|JPEG|jpg|gif|png|jpeg)$ group1/M00$1.$4           break;
            }

            #根据给定的长宽生成缩略图
            image_filter resize $w $h;
            #原图最大50M,要裁剪的图片超过2M返回415错误
            image_filter_buffer 50M;

            try_files group[1-9]/M00$1.$4 $1.jpg;
        }


        location ~/group[0-9]/ {
            ngx_fastdfs_module;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root html;
        }
    }

}

重启nginx

/usr/local/nginx/sbin/nginx

这些ng的缩略图配置都是在 fastdfs_storage 的docker容器中操作的

操作完事后,退出docker容器

exit;

测试:

springboot简单整合fastdfs,为了测试用

pom.xml

<dependency>
  <groupId>com.github.tobato</groupId>
  <artifactId>fastdfs-client</artifactId>
  <version>1.26.7</version>
</dependency>

application.yml

fdfs:
  # 链接超时
  connect-timeout: 60
  # 读取时间
  so-timeout: 60
  # 生成缩略图参数
  thumb-image:
    width: 150
    height: 150
  tracker-list: 10.100.232.172:22122

FileController

package com.controller;

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * 描述: 上传文件测试controller <br>
 * 时间: 2022-08-09 15:32  <br>
 * 作者:IT学习道场
 */
@RestController
@RequestMapping("/file")
public class FileController {
    @Autowired
    private FastFileStorageClient storageClient;
    @RequestMapping(value = "/uploadFile", headers = "content-type=multipart/form-data", method = RequestMethod.POST)
    public String upload(@RequestParam("file") MultipartFile file) throws Exception {
        String originalFilename = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
        StorePath storePath = this.storageClient.uploadImageAndCrtThumbImage(file.getInputStream(),file.getSize(), originalFilename, null);
        return storePath.getFullPath();
    }

}

postman上传文件测试下

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

浏览器输入地址试试:ip:8888/图片地址

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

现在看看缩略的测试情况

fastdfs部署+nginx实时缩略图-鸿蒙开发者社区

缩略成功,缩略的规则是在图片名 和 后缀中间,加上  

_widthxheight   ,是下划线 + 宽 x  高
示例:_200x200,中间这个是 x ,(英文字母 x )

缩略前路径:http://10.100.232.172/download/group1/M00/00/00/CmTorGLyQPWACSd8AABvnybwBfQ515.jpg
缩略后路径:http://10.100.232.172/download/group1/M00/00/00/CmTorGLyQPWACSd8AABvnybwBfQ515_400x400.jpg

下面另一台服务器上再部署一个单独的nginx,用来缓存缩略图的缓存,这样就不用每次请求缩略图都进行图片缩略和压缩,巨大的浪费cpu,图片的压缩是很吃cpu的

下面是nginx的部署缩略图配置

注意docker部署的nginx要重新部署,新增映射 图片缓存的路径

docker部署nginx的命令:

# 这里网络模式配置为主机模式,以便用127.0.0.1访问本机端口
docker run \
  --restart=always  \
  --name nginx \
  --network host \
  --privileged=true \
  -d \
  -p 80:80 \
  -p 443:443 \
  -e "TZ=Asia/Shanghai" \
  -v /opt/docker/nginx/conf/conf.d:/etc/nginx/conf.d \
  -v /opt/docker/nginx/conf/cert:/etc/nginx/cert \
  -v /opt/docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
  -v /opt/docker/nginx/logs:/var/log/nginx \
  -v /opt/docker/nginx/html:/usr/share/nginx/html \
  -v /opt/docker/nginx/cache:/opt/docker/nginx/cache \
 nginx

解释:

  • restart:跟随docker启动而启动
  • name:当前容器的名字
  • network:host是网络模式配置为主机模式,以便用127.0.0.1访问本机端口
  • privileged:以管理员期限来运行,若不加这段则会出现挂载不上去报期限错误
  • d:后台启动
  • p:暴露端口
  • e:设置环境变量
  • v:挂在数据卷映射

-v /opt/docker/nginx/cache:/opt/docker/nginx/cache 这个是自定义的图片缓存配置,
只缓存缩略图片,因为每次图片缩略都会吃很大的cpu,所以对缩略图进行缓存来减轻cpu的压力

这是nginx.conf的配置,配置了图片的缩略图缓存


#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;


    #nginx_cache 使用一块最大100M的共享内存,用于硬盘上的文件索引,包括文件名和请求次数,
    #proxy_cache_path 缓存文件路径
    #levels 设置缓存文件目录层次;levels=1:2 表示两级目录
    #keys_zone 设置缓存名字和共享内存大小
    #inactive 在指定时间内没人访问则被删除
    #max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。
    proxy_cache_path /opt/docker/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=7d max_size=10g;
    
    #fastdfs的别名
    upstream nginx-cache-server{
        server 10.100.232.172:8888;
    }

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

      
     #从nginx_cache获取图片
      location ~ /download/group[1-9]/M00/(.+)_([0-9]+)x([0-9]+)\.(jpg|gif|png|jpeg) {  
      log_not_found off;
      access_log off;
      proxy_pass http://nginx-cache-server;
      proxy_cache imgcache;
      proxy_cache_key $uri;
      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_cache_valid 200 302 7d;
      proxy_cache_valid 404 10m;
      proxy_cache_valid any 1h;
      proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
      }


        #图片服务器的代理
        location /download/ {
      proxy_pass http://10.100.232.172:8888/;
      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_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-NginX-Proxy true;
     }
     
        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }

}

nginx的重载配置脚本,reload.sh的内容

docker exec -it nginx /usr/sbin/nginx -s reload




文章转载自公众号:IT学习道场

分类
已于2023-7-3 11:52:45修改
收藏
回复
举报
回复
    相关推荐