图床项目总结

1. fastdfs 、nginx 文件管理模块

1. 1 fastdfs 三大组件

1.1.1 tracker server

相当于一个调度器 ,其内部不存储文件,只存储storage 服务器相关的一些元信息(存在于内存中) ,通过连接storage后由storage汇报的信息 生成的,根据这些信息 组成一个 group --> storage 的关联信息。

当需要上传文件的时候,

  1. 分配goup : 客户端先访问tracker ,tracker根据负载均衡的策略(比如:某个剩余空间大的storage服务器),分配一个group;
  2. 分配storage :然后再到group中选择一个storage 并给出path
  3. 客户端访问storage: 客户端将向storage发送写文件请求,storage将会为文件分配一个数据存储目录

配置tracker : 只需要配置datalogs路径!

1.1.2 storage server

真正存储文件的服务器,其中经过tracker的分配,会有一个逻辑上的组:group ,每个group中含有多个storage互为备份 ,当上传文件的时候选择其中一个上传,其余storage后续通过binlog日志进行同步 。一般内部会有 256*256个子目录 去存储文件。比如 00/00/ 00/01等。
当文件上传的时候

  1. 记录Fileid :storage会为文件生成一个Fileid,由:storage server ip、文件创建时间、文件大小、文件crc32和一个随机数拼接而成,然后将这个二进制串进行base64编码,转换为可打印的字符串。
  2. 存储到两级目录: 每个存储目录下有两级256*256的子目录,storage会按文件fileid进行两次hash(猜测),路由到其中一个子目录,然后将文件以fileid为文件名存储到该子目录下
  3. 生成文件名: 当文件存储到某个子目录后,即认为该文件存储成功,接下来会为该文件生成一个文件名,文件名由:group、存储目录、两级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

    配置storage: ① 需要设置好data的存储位置;② 需要设置好M00的映射地址(真正的物理地址)

1.1.3 client

提供的一个客户端,相当于需要往fastdfs中输入一些指令 upload xx xxdownload xx xx

1.1.4 fastdfs-nginx-module 下载文件

该模块是fastdfs中长期使用的稳定模块,而且配置文件的路径和storage一致,这个模块的作用

  1. 如果没有此模块:
    • 后端从数据库取出文件ID: 如 group1/M00/00/00/xxx.txt
    • 后端连接Tracker : 调用指令获取文件所在的地址
    • 后端发送读取请求,获取文件流: 调用指令读取文件
    • 后端返回文件流给前端
  2. 如果有此模块:
    • 后端只返回一个 HTTP URL 给前端: http://10.0.0.1:80/group1/M00/00/00/xxx.txt
    • 前端直接使用URL下载: 前端直接使用这个 URL 下载(<a 标签或 window.open),流量直接从 Nginx 流向客户端,不经过业务后端

1. 2 nginx 模块

nginx-upload-module用于上传文件
如果没有该模块,则上传文件的二进制内容 全都要放在请求体中数据解析困难
引入该模块之后,便可以在nginx层面解析multipart/form-data ,将文件保存到临时目录 ,然后生成包含文件信息的变量 (如$upload_tmp_path等),再将请求转发到后端 (如/api/upload),后端从变量或环境变量中获取文件路径来处理

2. 文件上传

2.0 文件秒传

其实就是根据MD5,查看file_info中是否已经存在相同MD5的文件,如果已经存在则再去查user_file_list中有没有,如果没有则添加一条记录,并且把file_info的引用计数 +1。
如果秒传失败,则触发2.1

2.1 上传

经过nginx-upload-file 模块的recv的数据是和前端调用axios后发送的有所不同!

  1. content解析: 其中content 以一个WebKitFormBoundaryjWE3qXXORSg2hZiB分割,进行filename、filetype、md5等信息的解析;
  2. 文件名拼接: 在经过nginx模块上传后,文件统一去掉了后缀名,这里找到type和filename进行拼接即可;
  3. fastdfs命令上传: 用到了进程间通信 ,需要执行fdfs_upload_file(指令) /etc/fdfs/client.conf(配置文件路径) 123.txt(文件) ,此步骤完成后可以获取到 group1/M00/00/00/xxx.txt
    • pipe(int fd[2]) : 创建一个无名管道,其中fd[0] 表示读端 ,fd[1] 表示写端;
    • pid_t pid = fork() : 创建子进程,子进程会完全复制父进程的地址空间,但是区分pid;
    • 子进程: pid = 0 则说明是子进程
      • 关闭读端,保留写端;
      • 将标准输出重定向到写管道
    • 父进程: pid = 1 则说明是父进程
      • 关闭写端,保留读端;
      • 设置读取数据的端 read(fd[0], fileid, TEMP_BUF_MAX_LEN) ,读取数据到fileid中
    • wait(NULL)/close(fd[0]) : 等待子进程结束,并关闭挥手
  4. 完整路径:
    • 依旧使用父子进程方式,执行命令fdfs_file_info ,获取ip
    • 拼接 http:// + ip + group1/M00/00/00/xxx.txt
  5. 数据库持久化: 将上传文件信息插入表做持久化,file_info 插入一条,user_file_list 插入一条;

父子间进程通信的原理: 可以使用popen() 是在fork()、pipe()之上的封装

fork()创建子进程后,共享父进程的数据 (也就是说父子进程皆可以访问管道的读写端,持有其fd ),如果是在此之前创建了pipe() ,那么父子进程都会有pipe读端和写端的引用如果写端引用不为0 ,则无法在读端读取数据,因此需要关闭子进程的读和父进程的写!

2. 注册登录

相关推荐
Rabbit_QL2 小时前
【HTTP协议解读】01小白篇:理解互联网的通信语言HTTP
网络·网络协议·http
liulilittle2 小时前
LINUX RING BUFFER TUN/TAP 2
linux·运维·服务器·开发语言·网络·c++
@insist1234 小时前
软件设计师-网络层核心知识全解:广域网协议、TCP/IP 体系与 IP 地址规划
网络·网络协议·tcp/ip·软考·软件设计师·软件水平考试
旺仔.29111 小时前
Linux 信号详解
linux·运维·网络
源远流长jerry14 小时前
在 Ubuntu 22.04 上配置 Soft-RoCE 并运行 RDMA 测试程序
linux·服务器·网络·tcp/ip·ubuntu·架构·ip
虾..15 小时前
UDP协议
网络·网络协议·udp
w-w0w-w15 小时前
Unix网络编程
服务器·网络·unix
未知鱼16 小时前
Python安全开发之子域名扫描器(含详细注释)
网络·python·安全·web安全·网络安全
寂柒16 小时前
序列化与反序列化
linux·网络