[TOC]
0x00 进阶使用 1.FastDFS 重复文件处理 描述: 由于FastDFS本身不能对重复上传的文件进行去重, 所以使用FastDFS时如果多次上传同一张照片,默认都会将其上传到storage服务器中,这样造成磁盘空间的浪费,所以我们可以采用 FastDHT 进行重复文件处理,以达到同一份图片只留存一份。
FastDHT是一个高性能的分布式哈希系统,它是基于键值对存储的,它可以存储大量键值对,例如文件名映射,会话数据和用户相关数据。但它依赖于Berkeley DB作为数据存储的媒介,使用libevent做网络IO处理, 同时需要依赖于libfastcommon
。
FastDHT 项目地址: https://github.com/happyfish100/fastdht Berkeley DB 下载地址: https://www.oracle.com/database/technologies/related/berkeleydb-downloads.html
FastDHT是一个高性能的分布式哈希系统,它是基于键值对存储的,而且它需要依赖于Berkeley DB作为数据存储的媒介,使用libevent做网络IO处理。同时需要依赖于libfastcommon。
在 FastDHT 中,key 包含三个字段:namespace、object ID 和 key name。这三个概念类似于数据库系统的概念:命名空间与数据库名称、对象 ID 与表名称以及键名称与字段名称
,由于导入了命名空间和对象 ID,系统更加灵活。
命名空间的目的是解决之间可能的数据冲突多个用户,例如不同的应用程序或产品。
对象ID的目的是方便组织和管理 对象相关数据,如用户数据,提高整体性能。
FastDHT 流程: 描述: FastDFS的storage server每次上传均计算文件的hash值,然后从FastDHT服务器上进行查找比对,如果没有返回,则写入hash,并将文件保存;如果有返回,则建立一个新的文件链接(软链),不保存文件。
FastDHT 由客户端决定应该选择哪台服务器,为例避免查表根据key(文件)的hash code
来选择服务器,算法描述如下:
计算出key(文件)的hash值hash_code
group_index = hash_code % group_count
new_hash_code = hash_code 高16位和低16位互换
server_index = new_hash_code % 组内 server_count
为啥计算 server_index
和 group_index
时使用了不同的hash code?
因为如果group_count
和组内server_count
相等,例如都等于2,那么对于一个组来说,任何key值都将选中其中一台固定的服务器server_index == group_index
,所以上述高16位和低16位互换
就是为了解决此问题。
部署流程:
Step 1.Berkeley DB 与 fastdht 下载编译安装 (此处安装环境还是Ubuntu并已经安装了FastDFS)
[TOC]
0x00 进阶使用 1.FastDFS 重复文件处理 描述: 由于FastDFS本身不能对重复上传的文件进行去重, 所以使用FastDFS时如果多次上传同一张照片,默认都会将其上传到storage服务器中,这样造成磁盘空间的浪费,所以我们可以采用 FastDHT 进行重复文件处理,以达到同一份图片只留存一份。
FastDHT是一个高性能的分布式哈希系统,它是基于键值对存储的,它可以存储大量键值对,例如文件名映射,会话数据和用户相关数据。但它依赖于Berkeley DB作为数据存储的媒介,使用libevent做网络IO处理, 同时需要依赖于libfastcommon
。
FastDHT 项目地址: https://github.com/happyfish100/fastdht Berkeley DB 下载地址: https://www.oracle.com/database/technologies/related/berkeleydb-downloads.html
FastDHT是一个高性能的分布式哈希系统,它是基于键值对存储的,而且它需要依赖于Berkeley DB作为数据存储的媒介,使用libevent做网络IO处理。同时需要依赖于libfastcommon。
在 FastDHT 中,key 包含三个字段:namespace、object ID 和 key name。这三个概念类似于数据库系统的概念:命名空间与数据库名称、对象 ID 与表名称以及键名称与字段名称
,由于导入了命名空间和对象 ID,系统更加灵活。
命名空间的目的是解决之间可能的数据冲突多个用户,例如不同的应用程序或产品。
对象ID的目的是方便组织和管理 对象相关数据,如用户数据,提高整体性能。
FastDHT 流程: 描述: FastDFS的storage server每次上传均计算文件的hash值,然后从FastDHT服务器上进行查找比对,如果没有返回,则写入hash,并将文件保存;如果有返回,则建立一个新的文件链接(软链),不保存文件。
FastDHT 由客户端决定应该选择哪台服务器,为例避免查表根据key(文件)的hash code
来选择服务器,算法描述如下:
计算出key(文件)的hash值hash_code
group_index = hash_code % group_count
new_hash_code = hash_code 高16位和低16位互换
server_index = new_hash_code % 组内 server_count
为啥计算 server_index
和 group_index
时使用了不同的hash code?
因为如果group_count
和组内server_count
相等,例如都等于2,那么对于一个组来说,任何key值都将选中其中一台固定的服务器server_index == group_index
,所以上述高16位和低16位互换
就是为了解决此问题。
部署流程:
Step 1.Berkeley DB 与 fastdht 下载编译安装 (此处安装环境还是Ubuntu并已经安装了FastDFS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 tar -zxf db-18.1.40.tar.gz -C /usr/local /src/ mkdir -vp /usr/local /src/db-18.1.40/docs/{bdb-sql,gsg_db_server} cd /usr/local /src/db-18.1.40/build_unix../dist/configure --prefix=/usr/local /berkeley-db make && make install ls /usr/local /berkeley-db rm -rf /usr/local /src/db-18.1.40 cd /usr/local /src/ git clone https://github.com/happyfish100/fastdht.git cd /usr/local /src/fastdht apt-get install libevent-dev vim /usr/local /src/fastdht/make.sh 27 行: CFLAGS='-Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I/usr/local/berkeley-db/include/ -L/usr/local/berkeley-db/lib/' ./make.sh && ./make.sh install ls /usr/local /bin/fdht* ls /etc/fdht/ cp /usr/local /berkeley-db/lib/libdb-18.so /usr/lib/ && cp /usr/local /berkeley-db/lib/libdb-18.so /usr/lib64/ ldconfig
Step 2.fastdht 相关配置文件设置1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 mkdir -vp /home/fdfs/fdht chown -R fdfs:fdfs /home/fdfs/fdht vi /etc/fdht/fdht_client.conf keep_alive=1 base_path=/home/fdfs/fdht group_count = 1 group0 = 10.10.107.225:11411 vi /etc/fdht/fdhtd.conf disabled=false bind_addr=10.10.107.225 port=11411 base_path=/home/fdfs/fdht bash_path= /fastdfs/fastdht store_type = BDB cache_size = 128MB run_by_group=fdfs run_by_user=fdfs
Step 3.在 FastDFS Storage 配置文件中设置接入FastDHT (重点)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 vi /etc/fdfs/storage.conf check_file_duplicate = 1 file_signature_method = hash key_namespace = FastDFS keep_alive = 1
Tips : 非常注意启用include时#include之间不能包含空格(巨坑、巨坑)
Step 4.使用systemd管理fdhtd进程和重启fdhtd、storage服务1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 tee /lib/systemd/system/fdhtd.service <<'EOF' [Unit] Description=FastDHT daemon service After=network-online.target [Service] Type=forking WorkingDirectory=/home/fdfs/fdht PIDFile=/home/fdfs/fdht/data/fdhtd.pid ExecStart=/usr/local /bin/fdhtd /etc/fdht/fdhtd.conf start ExecStop=/usr/local /bin/fdhtd /etc/fdht/fdhtd.conf stop ExecReload=/usr/local /bin/fdhtd /etc/fdht/fdhtd.conf restart TimeoutSec=3 OOMScoreAdjust=-1000 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload ufw allow 11411/tcp /usr/local /bin/fdhtd /etc/fdht/fdhtd.conf stop systemctl restart fdhtd.service && systemctl status fdhtd.service systemctl restart fdfs_storaged.service && systemctl status fdfs_storaged.service $ ps aux | grep "/etc/fd"
Step 5.验证FastDHT文件去重功能。1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@Master:/tmp/test $fdfs_upload_file /etc/fdfs/client.conf bibi.png group1/M00/00/00/Cgpr4mGBS9yABcwKAAFcGln-TU4047.png root@Master:/tmp/test $fdfs_upload_file /etc/fdfs/client.conf bibi.png group1/M00/00/00/Cgpr4WGBS92ASA_jAAFcGkEHz3I407.png root@Master:/tmp/test $fdfs_upload_file /etc/fdfs/client.conf bibi.png group1/M00/00/00/Cgpr4mGBS92APWWCAAFcGh4OjGI465.png root@Master:/tmp/test -rw-r--r-- 1 fdfs fdfs 88K Nov 2 22:28 /home/fdfs/storage/data/00/00/Cgpr4WGBSxaABHlXAAFcGo7c2Jk187.png lrwxrwxrwx 1 fdfs fdfs 64 Nov 2 22:31 /home/fdfs/storage/data/00/00/Cgpr4WGBS92ASA_jAAFcGkEHz3I407.png -> /home/fdfs/storage/data/00/00/Cgpr4WGBSxaABHlXAAFcGo7c2Jk187.png lrwxrwxrwx 1 fdfs fdfs 64 Nov 2 22:31 /home/fdfs/storage/data/00/00/Cgpr4mGBS9yABcwKAAFcGln-TU4047.png -> /home/fdfs/storage/data/00/00/Cgpr4mGBSxWAV58oAAFcGo7c2Jk916.png lrwxrwxrwx 1 fdfs fdfs 64 Nov 2 22:31 /home/fdfs/storage/data/00/00/Cgpr4mGBS92APWWCAAFcGh4OjGI465.png -> /home/fdfs/storage/data/00/00/Cgpr4mGBSxWAV58oAAFcGo7c2Jk916.png
weiyigeek.top-FastDHT重复处理
至此完毕!
上面是单实例的FastDHT环境搭建,在实际企业环境中我们可以根据需求搭建 FastDHT 集群。
FastDHT 集群由一个或多个组(group)组成,每个组由一台或多台服务器组成,同组服务器上存储的数据是相同的,数据同步只在同组的服务器之间进行。组内各个服务器是对等的,对数据进行存取时,可以根据key的hash值来决定使用哪台服务器。数据同步采用推(Push)的方式,由源服务器主动将数据同步到本组的其他服务器。
Step 1.FastDHT 集群的安装非常的简单,按照上面单实例流程进行安装宁外一台FastDHT服务,然后分别修改两台主机的fdht_servers.conf
,重启后即可使用。1 2 3 4 5 6 7 8 9 10 11 12 vim /etc/fdhtd/fdht_servers.conf group_count = 2 group0 = 10.10.107.225:11411 group0 = 10.10.107.226:11411 /usr/local /bin/fdhtd /etc/fdht/fdhtd.conf stop systemctl restart fdhtd.service && systemctl status fdhtd.service systemctl restart fdfs_storaged.service && systemctl status fdfs_storaged.service
Step 2.验证FastDHT 集群的去重功能是否正常。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 root@elk2:/tmp hello.logs root@Slave:/tmp group1/M00/00/00/Cgpr4WGBUTmAKPrOAAAAHW1scFk00.logs group1/M00/00/00/Cgpr4WGBWruAVSl2AAAAHUTG_1091.logs root@elk2:/tmp lrwxrwxrwx 1 fdfs fdfs 64 Nov 2 22:54 /home/fdfs/storage/data/00/00/Cgpr4WGBUTmAKPrOAAAAHW1scFk00.logs -> /home/fdfs/storage/data/00/00/Cgpr4WGBUTmAH106AAAAHdiEv7475.logs lrwxrwxrwx 1 fdfs fdfs 64 Nov 2 22:54 /home/fdfs/storage/data/00/00/Cgpr4WGBWruAVSl2AAAAHUTG_1091.logs -> /home/fdfs/storage/data/00/00/Cgpr4WGBUTmAH106AAAAHdiEv7475.logs -rw-r--r-- 1 fdfs fdfs 29 Nov 2 22:54 /home/fdfs/storage/data/00/00/Cgpr4WGBUTmAH106AAAAHdiEv7475.logs root@elk2:/tmp root@elk2:/tmp -rw-r--r-- 1 root root 29 Nov 2 22:55 Cgpr4WGBUTmAKPrOAAAAHW1scFk00.logs -rw-r--r-- 1 root root 29 Nov 2 22:48 hello.logs root@elk2:/tmp Hello World,FastDFS! By Test
Tips : FastDFS不会返回原始文件的索引,返回的全部都是软链接,当所有的软链接都被删除的时候,原始文件也会从FastDFS中被删除。
扩展说明: 使用fdht提供的相关工具操作FastDHT服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 fdht_set /etc/fdht/fdht_client.conf database:user01 name='WeiyiGeek' ,age=18; fdht_get <config_file> [namespace:][object id] <key list split by comma> fdht_get /etc/fdht/fdht_client.conf database:user01 name,age; fdht_delete /etc/fdht/fdht_client.conf database:user01 name;
至此安装搭建配置、验证测试完成!
2.FastDFS 原始文件名恢复 描述: 当其它文档被上传到FastDFS后,Storage服务端将返回的文件索引(FID),其中文件名是根据FastDFS自定义规则重新生成的而不是原始文件名。
例如: 在使用浏览器访问 http://file.weiyigeek.top/group2/M00/00/89/eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.docx
, 显示给用户的文件名会是这样的eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.docx 那FastDFS在进行文件下载时如何恢复原始文件名称。
操作流程:
Step 1.应用系统在上传文件到FastDFS成功时将原始文件名以及”文件索引(FID)”都保存下来(例如:保存到数据库)。
Step 2.用户点击下载的时用Nginx的域名和FID拼出url,然后在url后面增加一个参数,指定原始文件名。 例如:http://file.weiyigeek.top/group2/M00/00/89/eQ6h3FKJf_PRl8p4AUz4wO8tqaA688.docx?attname=filename.docx
Step 3.然后在Nginx上进行如下配置此时Nginx就会截获url中的参数attname,并且在Http响应头中进行展示Content-Disposition "attachment;filename=$arg_attname"
1 2 3 4 5 6 location /group1/M00 { if ($arg_attname ~* \.(doc|docx|txt|pdf|zip|rar|xls|xlsx|png|jpeg|jpg)$) { add_header Content-Disposition "attachment;filename=$arg_attname " ; } ngx_fastdfs_module; }
Tips : 上面脚本主要功能是通过正则匹配文件后缀,然后将下载文件名改为路径参数attname的值,并返回给客户端。
Step 4.现在要将下载后的文件名称由Cgpr4WGCMMOAamL7AAApDWjxgXI44.xlsx
改为设备清单.xlsx
, 访问如下下载路径格式即可http://file.weiyigeek.top/group1/M00/00/00/Cgpr4WGCMMOAamL7AAApDWjxgXI44.xlsx?attname=设备清单.xlsx
1 2 3 4 5 6 7 8 9 10 HTTP/1.1 200 OK Server: nginx/1.20.1 Date: Wed, 03 Nov 2021 06:53:42 GMT Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Content-Length: 10509 Last-Modified: Wed, 03 Nov 2021 06:48:35 GMT Connection: keep-alive Accept-Ranges: bytes Content-Disposition: attachment;filename=%E8%AE%BE%E5%A4%87%E6%B8%85%E5%8D%95.xlsx
weiyigeek.top-FastDFS下载文件重命名
3.FastDFS 资源防盗链功能 描述: 前面使用nginx支持http方式访问文件,但所有人都能直接访问这个文件服务器,所以有些场景我们需要做一下权限控制。
FastDFS 的权限控制是在服务端开启token验证, 客户端根据文件名、当前unix时间戳、秘钥获取token,然后在地址中带上token参数即可通过http方式访问文件。
操作流程:
(1) 服务端开启token验证, 我们修改/etc/fdfs/http.conf
配置文件。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 vim /etc/fdfs/http.conf http.default_content_type = application/octet-stream http.mime_types_filename = mime.types http.anti_steal.check_token=true http.anti_steal.secret_key=WeiyiGeek.top http.anti_steal.token_ttl=1800 http.anti_steal.token_check_fail = /home/yuqing/fastdfs/conf/anti-steal.jpg http.multi_range.enabed = true
(2) 拷贝Token检查失败时显示的图片以及重启Nginx服务。1 2 cp /usr/local /src/fastdfs/conf/anti-steal.jpg /usr/local /nginx/html/anti-steal.jpg systemctl restart nginx
(3) 客户端生成Token需要文件名称、当前时间以及httpSecretKey参数1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static String getToken (String filepath, String httpSecretKey) { int ts = (int ) Instant.now().getEpochSecond(); String token = "null" ; try { token = ProtoCommon.getToken(getFilename(filepath), ts, httpSecretKey); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (MyException e) { e.printStackTrace(); } StringBuilder sb = new StringBuilder(); sb.append("token=" ).append(token); sb.append("&ts=" ).append(ts); return sb.toString(); }
(4) 此时访问文件需要带上生成的token以及unix时间戳,例如http://file.weiyigeek.top/group1/M00/00/00/wKgzgFnkaXqAIfXyAAEoRmXZPp878.jpeg?token=078d370098b03e9020b82c829c205e1f&ts=1508141521
注意事项:
Tips: 如果生成的token验证无法通过,请进行如下两项检查:
(1) 确认调用token生成函数(ProtoCommon.getToken),传递的文件ID中没有包含group name。传递的文件ID格式形如:M00/00/00/wKgzgFnkTPyAIAUGAAEoRmXZPp876.jpeg (2) 确认服务器时间基本是一致的,注意服务器时间不能相差太多,不要相差到分钟级别。
Tips: 如果系统文件隐私性较高,可以直接通过fastdfs-client
提供的API去访问即可,不用再配置Nginx走http访问。配置Nginx的主要目的是为了快速访问服务器的文件(如图片),如果还要加权限验证,则需要客户端生成token,其实已经没有多大意义, 但是防止爬虫还是非常不错的。
4.FastDFS 从文件的使用技巧 描述: 当时使用FastDFS存储一个图片的多个分辨率的备份时,希望只记录源图的FID,并能将其它分辨率的图片与源图关联,此时可以使用从文件方法。
主从文件是指文件ID有关联的文件,一个主文件可以对应多个从文件。
主文件ID = 主文件名 + 主文件扩展名 从文件ID = 主文件名 + 从文件后缀名 + 从文件扩展名
以本场景为例:主文件为原始图片,从文件为该图片的一张或多张缩略图。
流程说明:
先上传主文件(即:原文件),得到主文件FID。
然后上传从文件(即:缩略图),指定主文件FID和从文件后缀名,上传后得到从文件FID。
java伪代码,如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 private static Logger logger = Logger.getLogger(FastDFSUtils.class);static { try { ClientGlobal.init("D:/WorkSpace/app-filesystem/conf/fdfs_client.conf" ); } catch (Exception e) { throw new RuntimeException(e); } } public static String uploadFile (String filePath) throws Exception { String fileId = "" ; String fileExtName = "" ; if (filePath.contains("." )) { fileExtName = filePath.substring(filePath.lastIndexOf("." ) + 1 ); } else { logger.warn("Fail to upload file, because the format of filename is illegal." ); return fileId; } try { fileId = client.upload_file1(filePath, fileExtName, null ); } catch (Exception e) { logger.warn("Upload file \"" + filePath + "\"fails" ); }finally { trackerServer.close(); } return fileId; } public static String uploadSlaveFile (String masterFileId, String prefixName, String slaveFilePath) throws Exception { String slaveFileId = "" ; String slaveFileExtName = "" ; if (slaveFilePath.contains("." )) { slaveFileExtName = slaveFilePath.substring(slaveFilePath.lastIndexOf("." ) + 1 ); } else { logger.warn("Fail to upload file, because the format of filename is illegal." ); return slaveFileId; } try { slaveFileId = client.upload_file1(masterFileId, prefixName, slaveFilePath, slaveFileExtName, null ); } catch (Exception e) { logger.warn("Upload file \"" + slaveFilePath + "\"fails" ); }finally { trackerServer.close(); } return slaveFileId; } public static int download (String fileId, String localFile) throws Exception { int result = 0 ; TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null ; StorageClient1 client = new StorageClient1(trackerServer, storageServer); try { result = client.download_file1(fileId, localFile); } catch (Exception e) { logger.warn("Download file \"" + localFile + "\"fails" ); }finally { trackerServer.close(); } return result; } public static void main (String[] args) { try { String masterFileId = uploadFile("D:/Tmp/apk/t01134ede0e696735e7.png" ); System.out.println(masterFileId); download(masterFileId, "D:/Tmp/apk/master.png" ); String slaveFileId = uploadSlaveFile(masterFileId, "_120x120" , "D:/Tmp/apk/PC.png" ); System.out.println(slaveFileId); download(slaveFileId, "D:/Tmp/apk/slave.png" ); } catch (Exception e) { logger.error("upload file to FastDFS failed." , e); } } }
上面代码运行后打印的文件Id为:1 2 主文件:group1/M00/00/00/wKhbylJx1zkIAAAAAAApPcQL87AAAAAAQCmDxUAAClV522.png 从文件:group1/M00/00/00/wKhbylJx1zkIAAAAAAApPcQL87AAAAAAQCmDxUAAClV522_120x120.png
Tips : FastDFS中的主从文件只是在文件ID上有联系。FastDFS server端没有记录主从文件对应关系,因此删除主文件,FastDFS不会自动删除从文件。删除主文件后,从文件的级联删除,需要由应用端来实现(程序后端)。
参考地址: http://www.ttlsa.com/fastdfs/fastdfs-experience-sharing
0x01 容灾恢复 1.主从同步异常恢复测试 描述: 假如此时 10.10.107.225
主 tracker、storage
由于异常退出, 此时10.10.107.226
正常运行,此时当我们想把文件上传到fastdfs时,它将自动通过/etc/fdfs/client.txt
中配置的tracker server
地址,选择一个可用的tracker server
地址进行文件上传,并在主 storage 节点服务恢复后自动同步到其storage数据目录中。
演示流程: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 systemctl stop fdfs_storaged.service fdfs_trackerd.service fdfs_monitor /etc/fdfs/client.conf fdfs_upload_file /etc/fdfs/client.conf ip.txt Slave$ ls /home/fdfs/storage/data/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt /home/fdfs/storage/data/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt Master$ ls /home/fdfs/storage/data/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt ls: cannot access '/home/fdfs/storage/data/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt' : No such file or directory systemctl start fdfs_storaged.service fdfs_trackerd.service Master$ cat /home/fdfs/storage/data/sync/binlog.000 1635728859 c M00/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt Master$ find /home/fdfs/storage/data/ -name *.png -ctime -0.5 -exec ls -lh {} \; -rw-r--r-- 1 fdfs fdfs 295K Oct 31 16:08 /home/fdfs/storage/data/00/00/Cgpr4WF_PduAOZWzAAACU_pTy9w129.txt
2.集群数据整体迁移(思路) 描述: 当我们系统需要从一个地方迁移到另一个地方并且需要数据保持不变,假如此时ip地址和网络情况不一样了,通常我会将FastDFS存储的目录整体拷贝过去,该方法简单粗暴且有效,这样文件在文件系统中的位置也不会发生变化,访问文件时文件地址只需要修改为迁移后的ip即可。
解决方案流程:
Step 1.在需要迁移的那边用同样的docker镜像构建FastDFS文件系统,容器映射目录、http访问端口尽量保持不变。
Step 2.首先将tracker目录下的data文件夹直接拷贝过去,覆盖新的文件系统中的tracker中的data目录,进行如下的修改: 1 2 3 4 storage_groups_new.dat storage_servers_new.dat storage_sync_timestamp.dat
Step 3.其次将storage目录下的data文件夹之际拷贝过去,覆盖新的文件系统中的storage中的data目录,进行如下的修改(可使用ll -a命令查看隐藏文件
):1 2 3 ls data/ .data_init_flag sync/${IP_addr} _${port} .mark
将上述文件中的旧ip改为新地址的ip即可。
Step 4.将上面都修改完成之后,启动集群,关闭防火墙,在新的服务器环境下通过url访问文件系统中的文件,即可获取到文件。
Step 5.假如迁移前后ip地址和端口不变只是系统进行更换时,我们配置好服务后将可直接将data目录拷贝过去,不需要修改任何信息。
0x02 测试优化 3.1 测试工具 描述: 我们采用 fastdfs 客户端命令 fdfs_upload_file 和 xargs 并发执行工具,上传测试样本 10W 张表情包图片 ,大小在 8KB–128KB 之间,我们分别进行上传和下载测试。
1 2 3 4 5 6 pwd time ls | xargs -n 1 -I {} -P 256 sh -c "/usr/bin/fdfs_upload_file /etc/fdfs/client.conf {}" time cat url.log | xargs -n 1 -I {} -P 256 sh -c "wget {}"
测试结果 使用 FastDFS 自带的上传测试工具和 xargs 并发执行工具,通过 xargs -P 参数指定的并发请求数,测得结果为单机性能在网络环境稳定的情况下可以达到 5000 并发上传请求。10W 张图片上传时间耗时 2 分 11 秒左右。使用定时脚本持续测试,总测试上传 100W 张图片。分析 tracker 服务器和 storage 服务器的日志,无论上传还是下载均未发现错误和异常,性能和稳定性较好。
3.2 优化参数 描述: 根据业务需求和线上环境调整一下参数,可充分发挥 FastDFS 文件系统的性能.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 # 接收请求的线程数数量 accept_threads=1 # work thread count, should <= max_connections # default value is 4 # since V2.00 # 工作线程数量,应当小于等于最大连接数 work_threads=4 # min buff size # default value 8KB # 最小缓冲大小,默认值为 8KB min_buff_size = 8KB # max buff size # default value 128KB # 最大缓冲大小,默认值为 128KB max_buff_size = 128KB # thread stack size, should >= 64KB # default value is 256KB # 线程栈的大小,应当大于 64KB,默认为 256KB thread_stack_size = 256KB # if use connection pool # default value is false # since V4.05 # 是否使用连接池 use_connection_pool = false # connections whose the idle time exceeds this time will be closed # unit: second # default value is 3600 # since V4.05 # 连接池的最大空闲时间 connection_pool_max_idle_time = 3600
0x0n 入坑&出坑 问题1.在启动Fastdfs时Storage启动报one MUST be an inner IP and another is a outer IP, or two different types of inner IP addresses
错误!
错误信息:1 2 3 ERROR - file: ../client/client_func.c, line: 168, conf file: /etc/fdfs/storage.conf, tracker_s erver is invalid, error info: invalid ip addresses 10.10.107.225 and 10.10.107.226, one MUST be an inner IP and another is a outer IP, or two different types of inner IP addresses [2021-10-30 15:27:42] CRIT - exit abnormally!
错误原因: 由于/etc/fdfs/storage.conf
中的 stracker_server = 10.10.107.225,10.10.107.226:22122
配置了同一个网段的地址,而非一个是内部地址,另外一个是外部地址。
解决办法:1 2 3 tracker_server = 10.10.107.225:22122 tracker_server = 10.10.107.226:22122
问题2.tracker端工具链接storage时报无法连接到 storage 服务器
错误信息:1 2 3 [2019-07-25 10:40:54] ERROR - file: ../client/storage_client.c, line: 996, fdfs_recv_response fail, result: 107 upload file fail, error no: 107, error info: Transport endpoint is not connected [2019-07-25 10:40:54] ERROR - file: tracker_proto.c, line: 37, server: 10.20.172.192:23000, recv data fail, errno: 107, error info: Transport endpoint is not connected
解决办法: 首先是在 tracker.conf 配置文件中添加允许访问的 IP ,其次是系统添加防火墙规则,例如: ufw allow 23000/tcp
问题3. 当 storage 服务器磁盘存储空间用尽时报tracker_query_storage fail, error no: 28, error info: No space left on device
错误。
错误信息: 1 2 3 [2019-07-26 16:16:45] ERROR - file: tracker_proto.c, line: 48, server: 10.10.107.232:22122, response status 28 != 0 [2019-07-26 16:16:45] ERROR - file: ../client/tracker_client.c, line: 907, fdfs_recv_response fail, result: 28 tracker_query_storage fail, error no: 28, error info: No space left on device
错误原因: 当 storage 服务器设定的上传存储目录所在的分区磁盘用尽将会出现无剩余空间的错误日志
解决办法: 增加 storage 存储磁盘空间或者临时解决办法设置tracker配置文件中的reserved_storage_space = 10%
参数。
问题4.当 storage 服务器磁盘存储空间用尽时报bind port 22122 failed, errno: 98, error info: Address already in use.
错误
错误信息: 使用service fdfs_trackerd restart
重启 tracker 或者 storage 服务时会报错,会提示端口已经占用。1 2 3 [2019-07-25 10:36:55] INFO - FastDFS v5.12, base_path=/home/dfs, run_by_group=, run_by_user=, connect_timeout=10s, network_timeout=60s, port=22122, bind_addr=, max_connections=1024, accept_threads=1, work_threads=4, min_buff_size=8,192, max_buff_size=131,072, store_lookup=2, store_group=, store_server=0, store_path=0, reserved_storage_space=10.00%, download_server=0, allow_ip_count=-1, sync_log_buff_interval=10s, check_active_interval=120s, thread_stack_size=256 KB, storage_ip_changed_auto_adjust=1, storage_sync_file_max_delay=86400s, storage_sync_file_max_time=300s, use_trunk_file=0, slot_min_size=256, slot_max_size=16 MB, trunk_file_size=64 MB, trunk_create_file_advance=0, trunk_create_file_time_base=02:00, trunk_create_file_interval=86400, trunk_create_file_space_threshold=20 GB, trunk_init_check_occupying=0, trunk_init_reload_from_binlog=0, trunk_compress_binlog_min_interval=0, use_storage_id=0, id_type_in_filename=ip, storage_id_count=0, rotate_error_log=0, error_log_rotate_time=00:00, rotate_error_log_size=0, log_file_keep_days=0, store_slave_file_use_link=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s [2019-07-25 10:36:55] ERROR - file: sockopt.c, line: 868, bind port 22122 failed, errno: 98, error info: Address already in use. [2019-07-25 10:36:55] CRIT - exit abnormally!
解决办法:1 2 3 4 5 /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf stop /usr/bin/fdfs_storaged /etc/fdfs/storage.conf stop
问题5.当启动storage服务器报fdht_client/fdht_func.c, line: 361, invalid group count: 0 <= 0!
错误
错误信息:1 2 2021-10-30 23:06:15] ERROR - file: fdht_client/fdht_func.c, line: 361, invalid group count: 0 <= 0! [2021-10-30 23:06:15] CRIT - exit abnormally!
错误原因: 由于 fast_dht 未正确配置或者启动导致。
解决办法: 在 storage.conf 中进行配置禁用重复文件校验。1 2 3 check_file_duplicate = 0 file_signature_method = hash keep_alive = 0
问题6.启动 Nginx 时报 load conf file "/etc/fdfs/mod_fastdfs.conf" fail, ret code: 2
错误
错误信息:1 2 3 4 [2021-10-30 21:51:31] ERROR - file: shared_func.c, line: 1214, file /etc/fdfs/mod_fastdfs.conf not exist [2021-10-30 21:51:31] ERROR - file: /usr/local /src/fastdfs-nginx-module/src/common.c, line: 163, load conf file "/et c/fdfs/mod_fastdfs.conf" fail, ret code: 22021/10/30 21:51:31 [alert] 1302417
解决办法: 将拉取的fastdfs-nginx-module项目中的mod_fastdfs.conf复制到/etc/fdfs中。1 cp /usr/local /src/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/mod_fastdfs.conf
问题7.启动 Nginx 时报 the "user" directive makes sense only if the master process runs with super-user privileges
警告
错误信息:1 2 3 4 $ more /usr/local /nginx/logs/error.log 2021/10/30 21:51:31 [warn] 1302406 er privileges, ignored in /usr/local /nginx/conf/nginx.conf:2 ngx_http_fastdfs_process_init pid=1302418
错误原因: 由于我是采用systemd管理的nginx服务而我在其unit service文件中指定一个与nginx.conf配置文件中的相同的低权限用户。
解决办法: 在 /usr/lib/systemd/system/nginx.service
权限指定的 User 和 Group 字段或者将其设置root。
问题7.在进行FastDHT
安装配置时安装berkeley-dbb报错cp: cannot stat 'bdb-sql': No such file or directory
。
问题信息:1 2 3 4 cp: cannot stat 'bdb-sql' : No such file or directory cp: cannot stat 'gsg_db_server' : No such file or directory Makefile:1307: recipe for target 'install_docs' failed make: *** [install_docs] Error 1
问题原因: 18.1.40 版本的BUG
解决办法:1 2 3 4 5 6 7 8 9 10 11 12 https://download.oracle.com/otn/berkeley-db/db-18.1.32.tar.gz tar -xzvf db-18.1.32.tar.gz tar -xzvf db-18.1.40.tar.gz cp -rp db-18.1.32/docs/bdb-sql db-18.1.40/docs/ cp -rp db-18.1.32/docs/gsg_db_server db-18.1.40/docs/ mkdir -vp /usr/local /src/db-18.1.40/docs/{bdb-sql,gsg_db_server}
错误参考: https://stackoverflow.com/questions/64707079/berkeley-db-make-install-fails-on-linux
问题8.在进行FastDHT
安装配置时报fatal error: db.h: No such file or directory
错误
问题信息:1 2 3 4 5 6 cc -Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -g -O -DDEBUG_FLAG -c -o db_op.o db_op.c -I../common -I/usr/include/fastcommon -I/usr/local /include db_op.c:9:10: fatal error: db.h: No such file or directory 9 | | ^~~~~~ compilation terminated. make: *** [Makefile:25: db_op.o] Error 1
解决办法:1 2 3 4 5 6 7 8 vim /usr/local /src/fastdht/make.sh 27 行: CFLAGS='-Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I/usr/local/berkeley-db/include/ -L/usr/local/berkeley-db/lib/' cp /usr/local /berkeley-db/lib/libdb-18.so /usr/lib/ && cp /usr/local /berkeley-db/lib/libdb-18.so /usr/lib64/ ldconfig
问题9.在启动FastDHT后又重启FastDFS的storage服务时报dht_client/fdht_func.c, line: 361, invalid group count: 0 <= 0
错误
错误信息:1 2 3 more /home/fdfs/storage/logs/storaged.log [2021-11-02 21:17:23] ERROR - file: fdht_client/fdht_func.c, line: 361, invalid group count: 0 <= 0! [2021-11-02 21:17:23] CRIT - exit abnormally!
错误原因: 通常两个原因,FastDHT 未配置成功(需要进一步验证是否正常运行),到成功运行工作时,验证storage接入FastDHT的参数是否有误。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ cat /etc/fdht/fdht_servers.conf group_count = 1 group0 = 10.10.107.225:11411 $ cat /etc/fdht/fdht_servers.conf ... check_file_duplicate = 1 file_signature_method = hash key_namespace = FastDFS keep_alive = 1 ...