Fastdfs 存储

摘要:
相比GlastFS中的master更为精简,不记录文件索引悉信息,占用的内存量很少。Storageserver存储服务器存储服务器,又称为存储节点或者数据服务器,文件和文件属性都保存在存储服务器上。group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录。FastDFS内存存储机制:1,选择trackerserver当集群中不止一个trackerserver时,由于trackerserver之间是完全对等的关系,客户端在upload文件时可以任意选择一个tracker。
FastDFS 分布式文件存储
作者: 张首富
时间: 2020-03-13
w x: y18163201
Bilibili: https://space.bilibili.com/252465893
51cto: https://blog.51cto.com/13447608

什么是FastDFS?

FastDFS是一个开源的轻量级的分布式文件系统。他解决了大量数据存储和负载均衡等问题。特别适合以中小文件(4KB < FileSize < 500MB)为载体的在线服务,如视频,音频,图片网站等等。
FastDFS是一款开源的轻量级分布式文件系统,他是由纯 C 实现,支持Linux,FreeBSD等UNIX系统类,不是通用的文件系统,只能通过专有的API访问,目前提供了C、Java和PHP API为互联网应用量身定做,解决大容量文件存储问题,追求高性能和高扩展性FastDFS可以看做是基于文件的key value pair存储系统,称作分布式文件存储服务更为合适。

FastDFS的特性?

  • 文件不分块存储,上传的文件和OS文件系统中的文件一一对应
  • 支持相同内容的文件只保存一份,节约磁盘空间(一个group里面只设置一个storage)
  • 下载文件支持HTTP协议,可以使用内置的Web Server,也可以和其他的Web Server配合使用
  • 支持在线扩容
  • 支持主从文件
  • 存储服务器上可以保存文件属性(meta-data)V2.0网络通信采用libevent,支持发兵法访问,整体性能更好

FastDFS架构

image-20200517112920841

Tracker Server 跟踪服务器

跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所存在的所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比GlastFS中的master 更为精简,不记录文件索引悉信息,占用的内存量很少。
Tracker 是FastDFS的协调者,负责管理所有的 storgae server 和 Group,每个storage在启动后会连接tracker,并告知tarcker自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳悉信息,建立group --> storage server list 的映射表。

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

Storage server 存储服务器

存储服务器,又称为存储节点或者数据服务器,文件和文件属性(meta-data)都保存在存储服务器上。storage server直接利用OS的文件系统调用管理文件。

stroage 以组(group)为单位组织,一个group内包含多台stroage机器,数据互为备份,存储空间以group内容量最小的storage为准,所以建议同一个group 里面的storage容量是相同的,防止资源浪费。

以group为单位组织存储能方便的进行应用隔离,负责均衡,副本数定义(group内storage的数量即为该group的副本数)。比如将不同服务的数据写到不同的group里面,来做到资源隔离,我们也可以把一个服务的数据写到多个group里面来做负载均衡。

group的容量受单机存储容量的限制,同时当group内有storag损坏,数据恢复只能依赖于group中其他storage机器,使得恢复时间会很长。

group内每个storage的存储依赖于本地文件系统,storage可配置多个数据存储目录。

storage接受到写文件操作时,会根据配置好的规则,选择其中一个存储目录来存储文件。为了避免单个目录下的文件数太多,在storage第一次启动时,会在每个数据存储目录里创建2级子目录,每级256个,一共65536个目录,新写的文件会以hash的方式被路由到其中某个子目录下,然后将文件数据直接作为一个本地文件存储到该目录中。

client server 客户端

客户端作为业务请求的发起方,通过专有接口,使用TCP/IP协议与跟踪服务器或存储节点发生数据交互。

group 组

组,又可称为卷。,同组内服务器上的文件是完全相同的,同一组内的storage server 之间是对等的,文件上传,删除等操作可以在任意一台storage server 上进行,一个storage server上上面可以对应着多少组,每个组在storage server上可以对应一个设备。

meta data 文件相关属性

键值对( Key Value Pair) 方式,如:width=1024,heigth=768

FastDFS DDL操作

上传机制

首先客户端请求Tracker服务获取到要操作的group组对应的存储服务器的ip地址和端口,然后客户端根据返回的IP地址和端口号请求上传文件,存储服务器接收到请求后生产文件,并且将文件内容写入磁盘并返回给客户端file_id、路径信息、文件名等信息,客户端保存相关信息上传完毕。
image-20200517112936104
FastDFS内存存储机制:
1,选择tracker server
当集群中不止一个tracker server时,由于tracker server之间是完全对等的关系,客户端在upload文件时可以任意选择一个tracker。当tracker接收到upload file的请求时,会为该文件分配一个可以存储该文件的group,支持如下选择group的规则:

  • Round Robin,轮询所有的group
  • Specified Group 指定某一个确定的group
  • Load Balance,剩余存储空间多的group 优先

2,选择storage server
当选定group后,tracker会在group内选择一个storage server 给客户端,支持如下选择storage的规则

  • Round Robin 在group内的所有storage轮询
  • First server ordered by ip 按照IP排序
  • First server ordred by priority 按照优先级排序,优先级在storage上配置

3,选择storage path
当分配好storage server后,客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录,支持如下规则:

  • Round Robin 多个存储目录轮询
  • 剩余存储空间最多的优先

4,生成FileID
选定目录之后,storage会为文件生成一个FileID,由storage server ip + 文件创建时间 + 文件大小 + 文件crc32 +随机数拼接。然后将这个二进制串进行base64编码,转换为可打印的字符串。选择两级目录 当选定存储目录之后,storage会为文件分配一个fileid,每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下。

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

6,storage写入磁盘
每个storage写文件后,同时会写一份binlogbinlog里面不包含文件数据,值包含文件名等元信息,这份binlog用于后台同步,storage会记录向Group内其他storage同步的进度,以便重启后能接上上次的进度继续同步;进度以时间戳的方式进行记录。

7,定期向tracker汇报信息
storage同步进度会作为元数据的一部分会报道所有的tracker上,tracker在选择storage的时候会以此作参考(看下面下载机制)

storage生成的文件名中,包含源头storage ID/IP地址和文件创建时间戳。storage 定时向tracker报告文件同步情况,包括向同组内其他storage同步到的文件时间戳。tracker 收到storage的文件同步报告后,找出该组内每台storage呗同步到的最小时间戳,作为storage属性保存到内存中。

下载机制

客户端带上文件名信息请求Tracker服务获取到storage服务器的IP和Port,然后client更具返回的IP地址和端口号请求下载文件,存储服务器没收到请求后返回文件给客户端。

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

  • 1,该文件上传的源头storage(通过文件名反解出storage ID/IP来判别)
  • 2,(当前时间-文件创建时间戳) > 文件同步延迟阀值(如一天)
  • 3,文件创建时间戳 < storage被同步到的时间戳。(如果当前storage同步到的时间戳为10,文件创建时间戳为8,说明这个文件已经被同步)
  • 4,文件创建时间戳 == storage 被同步到的时间戳,且(当前时间-文件创建时间戳) > 同步一个文件的最大时常(如5分钟)。

上述文件同步延迟阀值和同步一个文件的最大时长这两个参数,在tracker.conf中配置,配置项分别是 storage_sync_file_max_delay 和 storage_sync_file_max_time。
因为FastDFS利用时间戳来解决文件同步延迟带来的文件访问问题。集群内部服务器的时间需要保持一致,要求时间误差不超过1S,所以建议使用NTP时间服务器来确保时间一致。

FastDFS 文件同步机制

我们上面一直在说文件同步,但是文件真正的同步机制又是什么样子的呢?
FastDFS同步文件采用binlog异步复制方式。stroage server 使用binlog(记录文件的元数据)文件记录文件上传、删除等操作,根据binlog进行文件同步。下面 给出几行binlog文件内容实例:
binlog文件的路径$base_path/data/sync/binlog.*

1574850031 C M00/4C/2D/rBCo3V3eTe-AP6SRAABk7o3hUY4681.wav
1574850630 C M00/4C/2D/rBCo3V3eUEaAPMwRAABnbqEmTEs918.wav
1574851230 C M00/4C/2D/rBCo3V3eUp6ARGlEAABhzomSJJo079.wav
1574851230 C M00/4C/2D/rBCo3V3eUp6ABSZWAABoDiunCqc737.wav
1574851830 C M00/4C/2D/rBCo3V3eVPaAYKlIAABormd65Ds168.wav
1574851830 C M00/4C/2D/rBCo3V3eVPaAPs-CAABljrrCwyI452.wav
1574851830 C M00/4C/2D/rBCo3V3eVPaAdSeKAABrLhlwnkU907.wav
1574852429 C M00/4C/2D/rBCo3V3eV02Ab4yKAABmLjpCyas766.wav
1574852429 C M00/4C/2D/rBCo3V3eV02AASzFAABorpw6oJw030.wav
1574852429 C M00/4C/2D/rBCo3V3eV02AHSM7AAB0jpYtHQA019.wav

从上面可以看出,binlog文件有三列,以此为时间戳、操作类型和文件ID(不带group名称)

文件操作类型采用单个字母编码,其中源头操作用大写字母表示,被同步的操作作为对应的小鞋字母
C:上传文件(upload)
D:删除文件(delete)
A:追加文件(append)
M:部分文件更新(modify)
U:整个文件更新(set metadata)
T:截断文件(truncate)
L:创建符号连接(文件去重功能,相同内容的只保存一份)

同组内的storage server之间是对等的,文件上传、删除等操作可以在任意一台storage server上进行。文件同步只在同组内的storage server之间进行,采用push方式,即源头服务器同步给本组的其他存储服务器。对于同组的其他storage server,一台storage server分别启动一个线程进行文件同步。

文件同步采用增量方式,记录已同步的位置到mark文件中。mark文件存放路径为 $base_path/data/sync/。mark文件内容示例:

binlog_index=3
binlog_offset=382
need_sync_old=1
sync_old_done=1
until_timestamp=1571976211
scan_row_count=2033425
sync_row_count=2033417

采用binlog的异步复制方式,必然存在同步延迟的问题,比如mysql的主从数据同步。

数据恢复

单盘数据恢复

当我们Group中一个storage磁盘存在损坏的时候,我们想更换磁盘的时候,磁盘换上之后自动恢复数据。
如何判断是否需要单盘数据恢复:检测$Store_path/data目录下的两个子目录00/00 和FF/FF (每级子目录采用默认256个的情况下)是否存在,若其中一个不存在,则自动建立所需子目录,并启动单盘数据自动恢复。
单盘数据恢复逻辑:

  • 1,从tracker server获取一台可用的storage server 做为源服务器;
  • 2,从storage server拉取该存储路径(store_path顺序对应)的binlog,并存储到本地
  • 3,更具本地binlog从源storage server复制文件到$store_path/data/下对应目录;
  • 4,单盘数据恢复完成后才能对外提供服务。

Docker 安装FastDFS

启动前分析

我们这边采用season/fastdfs:1.2 这个镜像,我们获取他的dockerfile看一下是如何启动的

cat > Obtain_dockerfile.sh <<-'EOF'
#!/bin/bash
#DOC 这是一个通过docker images 来获取dockerfile,看一下怎么启动的
export PATH=$PATH
if [ $# -eq 1 ];then
		docker history --format {{.CreatedBy}} --no-trunc=true $1 |sed "s//bin/sh -c #(nop) //g"|sed "s//bin/sh -c/RUN/g" | tac
	else
		echo "sh Obtain_dockerfile.sh $DOCKER_IMAGE"
fi
EOF

执行脚本获取到的dockerfile如下

# sh Obtain_dockerfile.sh season/fastdfs:1.2
ADD file:b908886c97e2b96665b7afc54ff53ebaef1c62896cf83a1199e59fceff1dafb5 in /
CMD ["/bin/bash"]
MAINTAINER season summer summer@season
RUN apt-get update && apt-get install -y gcc gcc-multilib libc6-dev-i386 make nano htop --no-install-recommends
RUN rm -rf /var/lib/apt/lists/*
COPY dir:9bb2976272b997f08c6435eb1f63b3801cec525f269b6a1de45ef02ba72dc919 in /FastDFS_v4.08
COPY dir:a74a73cd25b708ddc7dc6556b6f9608066876c344de608fb0f2e14dda04a48ba in /libevent-2.0.14
COPY dir:d5fde946a90870a8850d6e9b0b8b7be4e5e41c0b0f2d18cc19589e6caa56061e in /zlib-1.2.8
COPY dir:46967139f210ec8160e07010de80fea21e3950bf7cc680ccd10f3d01d458afce in /fastdfs-nginx-module
COPY dir:d39817fa72b763e78b1fe17483b6fcbebe769e79caf0a2411a9b35b5b104c5f7 in /nginx-1.8.0
COPY file:232f9aba296194eae5e61a56594f2d9b7fc4f03bfb7739e423335b36c7866653 in /entrypoint.sh
WORKDIR /libevent-2.0.14
RUN ./configure --prefix=/usr/local/libevent-2.0.14 && make && make install  && make clean
RUN echo '/usr/local/libevent-2.0.14/include' >> /etc/ld.so.conf
RUN echo '/usr/local/libevent-2.0.14/lib' >> /etc/ld.so.conf
RUN ldconfig
WORKDIR /FastDFS_v4.08
RUN ./make.sh C_INCLUDE_PATH=/usr/local/libevent-2.0.14/include LIBRARY_PATH=/usr/local/libevent-2.0.14/lib && ./make.sh install && ./make.sh clean
WORKDIR /nginx-1.8.0
RUN ./configure --user=root --group=root --prefix=/etc/nginx --with-http_stub_status_module --with-zlib=/zlib-1.2.8 --without-http_rewrite_module --add-module=/fastdfs-nginx-module/src
RUN make
RUN make install
RUN make clean
RUN ln -sf /etc/nginx/sbin/nginx /sbin/nginx
RUN mkdir /fastdfs
RUN mkdir /fastdfs/tracker
RUN mkdir /fastdfs/store_path
RUN mkdir /fastdfs/client
RUN mkdir /fastdfs/storage
RUN mkdir /fdfs_conf
RUN cp /FastDFS_v4.08/conf/* /fdfs_conf
RUN cp /fastdfs-nginx-module/src/mod_fastdfs.conf /fdfs_conf
WORKDIR /
RUN chmod a+x /entrypoint.sh
ENTRYPOINT &{["/entrypoint.sh"]}

可以看到我们启动都是执行这个脚本来实现的,我们现在来看一下这个脚本里面的内容如下:

#!/bin/bash
#set -e

TRACKER_BASE_PATH="/fastdfs/tracker"
TRACKER_LOG_FILE="$TRACKER_BASE_PATH/logs/trackerd.log"

STORAGE_BASE_PATH="/fastdfs/storage"
STORAGE_LOG_FILE="$STORAGE_BASE_PATH/logs/storaged.log"

TRACKER_CONF_FILE="/etc/fdfs/tracker.conf"
STORAGE_CONF_FILE="/etc/fdfs/storage.conf"

NGINX_ACCESS_LOG_FILE="/etc/nginx/logs/access.log"
NGINX_ERROR_LOG_FILE="/etc/nginx/logs/error.log"

MOD_FASTDFS_CONF_FILE="/etc/fdfs/mod_fastdfs.conf"

# remove log files
if [  -f "/fastdfs/tracker/logs/trackerd.log" ]; then
	rm -rf "$TRACKER_LOG_FILE"
fi

if [  -f "/fastdfs/storage/logs/storaged.log" ]; then
	rm -rf "$STORAGE_LOG_FILE"
fi

if [  -f "$NGINX_ACCESS_LOG_FILE" ]; then
	rm -rf "$NGINX_ACCESS_LOG_FILE"
fi

if [  -f "$NGINX_ERROR_LOG_FILE" ]; then
	rm -rf "$NGINX_ERROR_LOG_FILE"
fi

if [ "$1" = 'shell' ]; then
	/bin/bash
fi

if [ "$1" = 'tracker' ]; then
	echo "start  fdfs_trackerd..."

	if [ ! -d "/fastdfs/tracker/logs" ]; then
		mkdir "/fastdfs/tracker/logs"
	fi

	n=0
	array=()
	#循环读取配置文件
	while read line
	do
	    array[$n]="${line}";
	    ((n++));
	done < /fdfs_conf/tracker.conf

	rm "$TRACKER_CONF_FILE"
	#${!array[@]} 为数组的下标
	for i in "${!array[@]}"; do
		#判断组名是否为空
	    if [ ${STORE_GROUP} ]; then
	    	#如果不为空,则判断是否包含storage_group 这个字段,然后把这行地换掉
	        [[ "${array[$i]}" =~ "store_group=" ]] && array[$i]="store_group=${STORE_GROUP}"
	    fi
	    # 循环追加配置
	    echo "${array[$i]}" >> "$TRACKER_CONF_FILE"
	done

	touch  "$TRACKER_LOG_FILE"
	ln -sf /dev/stdout "$TRACKER_LOG_FILE"

	fdfs_trackerd $TRACKER_CONF_FILE
	sleep 3s  #delay wait for pid file
	# tail -F --pid=`cat /fastdfs/tracker/data/fdfs_trackerd.pid`  /fastdfs/tracker/logs/trackerd.log
	# wait `cat /fastdfs/tracker/data/fdfs_trackerd.pid`
	tail -F --pid=`cat /fastdfs/tracker/data/fdfs_trackerd.pid`  /dev/null
 fi

if [ "$1" = 'storage' ]; then
	echo "start  fdfs_storgaed..."
	n=0
	array=()

	while read line
	do
	    array[$n]="${line}";
	    ((n++));
	done < /fdfs_conf/storage.conf

	rm "$STORAGE_CONF_FILE"

	for i in "${!array[@]}"; do
	    if [ ${GROUP_NAME} ]; then
	        [[ "${array[$i]}" =~ "group_name=" ]] && array[$i]="group_name=${GROUP_NAME}"
	    fi

	    if [ ${TRACKER_SERVER} ]; then
	            [[ "${array[$i]}" =~ "tracker_server=" ]] && array[$i]="tracker_server=${TRACKER_SERVER}"
	    fi
	    echo "${array[$i]}" >> "$STORAGE_CONF_FILE"
	done

	if [ ! -d "/fastdfs/storage/logs" ]; then
		mkdir "/fastdfs/storage/logs"
	fi

	touch  "$STORAGE_LOG_FILE"
	ln -sf /dev/stdout "$STORAGE_LOG_FILE"

	fdfs_storaged "$STORAGE_CONF_FILE"
	sleep 3s  #delay wait for pid file
	# tail -F --pid=`cat /fastdfs/storage/data/fdfs_storaged.pid`  /fastdfs/storage/logs/storaged.log
	#wait -n `cat /fastdfs/storage/data/fdfs_storaged.pid`
	tail -F --pid=`cat /fastdfs/storage/data/fdfs_storaged.pid`  /dev/null
fi

if [ "$1" = 'nginx' ]; then
	echo "starting nginx..."
	# ln log files to stdout/stderr
	touch  "$NGINX_ACCESS_LOG_FILE"
	ln -sf /dev/stdout "$NGINX_ACCESS_LOG_FILE"
	touch  "$NGINX_ERROR_LOG_FILE"
	ln -sf /dev/stderr "$NGINX_ERROR_LOG_FILE"

	# change mod_fastfdfs.conf
	n=0
	array=()

	while read line
	do
	    array[$n]="${line}";
	    ((n++));
	done < /fdfs_conf/mod_fastdfs.conf

	if [  -f "$MOD_FASTDFS_CONF_FILE" ]; then
		rm -rf "$MOD_FASTDFS_CONF_FILE"
	fi

	for i in "${!array[@]}"; do
	    if [ ${GROUP_NAME} ]; then
	        [[ "${array[$i]}" =~ "group_name=" ]] && array[$i]="group_name=${GROUP_NAME}"
	    fi

	    if [ ${TRACKER_SERVER} ]; then
	            [[ "${array[$i]}" =~ "tracker_server=" ]] && array[$i]="tracker_server=${TRACKER_SERVER}"
	    fi

	    if [ ${URL_HAVE_GROUP_NAME} ]; then
	            [[ "${array[$i]}" =~ "url_have_group_name=" ]] && array[$i]="url_have_group_name=${URL_HAVE_GROUP_NAME}"
	    fi

	    if [ ${STORAGE_SERVER_PORT} ]; then
	            [[ "${array[$i]}" =~ "storage_server_port=" ]] && array[$i]="storage_server_port=${STORAGE_SERVER_PORT}"
	    fi

	    echo "${array[$i]}" >> "$MOD_FASTDFS_CONF_FILE"
	done

	nginx -g "daemon off;"

我们简单的分析下这个脚本,发现这个脚本是按照启动后面传递的参数来判断是启动trakcerstorage还是nginx,脚本什么的我们也看了 现在来启动测试下

启动tracker

docker run -ti -d --name trakcer 
    -v /etc/localtime:/etc/localtime
    -v /tracker_data:/fastdfs/tracker/data 
    --net=host 
    --restart=always 
    season/fastdfs tracker

启动之后tracker 会监听在22122 上,我们也可以通过传递环境变量的方式来更改端口

-e port=22222

所有配置文件里面的配置我们都可以通过环境变量的方式传递进去

# 是否禁用该配置文件
# false:开启
# true:禁用
disabled=false
# 绑定地址
# 空字符表示绑定所有地址
bind_addr=
# 端口设置
port=22122
# 连接超时设置
connect_timeout=30
# 网络超时设置
network_timeout=30
# 存储数据和日志的目录
base_path=/mnt/fastdfs
# 最大并发连接数
max_connections=256
# 接受请求的线程数
accept_threads=1
# 执行请求的线程数 <= max_connections
work_threads=4
# 如何选择上传文件的存储group
# 0: 轮询
# 1: 制定group名称
# 2: 负载均衡, 选择空闲空间最大的存储group
store_lookup=2
# 选择哪一个存储group,当store_lookup=1时,须指定存储group的名称
store_group=group2
# 如何选择上传文件的存储server
# 0: 轮询
# 1: 以ip地址排序的第一个地址
# 2: 以优先级排序
store_server=0
# 选择存储server的哪一个路径(磁盘或挂载点)上传文件
# 0: 轮询
# 2: 负载均衡, 选择空闲空间最大的路径来存储文件
store_path=0
# 选择哪一个存储server下载文件
# 0: 轮询
# 1: 选择该文件上传时的那个存储server
download_server=0
# 预留的存储空间 G(GB) M(MB) K(KB) 默认byte(B),% 表示比例,如下,预留的存储空间为10%,即只占用90%
reserved_storage_space = 10%
# log级别:alert error notice info debug
log_level=info
# 运行用户组,默认当前用户组
run_by_group=
# 运行用户,默认当前用户
run_by_user=
# 允许的host,例子如下
# "*" 表示所有
# 10.0.1.[1-15,20]
# host[01-08,20-25].domain.com:
# allow_hosts=10.0.1.[1-15,20]
# allow_hosts=host[01-08,20-25].domain.com
allow_hosts=*
# 同步内存日志到磁盘的间隔时间,默认10s
sync_log_buff_interval = 10
# 检查存储server存活的间隔时间,默认120s
check_active_interval = 120
# 线程栈大小 >= 64KB
thread_stack_size = 64KB
# 当存储server变化时,是否自动调整存储server的ip
storage_ip_changed_auto_adjust = true
# 存储server同步文件的最大延时,默认86400s(1天)
storage_sync_file_max_delay = 86400
# 存储server同步一个文件的最大时间
storage_sync_file_max_time = 300
# 是否用一个主文件存储多个小文件
use_trunk_file = false
# 最小的slot大小(多个小文件之间的空隙大小),小于4k
slot_min_size = 256
# 最大的slot大小(多个小文件之间的空隙大小) > slot_min_size
# 当上传的文件大小 < slot_max_size时,那么该文件将合并到一个主文件
slot_max_size = 16MB
# 主文件大小 >= 4MB
trunk_file_size = 64MB
# 是否提前创建trunk文件
trunk_create_file_advance = false
# 创建trunk文件的基准时间
trunk_create_file_time_base = 02:00
# 创建trunk文件的间隔时间,默认86400s(1天)
trunk_create_file_interval = 86400
# 创建trunk文件的阈值空间
trunk_create_file_space_threshold = 20G
# 当加载trunk空间空间时,是否检查trunk空间占用情况,设置为true,启动会变慢 
trunk_init_check_occupying = false
# 是否忽略storage_trunk.dat
trunk_init_reload_from_binlog = false
# 压缩trunk binlog文件的间隔时间
# 0表示不压缩
trunk_compress_binlog_min_interval = 0
# 是否使用storage ID,而不是ip地址
use_storage_id = false
# 定义storage ids文件,相对或绝对路径
storage_ids_filename = storage_ids.conf
# 存储id类型,当use_storage_id=true时有效
## ip: 存储server的IP
## id: 存储server的ID
id_type_in_filename = ip
# 是否使用链接文件存储slave文件
store_slave_file_use_link = false
# 是否滚动错误日志文件
rotate_error_log = false
# 滚动错误日志文件的时间点
error_log_rotate_time=00:00
# 当错误日志文件超出该值时,滚动错误日志文件
rotate_error_log_size = 0
# 保存日志文件的天数
# 0表示不删除日志文件
log_file_keep_days = 0
# 是否使用连接池
use_connection_pool = false
# 连接最大空闲时间
connection_pool_max_idle_time = 3600
# tracker的http端口
http.server_port=8080
# 检查http server存活的时间间隔
http.check_alive_interval=30
# 检查http sever存活的类型
#   tcp : 仅连接http端口,不发请求
#   http: 发http请求,并需返回200
# default value is tcp
http.check_alive_type=tcp
# 检查http server的uri
http.check_alive_uri=/status.html

启动storage

docker run -di --name storage 
     --restart=always 
     -v /storage_data:/fastdfs/storage/data 
     -v /store_path:/fastdfs/store_path 
     --net=host 
     -e TRACKER_SERVER=172.16.1.170:22122 season/fastdfs:1.2 storage

可传递的环境变量

这个配置文件是否不生效
disabled=false
group_name=group1
bind_addr=
client_bind=true
port=23000
connect_timeout=30
network_timeout=60
heart_beat_interval=30
stat_report_interval=60
base_path=/fastdfs/storage
max_connections=256
buff_size = 256KB
accept_threads=1
work_threads=4
disk_rw_separated = true
disk_reader_threads = 1
disk_writer_threads = 1
sync_wait_msec=50
sync_interval=0
sync_start_time=00:00
sync_end_time=23:59
write_mark_file_freq=500
store_path_count=1
store_path0=/fastdfs/store_path
subdir_count_per_path=256
tracker_server=172.16.1.170:22122
log_level=info
run_by_group=
run_by_user=
allow_hosts=*
file_distribute_path_mode=0
file_distribute_rotate_count=100
fsync_after_written_bytes=0
sync_log_buff_interval=10
sync_binlog_buff_interval=10
sync_stat_file_interval=300
thread_stack_size=512KB
upload_priority=10
if_alias_prefix=
check_file_duplicate=0
file_signature_method=hash
key_namespace=FastDFS
keep_alive=0
use_access_log = false
rotate_access_log = false
access_log_rotate_time=00:00
rotate_error_log = false
error_log_rotate_time=00:00
rotate_access_log_size = 0
rotate_error_log_size = 0
file_sync_skip_invalid_record=false
use_connection_pool = false
connection_pool_max_idle_time = 3600
http.domain_name=
http.server_port=8888

测试fastdfs

进入到tracker容器里面

docker exec -it tracker bash
grep 22122 /home/fdfs/client.conf
sed -i "s#`grep 22122 /home/fdfs/client.conf`#tracker_server=172.16.1.170:22122#g" /home/fdfs/client.conf

#使用下面命令查看fastdfs集群状态
fdfs_monitor /etc/fdfs/client.conf
#上传一个文件测试下
fdfs_upload_file /etc/fdfs/client.conf /etc/hosts
    group1/M00/00/00/rBABql33W5CAK7yFAAAAnrLoM8Y9254622

#下载文件
root@test01:/etc/fdfs# fdfs_download_file /etc/fdfs/client.conf group1/M00/00/00/rBABql33W5CAK7yFAAAAnrLoM8Y9254622
root@test01:/etc/fdfs# ls -l rBABql33W5CAK7yFAAAAnrLoM8Y9254622
-rw-r--r-- 1 root root 158 Dec 16 10:26 rBABql33W5CAK7yFAAAAnrLoM8Y9254622
root@test01:/etc/fdfs# cat rBABql33W5CAK7yFAAAAnrLoM8Y9254622
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

#删除文件
fdfs_delete_file /etc/fdfs/client.conf group1/M00/00/00/rBABql33W5CAK7yFAAAAnrLoM8Y9254622

启动nginx给别的程序访问使用

docker run -id --name fastdfs_nginx 
    --restart=always 
    -v /store_path:/fastdfs/store_path 
    -p 8888:80 
    -e GROUP_NAME=group1 
    -e TRACKER_SERVER=172.16.1.170:22122 
    -e STORAGE_SERVER_PORT=23000 
    season/fastdfs:1.2 nginx

注意:

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

可更改的配置文件

grep -Ev "^#|^$" mod_fastdfs.conf
connect_timeout=2
network_timeout=30
base_path=/tmp
load_fdfs_parameters_from_tracker=true
storage_sync_file_max_delay = 86400
use_storage_id = false
storage_ids_filename = storage_ids.conf
tracker_server=tracker:22122
storage_server_port=23000
group_name=group1
url_have_group_name=true
store_path_count=1
#storage_path0 要和storage的相同
store_path0=/fastdfs/store_path
log_level=info
log_filename=
response_mode=proxy
if_alias_prefix=
flv_support = true
flv_extension = flv
group_count = 0

集群搭建

我们只需要在另外一台机器启动storage和nginx即可,然后做个负载均衡为所有的

命令

数据迁移

迁移时IP地址变化采用默认配置安装
在这种模式下,文件迁移本质就是替换ip地址,找到文件服务器中配置文件与ip地址相关的文件替换后就可以了。为了迁移以前的文件数据,需进行如下操作:

新服务器停止tracker和storage,分别将其数据文件目录用旧服务器的文件替换掉。
删除新服务器data数据,使用旧服务器data替换(复制的时候一定要注意.data_init_flang 文件)
修改tracker的data文件,主要是修改以下文件中的ip信息。

data/storage_groups_new.dat
data/storage_servers_new.dat
data/storage_sync_timestamp.dat

修改storage的data文件,主要是修改以下文件中的ip信息 。

./.data_init_flag
data/sync/${ip_addr}_${port}.mark:此类文件,需要将文件名中的IP地址调整过来。若sync目录下面没有文件,可不修改。

修改client.conf,storage.conf,mod_fastdfs.conf文件中的ip地址为新的ip地址。
利用ngigx测试下载以前服务器中存成的文件,下载原文件成功表示迁移成功。

文件解读

tracker 文件解读

tracker.conf

# 是否禁用该配置文件
# false:开启
# true:禁用
disabled=false
# 绑定地址
# 空字符表示绑定所有地址
bind_addr=
# 端口设置
port=22122
# 连接超时设置
connect_timeout=30
# 网络超时设置
network_timeout=30
# 存储数据和日志的目录
base_path=/mnt/fastdfs
# 最大并发连接数
max_connections=256
# 接受请求的线程数
accept_threads=1
# 执行请求的线程数 <= max_connections
work_threads=4
# 如何选择上传文件的存储group
# 0: 轮询
# 1: 制定group名称
# 2: 负载均衡, 选择空闲空间最大的存储group
store_lookup=2
# 选择哪一个存储group,当store_lookup=1时,须指定存储group的名称
store_group=group2
# 如何选择上传文件的存储server
# 0: 轮询
# 1: 以ip地址排序的第一个地址
# 2: 以优先级排序
store_server=0
# 选择存储server的哪一个路径(磁盘或挂载点)上传文件
# 0: 轮询
# 2: 负载均衡, 选择空闲空间最大的路径来存储文件
store_path=0
# 选择哪一个存储server下载文件
# 0: 轮询
# 1: 选择该文件上传时的那个存储server
download_server=0
# 预留的存储空间 G(GB) M(MB) K(KB) 默认byte(B),% 表示比例,如下,预留的存储空间为10%,即只占用90%
reserved_storage_space = 10%
# log级别:alert error notice info debug
log_level=info
# 运行用户组,默认当前用户组
run_by_group=
# 运行用户,默认当前用户
run_by_user=
# 允许的host,例子如下
# "*" 表示所有
# 10.0.1.[1-15,20]
# host[01-08,20-25].domain.com:
# allow_hosts=10.0.1.[1-15,20]
# allow_hosts=host[01-08,20-25].domain.com
allow_hosts=*
# 同步内存日志到磁盘的间隔时间,默认10s
sync_log_buff_interval = 10
# 检查存储server存活的间隔时间,默认120s
check_active_interval = 120
# 线程栈大小 >= 64KB
thread_stack_size = 64KB
# 当存储server变化时,是否自动调整存储server的ip
storage_ip_changed_auto_adjust = true
# 存储server同步文件的最大延时,默认86400s(1天)
storage_sync_file_max_delay = 86400
# 存储server同步一个文件的最大时间
storage_sync_file_max_time = 300
# 是否用一个主文件存储多个小文件
use_trunk_file = false
# 最小的slot大小(多个小文件之间的空隙大小),小于4k
slot_min_size = 256
# 最大的slot大小(多个小文件之间的空隙大小) > slot_min_size
# 当上传的文件大小 < slot_max_size时,那么该文件将合并到一个主文件
slot_max_size = 16MB
# 主文件大小 >= 4MB
trunk_file_size = 64MB
# 是否提前创建trunk文件
trunk_create_file_advance = false
# 创建trunk文件的基准时间
trunk_create_file_time_base = 02:00
# 创建trunk文件的间隔时间,默认86400s(1天)
trunk_create_file_interval = 86400
# 创建trunk文件的阈值空间
trunk_create_file_space_threshold = 20G
# 当加载trunk空间空间时,是否检查trunk空间占用情况,设置为true,启动会变慢 
trunk_init_check_occupying = false
# 是否忽略storage_trunk.dat
trunk_init_reload_from_binlog = false
# 压缩trunk binlog文件的间隔时间
# 0表示不压缩
trunk_compress_binlog_min_interval = 0
# 是否使用storage ID,而不是ip地址
use_storage_id = false
# 定义storage ids文件,相对或绝对路径
storage_ids_filename = storage_ids.conf
# 存储id类型,当use_storage_id=true时有效
## ip: 存储server的IP
## id: 存储server的ID
id_type_in_filename = ip
# 是否使用链接文件存储slave文件
store_slave_file_use_link = false
# 是否滚动错误日志文件
rotate_error_log = false
# 滚动错误日志文件的时间点
error_log_rotate_time=00:00
# 当错误日志文件超出该值时,滚动错误日志文件
rotate_error_log_size = 0
# 保存日志文件的天数
# 0表示不删除日志文件
log_file_keep_days = 0
# 是否使用连接池
use_connection_pool = false
# 连接最大空闲时间
connection_pool_max_idle_time = 3600
# tracker的http端口
http.server_port=8080
# 检查http server存活的时间间隔
http.check_alive_interval=30
# 检查http sever存活的类型
#   tcp : 仅连接http端口,不发请求
#   http: 发http请求,并需返回200
# default value is tcp
http.check_alive_type=tcp
# 检查http server的uri
http.check_alive_uri=/status.html
[root@zsf-140 tracker]# tree
.
├── fdfs_trackerd.pid               #tracker的进程 ID
├── storage_changelog.dat           #
├── storage_groups_new.dat          #分组信息
├── storage_servers_new.dat         #storage 服务器信息
└── storage_sync_timestamp.dat      #同步信息

storage_groups_new.dat

# global section
[Global]                    #全局配置
	group_count=1            #当前这个tracker 下有几个 group 组

# group: group1
[Group001]                  #组 1
	group_name=group1        #组名
	storage_port=23000       #storage 端口
	storage_http_port=8888   #nginx 端口
	store_path_count=1       
	subdir_count_per_path=256
	current_trunk_file_id=0
	trunk_server=
	last_trunk_server=

storage_servers_new.dat

# storage 192.168.4.140:23000
[Storage001]
	group_name=group1          #组名
	ip_addr=192.168.4.140      #当前地址
	status=7                   #状态
	version=4.08               #版本
	join_time=1583718804
	storage_port=23000         #端口
	storage_http_port=8888     #nginx 端口
	domain_name=               #域名
	sync_src_server=           #向该 storage server 同步已有数据文件的源服务器
	sync_until_timestamp=0     #同步已有数据文件的截至时间(UNIX时间戳)
	store_path_count=1 
	subdir_count_per_path=256
	upload_priority=10
	total_mb=0
	free_mb=0
	total_upload_count=0       #上传文件次数
	success_upload_count=0     #成功上传文件次数
	total_append_count=0       #修改文件次数
	success_append_count=0     #成功修改文件次数
	total_set_meta_count=0     #更改 meta data 次数
	success_set_meta_count=0   #成功更改 meta data 次数
	total_delete_count=0       #删除文件次数
	success_delete_count=0     #成功删除文件次数
	total_download_count=0     #下载文件次数
	success_download_count=0   #成功下载文件次数
	total_get_meta_count=0     #获取 meta data 次数
	success_get_meta_count=0   #成功获取 meta data 次数
	total_create_link_count=0  #创建连接文件次数
	success_create_link_count=0#成功创建连接文件次数
	total_delete_link_count=0  #删除连接次数
	success_delete_link_count=0#成功删除连接次数
	total_upload_bytes=0       #上传的总字节数
	success_upload_bytes=0     #成功上传的字节数
	total_append_bytes=0       
	success_append_bytes=0
	total_download_bytes=0
	success_download_bytes=0
	total_sync_in_bytes=0      #同步的字节数
	success_sync_in_bytes=0    #成功同步的字节数
	total_sync_out_bytes=0
	success_sync_out_bytes=0
	total_file_open_count=0    #打开文件的次数
	success_file_open_count=0  #成功打开文件的次数
	total_file_read_count=0    #读取的文件次数
	success_file_read_count=0  #成功读取的文件次数
	total_file_write_count=0   #写文件次数
	success_file_write_count=0 #成功写文件次数
	last_source_update=0       #最近一次源头更新时间(更新操作来自客户端)
	last_sync_update=0         #最近一次同步更新时间(更新操作来自其他storage server的同步)
	last_synced_timestamp=0
	last_heart_beat_time=1583718834
	changelog_offset=35


# global section
[Global]                    #全局配置
	storage_count=1          #storage 的数量

storage.conf

## 是否禁用该配置文件# false:开启
# true:禁用
disabled=false
# 组名称
group_name=group1
# 绑定地址
# 空字符表示绑定所有地址
bind_addr=
# 当连接其他存储server时,是否绑定地址 
client_bind=true
# 存储server端口
port=23000
# 连接超时
connect_timeout=30
# 网络超时
network_timeout=60
# 心跳
heart_beat_interval=30
# 磁盘使用报道的时间间隔
stat_report_interval=60
# 数据和日志目录
base_path=/mnt/fastdfs
# 最大并发连接数
max_connections=256
# 接收和发送数据的缓冲区大小
buff_size = 256KB
# 接受请求的线程数
accept_threads=1
# 执行请求的线程数
work_threads=4
# 磁盘读写是否分开
disk_rw_separated = true
# 每个存储基路径的读线程数
disk_reader_threads = 1
# 每个存储基路径的写线程数
disk_writer_threads = 1
# 当没有进行同步时, 多少毫秒后读取binlog
sync_wait_msec=50
# 当同步完一个文件后, 暂停多少毫秒
# 0 表示不调用usleep
sync_interval=0
# 每天存储同步的开始时间
sync_start_time=00:00
# 每天存储同步的结束时间
sync_end_time=23:59
# 同步多少个文件后,写入mark文件
write_mark_file_freq=500
# 路径(磁盘或挂载点)数
store_path_count=1
# 存储路径
store_path0=/mnt/fastdfs
# 子目录数
subdir_count_per_path=256
# tracker地址,可以配置多个tracker_server
tracker_server=10.112.88.105:22122
# tracker_server=10.112.88.104:22122
# 日志级别:alert error notice info debug
log_level=debug
# 运行进程的用户组,默认当前用户组
run_by_group=
# 运行进程的用户,默认当前用户
run_by_user=
# 允许的host,例子如下# "*" 表示所有
# 10.0.1.[1-15,20]
# host[01-08,20-25].domain.com:
# allow_hosts=10.0.1.[1-15,20]
# allow_hosts=host[01-08,20-25].domain.com
allow_hosts=*
# 文件分布模式
# 0: 轮询
# 1: 随机
file_distribute_path_mode=0
# 写多少个文件后,轮到下一个路径 
file_distribute_rotate_count=100
# 当写入多大文件时,调用fsync
# 0: 不调用
# other: 文件大小(byte)
fsync_after_written_bytes=0
# 同步内存日志到磁盘的间隔时间,默认10s
sync_log_buff_interval=10
# 同步binlog到磁盘的间隔时间,默认10s
sync_binlog_buff_interval=10
# 同步存储状态信息到磁盘的间隔时间,默认300s
sync_stat_file_interval=300
# 线程栈大小 >= 512KB
thread_stack_size=512KB
# 上传文件的优先级,tracker中使用
upload_priority=10
# the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a
# multi aliases split by comma. empty value means auto set by OS type
# default values is empty
if_alias_prefix=
# 是否检查文件重复
check_file_duplicate=0
# 文件签名方法
## hash: hash
## md5: MD5
file_signature_method=hash
# 存储文件索引的命名空间,check_file_duplicate=true时
key_namespace=FastDFS
# 设置与FastDHT保持长连接的数目,0表示使用短连接
keep_alive=0
# FastDHT server列表,需要安装FastDHT
##include /home/yuqing/fastdht/conf/fdht_servers.conf
# 是否记录访问日志
use_access_log = true
# 是否每天滚动访问日志文件
rotate_access_log = true
# 访问日志滚动时间点
access_log_rotate_time=00:00
# 是否每天滚动错误日志文件
rotate_error_log = true
# 错误日志滚动时间点
error_log_rotate_time=00:00
# 访问日志滚动大小
rotate_access_log_size = 0
# 错误日志滚动大小
rotate_error_log_size = 0
# 日志保留天数
log_file_keep_days = 7
# 同步文件时是否跳过无效的文件
file_sync_skip_invalid_record=false
# 是否使用连接池
use_connection_pool = false
# 连接最大空间时间
connection_pool_max_idle_time = 3600
# 域名
http.domain_name=
# http端口
http.server_port=8888

192.168.4.140_23000.mark

binlog_index=0
binlog_offset=111583938  #同步的偏移量,如果大小刚好等于 index.000的文件大小,说明同步完成
need_sync_old=1     #表示是否需要同步旧文件给对方
sync_old_done=1     #表示若需要同步旧文件,那么旧文件是否同步完成
until_timestamp=1581590986  #表示源主机同步的操作截止时间
scan_row_count=1923861      #表示扫描过的操作记录行数
sync_row_count=961809       #表示同步完的操作记录行数

生产优化参数调整

tracker

# 最大并发连接数
max_connections=256
# 接受请求的线程数
accept_threads=1
# 执行请求的线程数 <= max_connections
work_threads=4
# 预留的存储空间 G(GB) M(MB) K(KB) 默认byte(B),% 表示比例,如下,预留的存储空间为10%,即只占用90%
reserved_storage_space = 10%
# 是否使用storage ID,而不是ip地址(建议采用此模式,为以后数据迁移做铺垫)
use_storage_id = false

免责声明:文章转载自《Fastdfs 存储》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Go语言中对图像进行缩放ExtJS 4.2 教程-01:Hello ExtJS下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

转:Nginx 日志文件切割

http://www.cnblogs.com/benio/archive/2010/10/13/1849935.html 偶然发现access.log有21G大,所以将其切割。 Nginx 是一个非常轻量的 Web 服务器,体积小、性能高、速度快等诸多优点。但不足的是也存在缺点,比如其产生的访问日志文件一直就是一个,不会自动地进行切割,如果访问量很大的话...

Hive 元数据库表信息

  Hive 的元数据信息通常存储在关系型数据库中,常用MySQL数据库作为元数据库管理。 1. 版本表   i) VERSION   -- 查询版本信息 2. 数据库、文件存储相关   i) DBS     -- 存储Hive中所有数据库的基本信息   ii) SDS    -- 存储Hive中文件存储的基本信息      3. 表、视图相关   i) ...

python文件和目录操作方法大全

一、python中对文件、文件夹操作时经常用到的os模块和shutil模块常用方法。1.得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd()2.返回指定目录下的所有文件和目录名:os.listdir()3.函数用来删除一个文件:os.remove()4.删除多个目录:os.removedirs(r“c:python”)5.检验给...

海量小文件存储与Ceph实践

  海量小文件存储(简称LOSF,lots of small files)出现后,就一直是业界的难题,众多博文(如[1])对此问题进行了阐述与分析,许多互联网公司也针对自己的具体场景研发了自己的存储方案(如taobao开源的TFS,facebook自主研发的Haystack),还有一些公司在现有开源项目(如hbase,fastdfs,mfs等)基础上做针对...

简单Web UI 自动化测试框架 seldom

pyse 更名为 seldom WebUI automation testing framework based on Selenium and unittest. 基于 selenium 和 unittest 的 Web UI自动化测试框架。 特点 提供更加简单API编写自动化测试。 提供脚手架,快速生成自动化测试项目。 自动生成HTML测试报告生成...

命名空间详解和分类

命名空间是什么 计算机科学领域中是通过名字来使用各种代码资源(变量和子程序)的,命名空间(namespace)则是组织这些资源的一组符号,例如目录树是就硬盘上文件们的命名空间。命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。 如果你还是无法获得命名空间的概念,你只要想...