C/C++编程:FastDFS单机版环境部署

  • Post author:
  • Post category:其他




gcc、g++编译器

apt-get install gcc
apt-get install g++
apt-get install build-essential
apt-get install libtool



Nginx的安装和部署



准备



PCRE库

  • PCRE(Perl Compatible Regular Expressions):perl语言兼容正则表达式。PCRE是一个用C语言编写的正则表达式函数库。PCRE是一个轻量级的函数库,比Boost之中的正则表达式库小得多。PCRE十分易用,同时功能也很强大,性能超过了POSIX正则表达式库和一些经典的正则表达式库
  • PCRE(Perl Compatible Regular Expressions):perl语言兼容正则表达式。PCRE是一个用C语言编写的正则表达式函数库。PCRE是一个轻量级的函数库,比Boost之中的正则表达式库小得多。PCRE十分易用,同时功能也很强大,性能超过了POSIX正则表达式库和一些经典的正则表达式库
  • PCRE被广泛使用在许多开源软件之中,最著名的莫过于Apache HTTP服务器和PHP脚本语言、R脚本语言,此外,正如从其名字所能看到的,PCRE也是perl语言的缺省正则库
  • PCRE是用C语言实现的,其C++实现版本叫PCRE++
wget https://sourceforge.net/projects/pcre/files/pcre/8.44/pcre-8.44.tar.gz
tar -zxvf pcre-8.44.tar.gz
cd pcre-8.44/
./configure
make
make install



zlib库

  • zlib 适用于数据压缩的函式库,由Jean-loup Gailly (负责compression)和 Mark Adler (负责decompression)开发。
  • zlib被设计成一个免费的、通用的、法律上不受阻碍(即没有被任何专利覆盖) 的无损数据压缩库。zlib几乎适用于任何计算器硬件和操作系统。
  • zlib本身的数据格式可以进行跨平台的移植。 与在Unix上适用的LZW压缩方法 以及 GIF 图像压缩不同, zlib中适用的压缩方法从不对数据进行拓展。(LZW在极端情况下会导致文件大小变为原来的两倍、甚至三倍)。zlib的内存占用也是独立于输入数据的,并且在必要的情况下可以适当减少部分内存占用。
wget https://nchc.dl.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz
tar -zxvf zlib-1.2.11.tar.gz
cd zlib-1.2.11/
./configure
make
make install



OpenSSL开发库

  • 在计算机网络上,OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。这个包广泛被应用在互联网的网页服务器上。
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
tar -zxvf openssl-1.1.1g.tar.gz
cd openssl-1.1.1g/
./config
make
make install



Nginx的安装和启动



编译和安装

wget http://nginx.org/download/nginx-1.16.1.tar.gz
tar -zxvf nginx-1.16.1.tar.gz
cd nginx-1.16.1/
./configure --with-http_ssl_module
make
make install



//./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-http_v2_module --with-openssl=../openssl-1.1.1g



启动和关闭

默认情况下,Nginx被安装在目录/usr/local/nginx中

$ cd /usr/local/nginx/
$ ls
conf  html  logs  sbin

其中,nginx的配置文件是conf/nginx.conf,bin文件位于sbin目录下的nginx

1)

默认方式启动nginx服务器

/usr/local/nginx/sbin/nginx

这时,会自动读取配置文件:/usr/local/nginx/conf/nginx.conf

2)

查看nginx进程

ps -ef | grep nginx/
root       93540    1384  0 08:40 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
root       97295    2911  0 08:41 pts/0    00:00:00 grep --color=auto nginx/

可以看到,nginx的主进程已经启动(nginx: master process)

打开浏览器访问此机器的IP,如果浏览器出现 Welcome to nginx! 则表示 Nginx 已经安装并运⾏成功

在这里插入图片描述

3)

指定配置⽂件启动服务器

$ /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

4)

)测试配置信息

$ /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful



FastDFS 安装与配置

版本:

  • libfastcommon 1.0.50
  • fastdfs 6.0.7

中文地址:

https://gitee.com/fastdfs100



安装libfastcommon

libfastcommon是一款开源的C基础库,是从FastDFS项目中剥离出来的C基础库

git clone https://gitee.com/fastdfs100/libfastcommon
cd libfastcommon
git checkout V1.0.50
./make.sh
./make.sh install

注意版本一定要切换到V1.0.50,否则安装 FastDFS时可能出现莫名其妙的错误



安装 FastDFS

git clone https://gitee.com/fastdfs100/fastdfs.git
cd fastdfs
git checkout V6.07
./make.sh
./make.sh install

在这里插入图片描述



配置Tracker

  • Tracker是FastDFS的协调者,负责管理所有的storage server和group
  • 每个storage在启动后会连接Tracker,告知⾃⼰所属的group等信息,并保持周期性的⼼跳,tracker根据storage的⼼跳信息,建⽴group==>[storage server list]的映射表。
# 创建Tracker的存储日志和数据的根目录
mkdir -p /home/fastdfs/tracker
cd /etc/fdf
cp tracker.conf.sample tracker.conf
# 配置tracker.conf
gedit tracker.conf

在这里,tracker.conf只是修改一下tracker存储日志和数据的路径

# 启⽤配置⽂件(默认为 false,表示启⽤配置⽂件)
disabled=false
# Tracker 服务端⼝(默认为 22122)
port=22122
# 存储⽇志和数据的根⽬录
base_path=/home/fastdfs/tracker

主要修改base_path路径。



配置storage

  • Storage server(后简称storage)以组(卷,group或volume)为单位组织,⼀个group内包含多台storage机器,数据互为备份
  • group内每个storage的存储依赖于本地⽂件系统,storage可配置多个数据存储⽬录,⽐如有10块磁盘,分别挂载在/data/disk1-/data/disk10,则可将这10个⽬录都配置为storage的数据存储⽬录。
# 创建 Storage 的存储⽇志和数据的根⽬录
mkdir -p /home/fastdfs/storage
cd /etc/fdfs
cp storage.conf.sample storage.conf
# 配置 storage.conf
gedit  storage.conf

在这⾥,storage.conf 只是修改⼀下 storage 存储⽇志和数据的路径

# 启⽤配置⽂件(默认为 false,表示启⽤配置⽂件)
disabled = false
# Storage服务端口,默认为23000
port = 23000
# 数据和日志文件存储根目录
base_path = /home/fastdfs/storage
# store_path1 则为 M01,以此递增到 M99(如果配置了多个存储⽬录的话,这⾥只指定 1 个)
store_path0=/home/fastdfs/storage
# Tracker 服务器 IP 地址和端⼝,单机搭建时也不要写 127.0.0.1
# tracker_server 可以多次出现,如果有多个,则配置多个
tracker_server=192.168.0.59:22122  #192.168.0.59为本机IP地址
# 设置 HTTP 访问⽂件的端⼝。这个配置已经不⽤配置了,配置了也没什么⽤
# 这也是为何 Storage 服务器需要 Nginx 来提供 HTTP 访问的原因
http.server_port=8888

主要修改:base_path、store_path0、tracker_server



启动 Tracker 和 Storage 服务



启动服务

# 启动 Tracker 服务
# 其它操作则把 start 改为 stop、restart、reload、status 即可。Storage 服务相同
/etc/init.d/fdfs_trackerd start
# 启动 Storage 服务
/etc/init.d/fdfs_storaged start

可以通过对应服务的端⼝查看服务是否正常启动

$ lsof -i:22122
COMMAND      PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
fdfs_trac 132006 root    5u  IPv4 1862830      0t0  TCP *:22122 (LISTEN)
$ lsof -i:23000
COMMAND      PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
fdfs_stor 131914 root    5u  IPv4 1860968      0t0  TCP *:23000 (LISTEN)

可以通过 fdfs_monitor 查看集群的情况

# 查看 Storage 是否已经注册到 Tracker 服务器中
# 当查看到 ip_addr = 192.168.0.59: (localhost.localdomain) ACTIVE
# ACTIVE 表示成功
/usr/bin/fdfs_monitor /etc/fdfs/storage.conf

在这里插入图片描述



查看⽇志

tracker:

  • tail -f /home/fastdfs/tracker/logs/trackerd.log

storage:

  • tail -f /home/fastdfs/storage/logs/storaged.log



上传文件测试

1)修改配置

# 修改 Tracker 服务器客户端配置⽂件
mkdir -p /home/fastdfs/client
cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
gedit /etc/fdfs/client.conf

client.conf 中修改 base_path 和 Tracker 服务器的 IP 地址与端⼝号即可

# 存储⽇志⽂件的基本路径
base_path=/home/fastdfs/client
# Tracker 服务器 IP 地址与端⼝号
tracker_server=192.168.0.59:22122

主要修改base_path和tracker_server

2)开始上传

# 存储到FastDFS服务器中
# 格式:  /usr/bin/fdfs_upload_file 配置⽂件 要上传的⽂件
$ /usr/bin/fdfs_upload_file /etc/fdfs/client.conf ./client.conf
group1/M00/00/00/wKgAO2IgGsaAepcnAAAHdc51W4U54.conf

返回了文件ID号group1/M00/00/00/wKgAO2IgGsaAepcnAAAHdc51W4U54.conf,表示上传成功

进入目录/home/fastdfs/storage/data/00/00,可以看到wKgAO2IgGsaAepcnAAAHdc51W4U54.conf就是我们刚刚上传的文件

$ cd  /home/fastdfs/storage/data/00/00
$ ls
wKgAO2IgGsaAepcnAAAHdc51W4U54.conf



下载文件测试

  1. 下载文件
$ fdfs_download_file /etc/fdfs/client.conf  group1/M00/00/00/wKgAO2IgGsaAepcnAAAHdc51W4U54.conf
$ ls
wKgAO2IgGsaAepcnAAAHdc51W4U54.conf
  1. 删除文件
$ fdfs_delete_file /etc/fdfs/client.conf  group1/M00/00/00/wKgAO2IgGsaAepcnAAAHdc51W4U54.conf

进入目录/home/fastdfs/storage/data/00/00,可以看到wKgAO2IgGsaAepcnAAAHdc51W4U54.conf这个目录没有了

到目前为止,我们已经 完成了FastDFS的安装和测试,可以使用api来完成文件的上传、同步和下载。

接下来,我们还会安装nginx,目的如下:

  • Storage安装nginx:提供http的访问和下载服务,同时解决group中storage服务器的同步延迟问题
  • Tracker安装nginx:提供http访问的反向代理、负载均衡、缓存服务



fastdfs-nginx-module安装



备份原来的nginx

为了避免端口冲突,建议备份原来的nginx服务

$ cd /usr/local
$ /usr/local/nginx/sbin/nginx -s stop
$ mv nginx bk-nginx-20220223



安装

注意:在安装之前,先

确保之前fastDFS的tracker、storage、client可以正常使用(服务必须已经开启)

,否则上传/下载文件时会出现:

ERROR - file: connection_pool.c, line: 140, connect to server 192.168.0.59:22122 fail, errno: 111, error info: Connection refused



下载fastdfs-nginx-module

$ git clone https://github.com/happyfish100/fastdfs-nginx-module.git
$ cd fastdfs-nginx-module
$ ls
HISTORY  INSTALL  LICENSE  src
$ git checkout V1.22
$ pwd
/home/oceanstar/workspace/fastdfs-nginx-module/src

cd fastdfs-nginx-module可以看到里面有个INSTALL和src目录,这个不需要make而是需要重新编译一些storage的nginx模块



重新编译nginx

1)


添加FastDFS模块

$ cd nginx-1.16.1  # 进入之前下载的nginx源目录
$ ./configure --add-module=/home/oceanstar/workspace/fastdfs-nginx-module/src  #是在编译Nginx时候,连同刚刚下载的fastdfs-nginx-module模块⼀起编译

编译时, 出现以下信息表示添加成功

在这里插入图片描述

2)


给 nginx ⽬录下的 objs/Makefile ⽂件中增加头⽂件⽬录

gedit objs/Makefile

添加

ALL_INCS = -I src/core \
-I /usr/include/fastdfs \
-I /usr/include/fastcommon \
-I src/event \
-I src/event/modules \

在这里插入图片描述

3)


重新编译并安装nginx

make
make install



配置fastdfs-nginx-module



拷贝配置文件

1)


拷⻉fastdfs-nginx-module配置⽂件

$ cd /home/oceanstar/workspace/fastdfs-nginx-module/src/
$ ls
common.c  common.h  config  mod_fastdfs.conf  ngx_http_fastdfs_module.c
$ cp mod_fastdfs.conf /etc/fdfs/

2)


拷⻉fastdfs/conf 配置⽂件

$ cd /home/oceanstar/workspace/fastdfs/conf
$ cp http.conf /etc/fdfs/
$ cp mime.types /etc/fdfs/



修改配置文件

$ mkdir -p /home/fastdfs/mod_fastdfs
$ gedit  /etc/fdfs/mod_fastdfs.conf

修改base_path 、tracker_server、url_have_group_name、store_path0。

#保存⽇志⽬录
base_path =/home/fastdfs/mod_fastdfs
# Tracker 服务器IP和端⼝修改,确保跟storage.conf⼀致即可
tracker_server=192.168.0.59:22122
# url 中是否包含 group 名称,改为 true,包含 group
url_have_group_name = true
# store_path0的路径必须和storage.conf的配置⼀致
store_path0=/home/fastdfs/storage
# 其它的⼀般默认即可,例如
group_name=group1
storage_server_port=23000
store_path_count=1



配置nginx

gedit /usr/local/nginx/conf/nginx.conf

配置为⽀持 group0-group9,以及 M00-M99,以便于以后扩容

location ~/group([0-9])/M([0-9])([0-9]) {
	ngx_fastdfs_module;
}

在这里插入图片描述



启动nginx

/usr/local/nginx/sbin/nginx



测试环境

服务器中测试上传

$ touch 0voice.txt
$ echo "You are not strong, no one brave for you" > 0voice.txt
$ fdfs_upload_file /etc/fdfs/client.conf 0voice.txt
group1/M00/00/00/wKgAO2IgJzeANR9sAAAAKTIQHvk110.txt

浏览器中输入: http://192.168.0.59:80/group1/M00/00/00/wKgAO2IgJzeANR9sAAAAKTIQHvk110.txt

在这里插入图片描述



协议格式



协议

FastDFS采用二进制TCP通信协议。一个数据包由包头(header)和包体(body)组成。包头只有10个字节,格式如下:

  • pkg_len:8字节整数,body长度,不包含header,只是body的长度
  • cmd:1字节整数,命令码
  • status:1字节整数,状态码,0表示成功,非0失败(unix错误码)
tracker\tracker_proto.h TrackerHeader
typede fstruct
{
   char pkg_len[FDFS_PROTO_PKG_LEN_SIZE]; // body⻓度,不包括header
   char cmd;   //command 命令
   char status; //status code for response 响应的状态码
} TrackerHeader;
即是头部固定10字节,body⻓度通过pkg_len给出。
即是⼀帧完整的协议为 TrackerHeader + body数据(可以为0.

数据包中的类型说明:

  • 整数类型采用网络字节序(Big-Endian),包括4字节整数和8字节整数
  • 1字节整数不存在字节序问题,在java中直接映射为byte类型,在C/C++中为char类型
  • 固定长度的字符串类型以ASCII码0结尾,对于Java等语言语⾔需要调⽤trim处理返回的字符串。变⻓字符串的⻓度可以直接拿到或者根据包⻓度计算出来,不以ASCII 0结尾。

下⾯将列举client发送给FastDFS server的命令码及其body(包体)结构。



公共命令码

  • FDFS_PROTO_CMD_ACTIVE_TEST:激活测试,通常⽤于检测连接是否有效。客户端使⽤连接池的情况下,建⽴连接后发送⼀次active test即可和server端保持⻓连接。

    • 请求body:⽆
    • 响应body:⽆



发送给tracker server的命令码

  • TRACKER_PROTO_CMD_SERVER_LIST_ONE_GROUP:查看⼀个group状态

    • 请求body:

      • @group_name:16字节字符串,组名
    • 响应body:

      • @

        group_name

        :17字节字符串
      • @

        total_mb

        :8字节整数,磁盘空间总量,单位MB
      • @

        free_mb

        :8字节整数,磁盘剩余空间,单位MB
      • @

        trunk_free_mb

        :8字节整数,trunk⽂件剩余空间,单位MB(合并存储开启时有效)
      • @

        server_count

        :8字节整数,storage server数量
      • @

        storage_port

        :8字节整数,storage server端⼝号
      • @

        storage_http_port

        :8字节整数,storage server上的HTTP端⼝号
      • @

        active_count

        :8字节整数,当前活着的storage server数量
      • @

        current_write_server

        :8字节整数,当前写⼊的storage server顺序号
      • @

        store_path_count

        :8字节整数,storage server存储路径数
      • @

        subdir_count_per_path

        :8字节整数,存储路径下的⼦⽬录数(FastDFS采⽤两级⼦⽬录),如 256
      • @

        current_trunk_file_id

        :8字节整数,当前使⽤的trunk⽂件ID(合并存储开启时有效)



⽂件上传

client\fdfs_test1.c

  1. FDFS_UPLOAD_BY_FILE: storage_upload_by_filename1
  2. FDFS_UPLOAD_BY_BUFF:storage_upload_by_filebuff1
  3. FDFS_UPLOAD_BY_CALLBACK: storage_upload_by_callback1

在这里插入图片描述

在这里插入图片描述

⽂件上传函数层次接⼝

#define storage_upload_by_filename1(pTrackerServer, // tracker server的地
址信息
   pStorageServer, // storage server的地址信息
   store_path_index, // 具体path,⽐如0、1 对应配置⽂件的store_path0、
store_path1
   local_filename, // 要上传的⽂件名
   file_ext_name, // ⽂件类型,⽐如txt、jpg
   meta_list, //⽐如 "width"  "160" 参考:client\fdfs_test1.c 201⾏
   meta_count, // meta_list列表的元素个数
   group_name, // 所属的group名
   file_id) // 上传成功后返回file_id
实际上传调⽤的函数
storage_upload_by_filename1_ex(pTrackerServer, pStorageServer, \
   store_path_index,  
   STORAGE_PROTO_CMD_UPLOAD_FILE, \ // 服务器响应的命令
   local_filename, file_ext_name, meta_list, meta_count, \
   group_name, file_id)
    
继续往下调⽤
int storage_upload_by_filename_ex(ConnectionInfo *pTrackerServer, \
 ConnectionInfo *pStorageServer, const int store_path_index, \
 const char cmd, const char *local_filename, \
 const char *file_ext_name, const FDFSMetaData *meta_list, \
 const int meta_count, char *group_name, char *remote_filename)
    
    
在继续
storage_do_upload_file(pTrackerServer, pStorageServer, \
 store_path_index, cmd, \
 FDFS_UPLOAD_BY_FILE, // 以⽂件的⽅式上传内容,还有FDFS_UPLOAD_BY_BUFF以内存的
载体⽅式上传
 local_filename, \ // 作为⽂件名或者 内存数据的起始地址
 NULL, // arg  
 stat_buf.st_size, // file_size
 NULL, // master_filename
 NULL, // prefix_name
 file_ext_name, \ // file_ext_name
 meta_list,
 meta_count, group_name,
 remote_filename // file_id
 )

调试

对应命令:

/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /etc/fdfs/client.conf

怎么调试:

(1) gdb启动上传⽂件程序:

gdb /usr/bin/fdfs_upload_file

(2) 设置参数:

set args /etc/fdfs/client.conf    /etc/fdfs/client.conf

(3) 在main函数打断点:

b main

(4) 在上传函数断点:

b storage_do_upload_file

(5) 开始运行:

 r

在这里插入图片描述

(6) 令程序进行执行,其会在下一个断点处停止: c

在这里插入图片描述



⽂件下载

client\fdfs_test1.c client\fdfs_download_file.c

  • FDFS_DOWNLOAD_TO_FILE:storage_do_download_file1_ex
  • FDFS_DOWNLOAD_TO_BUFF:storage_download_file1
  • FDFS_DOWNLOAD_TO_CALLBACK:storage_download_file_ex

在这里插入图片描述

在这里插入图片描述

对应命令:

fdfs_download_file /etc/fdfs/client.conf group1/M00/00/00/wKgAO2IgGsaAepcnAAAHdc51W4U54.conf

怎么调试:

(1) gdb启动上传⽂件程序:

gdb /usr/bin/fdfs_download_file 

(2) 设置参数:

set args /etc/fdfs/client.conf  group1/M00/00/00/wKgAO2InXbSAF9vpAAAHdc51W4U06.conf

(3) 打断点:

b main
b storage_do_download_file1_ex

(4) 开始运行:

 r



断点续传

对应命令:

  1. 准备
echo hello > test1.txt
echo world > test2.txt
  1. 上传第一部分
fdfs_upload_appender /etc/fdfs/client.conf test1.txt

得到:group1/M00/00/00/rBMYd2Id2FmEay2-AAAAADY6MCA286.txt ,在fdfs_append_file的时候需要

  1. 上传第二部分
fdfs_append_file /etc/fdfs/client.conf group1/M00/00/00/rBMYd2Id2FmEay2-AAAAADY6MCA286.txt test2.txt
  1. 在服务器相应的⽬录下查找对应的⽂件,⽤cat读取⽂件内容。
root@iZbp1h2l856zgoegc8rvnhZ:/home/fastdfs/storage/data/00/00# cat
rBMYd2Id2FmEay2-AAAAADY6MCA286.txt
hello
world

可以看出,断点续传文件分为两个阶段:

  1. fdfs_upload_appender 上传第⼀部分⽂件,以

    STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE

    命令
  2. fdfs_append_file 上传其他部分的⽂件,以

    STORAGE_PROTO_CMD_APPEND_FILE

    命令

需要注意:

  1. 注意断点续传的顺序性
  2. fastdfs支持断点续传,但是不支持多线程分片上传同一个文件



网络IO模型



对应配置项

# accept thread count
# default value is 1 which is recommended
# since V4.07
accept_threads = 1
# work thread count
# work threads to deal network io
# default value is 4
# since V2.00
work_threads = 4
# if disk read / write separated
## false for mixed read and write
## true for separated read and write
# default value is true
# since V2.00
disk_rw_separated = true
# disk reader thread count per store path
# for mixed read / write, this parameter can be 0
# default value is 1
# since V2.00
disk_reader_threads = 1
# disk writer thread count per store path
# for mixed read / write, this parameter can be 0
# default value is 1
# since V2.00
disk_writer_threads = 1



线程初始化

默认:

g_accept_threads 1
g_work_threads 4
g_disk_reader_threads默认1
g_disk_writer_threads 1

⽂件操作线程: 根据读写线程数量,path数量确定 ⽂件操作线程数量

threads_count_per_path = g_disk_reader_threads + g_disk_writer_threads;
context_count = threads_count_per_path * g_fdfs_store_paths.count;
文件名 作用 初始化函数 工作线程
storage\storage_service.c ⽹络IO任务处理 storage_service_init work_thread_entrance
storage\storage_service.c accept线程 storage_accept_loop accept_thread_entrance
storage\storage_dio.c ⽂件操作相关初始化 storage_dio_init dio_thread_entrance

队列:

  • storage_dio_queue ⽂件处理队列
  • task_queue 任务对象池队列

管道:

  • thread_data.pipe_fds[2]:IO⼯作线程的触发
  • storage_nio_notify 触发取读取⽹络io数据

accept_thread_entrance:

  • 本身是一个线程
  • 调用accept获取新的连接:

    incomesock = accept(server_sock, (struct sockaddr*)&inaddr, &sockaddr_len);
  • 获取对方IP地址:

    client_addr = getPeerIpaddr(incomesock, szClientIp, IP_ADDRESS_SIZE);
  • 从对象池取⼀个task对象:

    pTask = free_queue_pop()
  • task对象里面有client的封装信息,需要设置:



    • pTask->event.fd = incomesock



    • pClientInfo = (StorageClientInfo

      *

      )pTask->arg;



    • pClientInfo->stage = FDFS_STORAGE_STAGE_NIO_INIT;

    • 轮询线程:


      pClientInfo->nio_thread_index = pTask->event.fd % g_work_threads;`

    • 通知IO线程有新的连接:


      write(pThreadData->thread_data.pipe_fds[1], &task_addr, sizeof(task_addr)) != sizeof(task_addr)

work_thread_entrance:

  • 本身是一个线程
  • 核⼼调⽤


    ioevent_loop(&pThreadData->thread_data, storage_recv_notify_read, task_finish_clean_up, &g_continue_flag);



    • storage_recv_notify_read


      当数据可读时触发
  • 进⼊到ioevent_loop核⼼:

    • 实际是调⽤epoll_wait:


      pThreadData->ev_puller.iterator.count = ioevent_poll(&pThreadData->ev_puller);


      返回可处理事件
    • 循环处理事件:deal_ioevents

      • 调⽤


        storage_recv_notify_read

⽂件IO线程dio_thread_entrance:

  • 本身是一个线程
  • 任务由storage_dio_queue_push 投递
  • 从blocked_queue_pop读取任务
  • 通过回调写⼊⽂件dio_write_file 或者 读取⽂件dio_read_file
(gdb) b storage_dio.c:748Breakpoint 1 at 0x55c62441f084: file storage_dio.c, line 748.
(gdb) b client_sock_read
Breakpoint 2 at 0x55c62441e852: file storage_nio.c, line 245.
(gdb) b client_sock_write
Breakpoint 3 at 0x55c62441eb80: file storage_nio.c, line 434.
(gdb) b storage_recv_notify_read
Breakpoint 4 at 0x55c62441ee7f: file storage_nio.c, line 121.
(gdb) b dio_write_file
Breakpoint 5 at 0x55c62441fa46: file storage_dio.c, line 405.
(gdb) b storage_nio_notify
Breakpoint 6 at 0x55c62440adf1: file storage_service.c, line 1918.
(gdb) b storage_upload_file_done_callback
Breakpoint 7 at 0x55c624416da4: file storage_service.c, line 1131.



框架

核⼼函数:

  • accept_thread_entrance accept线程⼊⼝
  • work_thread_entrance nio线程⼊⼝
  • dio_thread_entrance 数据处理线程⼊⼝

在这里插入图片描述

dio线程不会直接给nio线程设置各种读写事件,⽽是通过FDFS_STORAGE_STAGE_NIO_INIT、FDFS_STORAGE_STAGE_NIO_RECV、FDFS_STORAGE_STAGE_NIO_SEND、FDFS_STORAGE_STAGE_NIO_CLOSE、FDFS_STORAGE_STAGE_DIO_THREAD等状态 + 通过pipe通知nio线程响应storage_recv_notify_read进⾏io事件的处理。



数据流程

在这里插入图片描述