0.项目背景
由于 FastDFS 分布式文件存储在上传文件时不保留原文件名,当上传完文件后会返回如下面格式的文件 ID。在文件 ID 中包含了文件所在的组,二级目录,以及由客户端 IP 、时间戳、文件大小生成的 base64 编码文件名。客户端数据库里存储这个着文件 ID ,且只能通过文件 ID 来访问获取文件。如果其他系统想要访问 FastDFS 文件存储就必须从上传客户端保存的数据库中获取该文件的文件 ID 。这样增加了系统的耦合程度,也不利于后续文件存储的迁移和运维。由于 FastDFS 是将文件直接存放在本地磁盘,并不对文件进行分块、合并操作,所以我们可以直接让 nginx 去请求获取本地磁盘上的文件,不经过查询客户端数据库获取文件 ID,无需经过 FastDFS 也可以获取到文件。
文件 ID 的组成
group1
是文件所在组名
M00
是文件所在的 storage 服务器上的分区
00/05
就是文件所在的一级子目录/二级子目录,是文件所在的真实路径
Cgpr6F1A7O6ASWv9AAA-az6haWc850.jpg
是新生成的文件名
文件存储的根目录,由 base_path=
配置参数设定,data 目录为文件存储目录,logs 目录存储日志
文件存储的一级子目录
文件存储的二级子目录
FastDFS 存储真实的文件,不对文件做分块、合并
为方便测试,在这里打开了 nginx
列出目录选项
实现过程
1. 获取原文件名和新生成的文件 ID
在客户端(C语言版)的日志中提取出以下格式的日志,其他版本的客户端可以在数据库中获取,该日志记录了原文件名和上传后由 FastDFS 存储服务生成的文件 ID 。
需要在原文件名前加上一个 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

### 2. 将数据导入 Redis
使用 shell 脚本将原文件名(也可以自定义)作为 KEY ,文件 ID 为 VALUE 导入 Redis 数据库
`awk 'BEGIN{ FS=" "}{arr[$NF]=$1}END{for( k in arr){ cmd=d="redis-cli set "k" "arr[k];system(cmd)}}' url.log`
```bash
# 通过脚本导入
#!/bin/bash
# import data
cat $1 | while read line
do
key=$(echo $line | cut -d ' ' -f2)
value=$(echo $line | cut -d ' ' -f1)
redis-cli set $key $value
done
1 | # 10W 条 K/V 键值对占用不到15MB 内存 |
3. 编译 nginx 加入 lua 和 lua-Redis 模块
3.1.1 编译环境
1 | yum install -y gcc g++ gcc-c++ zlib zlib-devel openssl openssl--devel pcre pcre-devel |
3.1.2 编译 luajit
1 | 编译安装 luajit |
3.1.3 下载 ngx_devel_kit(NDK)模块
1 | wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz |
3.1.4 下载 lua-nginx-module 模块
1 | wget https://github.com/openresty/lua-nginx-module/archive/v0.10.2.tar.gz |
3.1.5 编译 nginx
1 | tar zxvf nginx-1.15.1.tar.gz |
4. 配置 nginx
1 | server { |
5. 测试访问
5.1.1 拼接图片文件的 url
地址
5.1.2 通过浏览器访问
5.1.3 使用 wget 和 xargs 并行下载
6. 不足和改进方案
优势
此方案的好处就是可以从过自定义访问的文件名来获取已经上传的文件,自定义的文件名根据业务的需求来设定。在 nginx location 模块写相应的正则表达式。从而将 FastDFS 与上传客户端解耦,使得访问文件无需依赖 FastDFS 存储,减少运维成本。同时由于使用的是 Redis 数据库和内部转发,对访问的客户端来说是透明的,性能损耗几乎可以忽略不计。
6.1 不足
- 由于 Redis 数据库里的数据需要从客户端日志或数据库中导入,所以无法对 Redis 数据库进行实时更新,如果对上传后的文件进行了修改或删除操作,无法更新到 Redis 数据库中。
- 需要重新编译安装 nginx 加入 lua-nginx 模块、还需要安装 Redis 数据库
6.2 改进
修改 FastDFS 日志输出的内容,添加元文件名字段,根据日志的操作记录对 Redis 进行增删改查
通过源码可知,FastDFS 在日志中记录了文件的操作类型,可以根据这些类型对 Redis 数据库进行增删改查,从而可以监控日志的而输出来对 Redis 数据库进行增删改查。
1
2
3
4
5
6
7
8
9
10//storage access log actions
仔细阅读了 FastDFS storage 模块的源代码后发现, FastDFS 服务端是不保存原文件名的,而且在相应的文件属性结构体里也未包含原文件名。需要修改源码才能将原文件名输出到日志,难度较大。
1 | typedef struct |
通过 FastDFS 日志记录的文件操作类型来实时更新 Redis 数据库