数据库架构之【FastDFS+Nginx+Keepalived】文件库集群方案

FastDFS 是一个开源的(遵循GPL协议)高性能的轻量级分布式文件系统,主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载平衡。

本方案基于CentOS8系统设计,建议在RedHat/CentOS系统中使用。方案使用服务器及网络资源较多,建议在实施前做好规划工作,有利于部署工作顺利、有序进行。

目录

1.前言

2.FastDFS 架构
-- 2.1.部署模式
-- 2.2.处理流程

3.FastDFS 单点部署

4.FastDFS+Nginx 单点部署

5.FastDFS 集群部署
-- 5.1.集群部署拓补图
-- 5.2.FastDFS 集群部署
---- 5.2.1.部署调度服务器
---- 5.2.2.部署存储服务器
---- 5.2.3.部署 FastDFS Client 客户端

6.FastDFS+Keepalived+Nginx 集群部署
-- 6.1.集群部署拓补图
-- 6.2.FastDFS+Keepalived+Nginx 集群部署
---- 6.2.1.部署调度服务器
---- 6.2.2.部署 Nginx Web 存储服务器
---- 6.2.3.部署 Nginx Proxy 服务器
---- 6.2.4.部署 Nginx Proxy Keepalived 服务器
---- 6.2.5.部署客户端

7.Java开发集成示例


1.前言

1、FastDFS 的主要特性

1)分布式的文件存储系统,存储节点和调度节点能够无限扩容,分离部署;

2)支持廉价磁盘(如:IDE 磁盘)的在线扩容,解决了海量文件存储的问题;

3)存储采用逻辑分组概念,通过在同组内配置多个存储实现软 RAID,解决了数据容错恢复的问题;

4)用于文件的存储、同步、访问,支持文件读取负载均衡,解决了高并发访问的问题;

5)通过 Client API 进行文件操作,存在单点性能瓶颈(主要是写入性能,读取性能可通过 Web 负载均衡优化)。

2、FastDFS 的适用场景

1)特别适合以中小文件(建议范围:4KB < 文件大小 < 500MB)作为内容的 B/S 应用,如:相册网站、视频网站等;

2)不支持文件分块存储,不适合分布式计算场景;

3)相比使用 RDBMS 数据库存储文件(RDBMS 数据库一般采用二进制流字段存储文件)性能更高(尤其在读取文件时),但是在写入文件时则无法利用 RDBMS 的事务功能,在程序异常时可能产生垃圾文件。比如:某个应用程序负责提交订单(订单信息,扫描文件等)。当客户提交订单时发生异常,如果订单信息和扫描文件都存储在 RDBMS 中,则会事务回滚机制保证订单信息和扫描文件都不入库;如果订单信息存储在 RDBMS 中,扫描文件存储在 FastDFS 中,则有可能订单信息不入库但扫描文件入库的现象,针对这种现象,必须在程序编码中进行优化处理。

3、核心组件简介

1) FastDFS:由存储服务器(Storage Server)、调度服务器(Tracker Server)和客户端(Client)三部分组成:

  • Storage Server(存储服务器):主要提供容量和备份服务,以组(Group)为组织单位,每个 Group 包含多台 Storage Server 且数据互为备份,存储空间以 Group 中容量最小的 Storage Server 为准。组与组之间相互独立,可以存放不同的文件数据。

  • Tracker Server(调度服务器):主要提供调度服务,在读取文件时提供负载均衡,负责管理所有的Storage Server 和 Group。每个 Storage Server 在启动后会连接 Tracker Server,通知自己所属的 Group 信息并保持周期性心跳,Tracker Server 根据心跳信息,建立 Group => [ Storage Server List ] 映射表。为避免单点故障应提供多个 Tracker Server ,多个 Tracker Server 之间是对等(点对点)的主备关系。

  • Client(客户端):主要提供连接 Tracker Server ,进行文件上传下载的工具。

2) Nginx:轻量级、高性能的 Web 服务器和反向代理服务器,提供 Web 协议负载均衡的能力。它实现通过 Web 协议访问跳过 Tracker Server 直接访问 Storage Server 中的文件,且只能读取。

3) Keepalived:基于 VRRP 协议的轻量级服务高可用和负载均衡方案,提供避免服务器单点故障和请求分流的能力。它为 Nginx 扩展了高可用能力,共同组成完整的 Web 服务集群模式(高可用+负载均衡)。


2.FastDFS 架构

2.1.部署模式

1、FastDFS 单点模式

FastDFS 单点模式部署结构图

  • 1台服务器部署 FastDFS 的 Storage Server 、Tracker Server 。

  • 通过 Client API 进行文件读写。

2、FastDFS + Nginx 单点模式

FastDFS+Nginx 单点模式部署结构图
  • 1台服务器部署 FastDFS 的 Storage Server 、Tracker Server 、Nginx(Web 服务器)。

  • 通过 Client API 进行文件读写,扩展通过 Web 协议进行文件读取。

3、FastDFS 分布式集群模式

FastDFS 分布式集群模式部署结构图

  • ≥4台服务器部署 FastDFS 的 Storage Server,至少2台服务器为一组,组内节点互为备份,组与组之间相互独立,存放不同的文件数据。

  • ≥3台服务器部署 FastDFS 的 Tracker Server,实现 Client API 连接的负载均衡和高可用。

  • 通过 Client API 进行文件读写。

4、FastDFS + Nginx + Keepalived 分布式集群模式

FastDFS + Nginx + Keepalived 分布式集群模式部署结构图

  • ≥4台服务器部署 FastDFS 的 Storage Server,至少2台服务器为一组,组内节点互为备份,组与组之间相互独立,存放不同的文件数据;每台服务器扩展部署 Nginx(Web 服务器)。

  • ≥3台服务器部署 FastDFS 的 Tracker Server ,实现 Client API 连接的负载均衡和高可用。

  • ≥3台服务器部署 Nginx(反向代理服务器)、Keepalived ,实现 Web 服务的负载均衡和高可用。

  • 通过 Client API 进行文件读写,扩展通过 Web 协议进行文件读取。

2.2.处理流程

1、通过 Tracker 进行文件上传的处理流程

file_id(路径信息和文件名)格式为:volume0/M00/00/02/Cs8b8lFJIIyAH841AAAbpQt7xVI4715674。其中:

  • 组名(volume0):文件所在的 Storage Server Group 的名称。
  • 虚拟磁盘(M00):Storage Server 配置的虚拟路径,与磁盘选项 store_path 对应。如果配置了 store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
  • 文件目录(00/02):Storage Server 在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
  • 文件名:(Cs8b8lFJIIyAH841AAAbpQt7xVI4715674):采用base64编码,信息包含源storage server Ip、文件创建时间、文件大小、文件CRC32效验码和随机数。

2、通过 Tracker 进行文件下载的处理流程

Tracker Server 和 Storage Server 内置 HTTP 协议支持,客户端可以通过 HTTP 协议下载文件,Tracker Server 在接收到请求后,通过 HTTP 协议的 Redirect 机制将请求重定向至文件所在的 Storage Server 上,可以通过 Nginx 扩展文件下载的支持。


3.FastDFS 单点部署

假设当前主机 IP 地址为:192.168.216.128

1、打开 FastDFS 下载页面【https://github.com/happyfish100/】,下载 fastdfs 和 libfastcommon 的源代码 zip 或 tar.gz 包到用户主目录中。

FastDFS 下载页面

2、验证并安装依赖软件。通过源代码编译的方式安装 FastDFS,需要依赖软件"make"、"gcc"、"libevent",验证或安装依赖软件。

[centos@host ~]$ sudo dnf install make gcc libevent perl-devel

补充知识:

① "gcc"是一个C/C++、FORTRAN、JAVA、OBJC、ADA等多种语言的编译器,用来将源代码编译成可发布的软件程序。

② "make"是一个工程管理工具,能够根据 Makefile 中的脚本执行编译、安装流程。

③ "libevent"是一个事件通知库,如系统安装了GUI模块则已默认安装。

④ "perl-devel"是一个 Perl 语言开发库。

在Linux系统中,大多数通过源代码编译来安装的软件都需要依赖"make"和"gcc"。

3、解压缩 libfastcommon 和 fastdfs 的源代码 tar 包到用户主目录下。

[centos@host ~]$ tar zxvf libfastcommon-1.0.43.tar.gz
[centos@host ~]$ tar zxvf fastdfs-6.06.tar.gz
[centos@host ~]$ ll
drwxrwxr-x. 12 centos centos   4096 12月 31 07:36 fastdfs-6.06
drwxrwxr-x.  5 centos centos   4096 12月 25 20:35 libfastcommon-1.0.43

4、安装 libfastcommon,进入源代码目录,编译安装程序。

[centos@host ~]$ cd libfastcommon-1.0.43
[centos@host libfastcommon-1.0.43]$ ./make.sh
[centos@host libfastcommon-1.0.43]$ sudo ./make.sh install

[centos@host ~]$ ll /usr/lib64/libfast*
-rwxr-xr-x. 1 root root 1186112 5月  14 14:58 /usr/lib64/libfastcommon.so
lrwxrwxrwx. 1 root root      20 5月  14 2019 /usr/lib64/libfastjson.so.4 -> libfastjson.so.4.2.0
-rwxr-xr-x. 1 root root   49216 5月  14 2019 /usr/lib64/libfastjson.so.4.2.0

[centos@host ~]$ ll /usr/lib/libfast*
lrwxrwxrwx. 1 root root 27 5月  14 14:58 /usr/lib/libfastcommon.so -> /usr/lib64/libfastcommon.so

库文件目录是"/usr/lib64"和"/usr/lib"。

5、安装 fastdfs,进入源代码目录,编译安装程序。

[centos@host ~]$ cd fastdfs-6.06
[centos@host fastdfs-6.06]$ ./make.sh
[centos@host fastdfs-6.06]$ sudo ./make.sh install

[centos@host ~]$ ll /usr/bin/fdfs*
-rwxr-xr-x. 1 root root  448528 5月  14 15:04 /usr/bin/fdfs_appender_test
-rwxr-xr-x. 1 root root  448360 5月  14 15:04 /usr/bin/fdfs_appender_test1
-rwxr-xr-x. 1 root root  435112 5月  14 15:04 /usr/bin/fdfs_append_file
-rwxr-xr-x. 1 root root  433448 5月  14 15:04 /usr/bin/fdfs_crc32
-rwxr-xr-x. 1 root root  435144 5月  14 15:04 /usr/bin/fdfs_delete_file
-rwxr-xr-x. 1 root root  436128 5月  14 15:04 /usr/bin/fdfs_download_file
-rwxr-xr-x. 1 root root  435760 5月  14 15:04 /usr/bin/fdfs_file_info
-rwxr-xr-x. 1 root root  456224 5月  14 15:04 /usr/bin/fdfs_monitor
-rwxr-xr-x. 1 root root  435312 5月  14 15:04 /usr/bin/fdfs_regenerate_filename
-rwxr-xr-x. 1 root root 1578400 5月  14 15:04 /usr/bin/fdfs_storaged
-rwxr-xr-x. 1 root root  458032 5月  14 15:04 /usr/bin/fdfs_test
-rwxr-xr-x. 1 root root  457352 5月  14 15:04 /usr/bin/fdfs_test1
-rwxr-xr-x. 1 root root  634920 5月  14 15:04 /usr/bin/fdfs_trackerd
-rwxr-xr-x. 1 root root  435872 5月  14 15:04 /usr/bin/fdfs_upload_appender
-rwxr-xr-x. 1 root root  437248 5月  14 15:04 /usr/bin/fdfs_upload_file

[centos@host ~]$ ll /etc/fdfs
-rw-r--r--. 1 root root  1909 5月  14 15:04 client.conf.sample
-rw-r--r--. 1 root root 10246 5月  14 15:04 storage.conf.sample
-rw-r--r--. 1 root root   620 5月  14 15:04 storage_ids.conf.sample
-rw-r--r--. 1 root root  9138 5月  14 15:04 tracker.conf.sample

程序安装目录是"/usr/bin",配置文件目录是"/etc/fdfs"。

6、创建 FastDFS 工作根目录,并将系统管理用户设置为目录的拥有者。

[centos@host ~ ]$ sudo mkdir -p /data/fdfs
[centos@host ~ ]$ sudo chown -R centos:centos /data/fdfs

7、配置调度服务器(Tracker Server)。

1)创建调度服务器工作子目录,并将系统管理用户设置为目录的拥有者。

[centos@host ~ ]$ sudo mkdir -p /data/fdfs/tracker
[centos@host ~ ]$ sudo chown -R centos:centos /data/fdfs/tracker

2)进入程序配置目录"/etc/fdfs",通过拷贝从模板文件中创建配置文件"tracker.conf"。

[centos@host ~ ]$ sudo cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf

3)设置调度服务器配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /etc/fdfs/tracker.conf

修改或验证文件中的以下参数并保存:

# 表示工作目录位置。
base_path = /data/fdfs/tracker

# 表示服务监听的 IP 地址,设置为空时监听全部地址。默认为空。
bind_addr =

# 表示服务的端口号。默认为 22122 。 
port = 22122

# 表示当前配置是否禁用,设置为 true 表示禁用,设置为 false 表示启用。默认为 false 。
disabled = false

# 表示连接超时的时长(单位为秒)。默认为30秒。
connect_timeout = 5

# 表示网络超时的时长(单位为秒)。默认为30秒。
network_timeout = 60

# 最大并发连接数。默认为256个。
max_connections = 1024

# 表示日志记录级别,记录级别依次为:emerg/alert/crit/error/warn/notice/info/debug。默认为info
log_level = info

# 表示运行该程序的用户组,为空表示当前用户组。默认为空。
run_by_group =

# 表示运行该程序的用户,为空表示当前用户。默认为空。
run_by_user =

# 客户端访问策略,* 表示所有客户端均可访问。默认为 * 。
# 例如:allow_hosts=10.0.1.[1-15,20]
allow_hosts = *

4)启动调度服务器。

[centos@host ~ ]$ /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart

5)配置调度服务器开机自启动。

使用文本编辑器修改开机启动脚本文件:

[centos@host ~ ]$ sudo gedit /etc/rc.d/rc.local

在文件中追加内容并保存如下:

/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart

修改开机启动脚本文件确保其具有可执行权限:

[centos@host ~ ]$ sudo chmod +x /etc/rc.d/rc.local

注意:需要在系统管理用户(本例为 centos 用户)登录系统后才会自启动。

6)设置防火墙端口(CentOS8默认安装firewall防火墙),允许"22122" 端口(FastDFS Tracker Server 默认端口)访问服务器。

[centos@host ~ ]$ sudo firewall-cmd --zone=public --add-port=22122/tcp --permanent
[centos@host ~ ]$ sudo firewall-cmd --reload

7)管理调度服务器。

启动服务:

[centos@host ~ ]$ /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start

启动/重新启动服务:

[centos@host ~ ]$ /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart

停止服务:

[centos@host ~ ]$ /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf stop

查看服务状态:

[centos@host ~ ]$ sudo ps -ef | grep fdfs_trackerd
centos    65825   5765  0 5月14 ?       00:00:12 /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart

查看服务端口:

[centos@host ~ ]$ sudo netstat -ntap | grep fdfs_trackerd
tcp        0      0 0.0.0.0:22122           0.0.0.0:*               LISTEN      65825/fdfs_trackerd 

8、配置存储服务器(Storage Server)。

1)创建存储服务器工作子目录,并将系统管理用户设置为目录的拥有者。

[centos@host ~ ]$ sudo mkdir -p /data/fdfs/storage
[centos@host ~ ]$ sudo chown -R centos:centos /data/fdfs/storage

2)进入程序配置目录"/etc/fdfs",通过拷贝从模板文件中创建配置文件"storage.conf"。

[centos@host ~ ]$ sudo cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf

3)设置存储服务器配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /etc/fdfs/storage.conf

修改或验证文件中的以下参数并保存:

# 表示存储服务器的组标识。默认为group1 。
group_name = group1

# 表示调度服务器的 IP:PORT
# 可以配置多个调度服务器
# 127.0.0.1回路地址无效
tracker_server = 192.168.216.128:22122

# 表示工作目录位置。
base_path = /data/fdfs/storage

# 表示数据存储目录的数量。默认为1。
store_path_count = 1

# 可以配置多个数据存储目录,使用参数名后缀的序号区分,序号默认从0开始。
store_path0 = /data/fdfs/storage

# 表示服务监听的 IP 地址,设置为空时监听全部地址。默认为空。
bind_addr =

# 表示服务的端口号。默认为 23000 。
port = 23000

# 表示当前配置是否禁用,设置为 true 表示禁用,设置为 false 表示启用。默认为 false 。
disabled = false

# 表示连接超时的时长(单位为秒)。默认为30秒。
connect_timeout = 5

# 表示网络超时的时长(单位为秒)。默认为30秒。
network_timeout = 60

# 最大并发连接数。默认为256个。
max_connections = 1024

# 表示日志记录级别,记录级别依次为:emerg/alert/crit/error/warn/notice/info/debug。默认为info
log_level = info

# 表示运行该程序的用户组,为空表示当前用户组。默认为空。
run_by_group =

# 表示运行该程序的用户,为空表示当前用户。默认为空。
run_by_user =

# 客户端访问策略,* 表示所有客户端均可访问。默认为 * 。
# 例如:allow_hosts=10.0.1.[1-15,20]
allow_hosts = *

4)启动存储服务器。

[centos@host ~ ]$ /usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart

5)配置存储服务器开机自启动。

使用文本编辑器修改开机启动脚本文件:

[centos@host ~ ]$ sudo gedit /etc/rc.d/rc.local

在文件中追加内容并保存如下:

 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart

修改开机启动脚本文件确保其具有可执行权限:

[centos@host ~ ]$ sudo chmod +x /etc/rc.d/rc.local

注意:需要在系统管理用户(本例为 centos 用户)登录系统后才会自启动。

6)设置防火墙端口(CentOS8默认安装firewall防火墙),允许"23000" 端口(FastDFS Storage Server 默认端口)访问服务器。

[centos@host ~ ]$ sudo firewall-cmd --zone=public --add-port=23000/tcp --permanent
[centos@host ~ ]$ sudo firewall-cmd --reload

7)管理存储服务器。

启动服务:

[centos@host ~ ]$ /usr/bin/fdfs_storaged /etc/fdfs/storage.conf start

启动/重新启动服务:

[centos@host ~ ]$ /usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart

停止服务:

[centos@host ~ ]$ /usr/bin/fdfs_storaged /etc/fdfs/storage.conf stop

查看服务状态:

[centos@host ~ ]$ sudo ps -ef | grep fdfs_storaged 
centos    66979   5765  0 5月14 ?       00:00:20 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart

查看服务端口:

[centos@host ~ ]$ sudo netstat -ntap | grep fdfs_storaged 
tcp        0      0 0.0.0.0:23000           0.0.0.0:*               LISTEN      66979/fdfs_storaged 

9、配置本地客户端(FastDFS Client)。

1)创建客户端工作子目录,并将系统管理用户设置为目录的拥有者。

[centos@host ~ ]$ sudo mkdir -p /data/fdfs/client
[centos@host ~ ]$ sudo chown -R centos:centos /data/fdfs/client

2)进入程序配置目录"/etc/fdfs",通过拷贝从模板文件中创建配置文件"client.conf"。

[centos@host ~ ]$ sudo cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf

3)设置客户端配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /etc/fdfs/client.conf

修改或验证文件中的以下参数并保存:

# 表示工作目录位置。
base_path = /data/fdfs/client

# 表示调度服务器的 IP:PORT
# 可以配置多个调度服务器
tracker_server = 192.168.216.128:22122

# 表示连接超时的时长(单位为秒)。默认为30秒。
connect_timeout = 5

# 表示网络超时的时长(单位为秒)。默认为30秒。
network_timeout = 60

4)测试客户端上传文件。

[centos@host ~ ]$ /usr/bin/fdfs_test /etc/fdfs/client.conf upload test.txt
上传文件成功反馈消息

4.FastDFS+Nginx 单点部署

假设当前主机 IP 地址为:192.168.216.128

1、部署 FastDFS 单点服务器。

参照章节 "3.FastDFS 单点部署" 安装、配置调度服务器(Tracker Server)、存储服务器(Stroage Server)、客户端(FastDFS Client)。

从 FastDFS 源代码中拷贝 Web 服务配置文件到配置目录:

1)将 FastDFS 源代码目录下"conf"目录中的"http.conf"、"mime.types"、"anti-steal.jpg"文件拷贝到 FastDFS 配置文件目录中。

[centos@host ~ ]$ cd fastdfs-6.06/conf
[centos@host conf ]$ sudo cp http.conf mime.types anti-steal.jpg /etc/fdfs

2)设置"http.conf"文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /etc/fdfs/http.conf

修改文件中的以下参数并保存:

http.anti_steal.token_check_fail = /etc/fdfs/anti-steal.jpg

2、打开 FastDFS 下载页面【https://github.com/happyfish100/】,下载 fastdfs-nginx-module 的源代码 zip 或 tar.gz 包到用户主目录中。

FastDFS 下载页面

3、解压缩 fastdfs-nginx-module 的源代码 tar 包到"/usr/src"目录下。

[centos@host ~]$ sudo tar -zxvf fastdfs-nginx-module-1.22.tar.gz -C /usr/src
[centos@host ~]$ ll /usr/src
drwxrwxr-x. 3 root root 47 11月 19 12:29 fastdfs-nginx-module-1.22

程序源代码目录是"/usr/src/fastdfs-nginx-module-1.22"。

4、创建 FastDFS Nginx 模块工作子目录,并将系统管理用户设置为目录的拥有者。

[centos@host ~ ]$ sudo mkdir -p /data/fdfs/mod_fastdfs
[centos@host ~ ]$ sudo chown -R centos:centos /data/fdfs/mod_fastdfs

5、将 "/程序源代码目录/src/mod_fastdfs.conf" 文件拷贝到 FastDFS 配置文件目录中。

[centos@host ~]$ sudo cp /usr/src/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf /etc/fdfs

6、设置 FastDFS Nginx 模块配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /etc/fdfs/mod_fastdfs.conf

修改或验证文件中的以下参数并保存:

# 表示存储服务器的组标识。默认为group1 。
group_name = group1

# 表示调度服务器的 IP:PORT
# 可以配置多个调度服务器
tracker_server = 192.168.216.128:22122

# 表示工作目录位置。
base_path = /data/fdfs/mod_fastdfs

# 表示数据存储目录的数量。默认为1。
store_path_count = 1

# 可以配置多个数据存储目录,使用参数名后缀的序号区分,序号默认从0开始。
store_path0 = /data/fdfs/storage

# 表示存储服务器的端口号。默认为23000。
storage_server_port = 23000

# 表示请求URI中是否包含从组名开始,设置为true时,uri格式为:/group1/M00/00/00/xxx;设置为false时,uri格式为:/M00/00/00/xxx。默认为false。
url_have_group_name = true

# 表示连接超时的时长(单位为秒)。默认为30秒。
connect_timeout = 2

# 表示网络超时的时长(单位为秒)。默认为30秒。
network_timeout = 60

# 表示日志记录级别,记录级别依次为:emerg/alert/crit/error/warn/notice/info/debug。
log_level = info

# 表示日志文件位置,为空表示输出到控制台。默认为空。
log_filename =

7、打开 Nginx 下载页面【http://nginx.org/en/download.html】,下载 Nginx 的源代码 tar.gz 包到用户主目录中。

图片.png

8、验证并安装依赖软件。通过源代码编译的方式安装 Nginx,需要依赖软件"make"、"gcc"、"pcre"、"pcre-devel"、"zlib"、"zlib-devel"、"openssl"、"openssl-devel",验证或安装依赖软件。

[centos@host ~]$ sudo dnf install make gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel

补充知识:

① "gcc"是一个C/C++、FORTRAN、JAVA、OBJC、ADA等多种语言的编译器,用来将源代码编译成可发布的软件程序。

② "make"是一个工程管理工具,能够根据 Makefile 中的脚本执行编译、安装流程。

③ "pcre"是一个正则表达式函数库;"pcre-devel"是它的开发库。

④ "zlib"是一个数据压缩函数库;"zib-devel"是它的开发库。

⑤ "openssl"是一个实现安全通信,避免窃听,同时确认另一端连接者身份的软件程序;"openssl-devel"是它的开发库。

9、解压缩 Nginx 的源代码 tar 包到用户主目录下。

[centos@host ~]$ tar -zxvf nginx-1.18.0.tar.gz
[centos@host ~]$ ll
drwxr-xr-x.  8 centos centos    4096 4月  21 22:09 nginx-1.18.0

10、安装 Nginx,进入源代码目录,配置、编译、安装程序。在配置 Nginx 时,需要指定"--add-module"参数,设置为 fastdfs-nginx-module 的程序源代码目录。

[centos@host ~]$ cd nginx-1.18.0
[centos@host nginx-1.18.0]$ ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module  --with-stream --add-module=/usr/src/fastdfs-nginx-module-1.22/src
Configuration summary
  + using system PCRE library
  + OpenSSL library is not used
  + using system zlib library

  nginx path prefix: "/usr/local/nginx"
  nginx binary file: "/usr/local/nginx/sbin/nginx"
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/usr/local/nginx/conf"
  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx/logs/error.log"
  nginx http access log file: "/usr/local/nginx/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"

[centos@host nginx-1.18.0]$ make
[centos@host nginx-1.18.0]$ sudo make install

[centos@host ~]$ ll /usr/local/nginx
drwxr-xr-x. 2 root root 4096 5月  18 09:39 conf
drwxr-xr-x. 2 root root   40 5月  18 09:39 html
drwxr-xr-x. 2 root root    6 5月  18 09:39 logs
drwxr-xr-x. 2 root root   19 5月  18 09:39 sbin

程序安装目录是"/usr/local/nginx"。

11、设置 Nginx 配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /usr/local/nginx/conf/nginx.conf

修改或验证文件中的以下参数并保存:

http {
    server {
        # 监听端口
        listen       80;
        # 服务器域名(主机头)
        server_name  localhost;
        # 资源URL前缀,设置存储服务器的"/组名/虚拟磁盘名"。
        location /group1/M00/ {
            # 资源根目录,设置为存储服务器的数据目录,工作目录下的"data"文件夹。
            root   /data/fdfs/storage/data;
            # 声明启用 FastDFS Nginx 模块
            ngx_fastdfs_module;
        }
    }
}

12、配置 Nginx 开机自启动。

使用文本编辑器创建配置文件:

[centos@host ~ ]$ sudo gedit /usr/lib/systemd/system/nginx.service

编写文件内容并保存如下:

[Unit]
Description=Nginx
After=syslog.target network.target

[Service]
Type=forking
User=root
Group=root

ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true

[Install]
WantedBy=multi-user.target

设置开机启动:

[centos@host ~ ]$ sudo systemctl daemon-reload
[centos@host ~ ]$ sudo systemctl enable nginx.service

13、启动 Nginx 服务。

[centos@host ~ ]$ sudo systemctl start nginx.service

14、设置防火墙端口(CentOS8默认安装firewall防火墙),允许"80"端口(Nginx 默认端口)访问服务器。

[centos@host ~ ]$ sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
[centos@host ~ ]$ sudo firewall-cmd --reload

15、测试通过 Nginx 访问文件。

1)上传测试文件。

[centos@host ~ ]$ /usr/bin/fdfs_test /etc/fdfs/client.conf upload test.txt

2)通过浏览器访问测试文件。


16、扩展 Nginx 客户端身份认证。

1)安装 httpd-tools 。

[centos@host ~ ]$ sudo dnf install httpd-tools

2)创建账户/口令数据文件。

[centos@host ~ ]$ sudo htpasswd -bc /usr/local/nginx/conf/auth.db root password

指令格式为:htpasswd -b[c] <数据文件位置> <账号> <口令>。参数 b 表示创建一组账号/口令,参数 c 表示创建数据文件。

3)设置 Nginx 配置文件参数。

使用文本编辑器打开配置文件:

[centos@host ~ ]$ sudo gedit /usr/local/nginx/conf/nginx.conf

修改或验证文件中的以下参数并保存:

http {
    server {
        ......
        location / {
            ......
            auth_basic "input password";
            auth_basic_user_file /usr/local/nginx/conf/auth.db;
        }
    }
}

4)重新启动 Nginx 服务。

[centos@host ~ ]$ sudo systemctl restart nginx.service

17、Nginx 运维管理。

1)启动 Nginx 服务(任选一种方式)

[centos@host ~ ]$ sudo systemctl start nginx.service

或者

[centos@host ~ ]$ sudo -u /usr/local/nginx/sbin/nginx

2)停止 Nginx 服务(任选一种方式)

[centos@host ~ ]$ sudo systemctl stop nginx.service

或者

[centos@host ~ ]$ /usr/local/nginx/sbin/nginx -s quit

3)重启 Nginx 服务

[centos@host ~ ]$ sudo systemctl restart nginx.service

或者

[centos@host ~ ]$ /usr/local/nginx/sbin/nginx -s reload

4)查看 Nginx 服务状态

[centos@host ~ ]$ sudo systemctl status nginx.service

或者

[centos@host ~ ]$ sudo ps -ef | grep nginx
root     119777      1  0 10:16 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx

[centos@host ~ ]$ sudo netstat -ntap | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      119777/nginx: maste

[centos@host ~ ]$ sudo tail /usr/local/nginx/logs/error.log
[centos@host ~ ]$ sudo tail /usr/local/nginx/logs/access.log

5)开启 Nginx 服务开机自启动

[centos@host ~ ]$ sudo systemctl enable nginx.service

6)禁用 Nginx 服务开机自启动

[centos@host ~ ]$ sudo systemctl disable nginx.service

5.FastDFS 集群部署

5.1.集群部署拓补图

FastDFS 分布式集群模式部署结构图

网络资源规划:

1、调度服务器节点(Tracker Server Node)

节点名 主机名 IP:PORT 程序 操作系统
Tracker 集群节点-1 Tracker-1 192.168.216.128:22122 FastDFS Tracker CentOS8
Tracker 集群节点-2 Tracker-2 192.168.216.129:22122 FastDFS Tracker CentOS8
Tracker 集群节点≥3 Tracker-3 192.168.216.130:22122 FastDFS Tracker CentOS8

2、存储服务器节点(Storage Server Node)

节点名 主机名 IP:PORT 程序 操作系统
Storage 集群节点-1 G1_Storage-1 192.168.216.11:23000 FastDFS Storage CentOS8
Storage 集群节点-2 G1_Storage-2 192.168.216.12:23000 FastDFS Storage CentOS8
Storage 集群节点-3 G2_Storage-3 192.168.216.21:23000 FastDFS Storage CentOS8
Storage 集群节点≥4 G2_Storage-4 192.168.216.22:23000 FastDFS Storage CentOS8

3、客户端(FastDFS Client)

  • 节点名:FastDFS Client
  • 主机名:Cllient
  • IP地址:192.168.216.254
  • 程序:FastDFS Client
  • 操作系统:CentOS8

注意:调度服务器可以合并部署到存储服务器节点上。


5.2.FastDFS 集群部署

5.2.1.部署调度服务器

各个 "Tracker 集群节点" (Tracker-1、Tracker-2、Tracker-3)参照章节 "3.FastDFS 单点部署" 之第1-7步安装、配置调度服务器(Tracker Server)。

5.2.2.部署存储服务器

各个 "Storage 集群节点" (G1_Storage-1、G1_Storage-2、G2_Storage-3、G2_Storage-4)参照章节 "3.FastDFS 单点部署" 之第1-6、8步安装、配置存储服务器(Storage Server)。

集群部署中,在设置存储服务器配置文件(/etc/fdfs/storage.conf)参数时注意:
① group_name 属性:同一组存储节点的 group_name 设置值必须一致。
② tracker_server 属性:必须指定集群中全部可用的调度服务器(Tracker Server)。
③ 其他设置与单点部署基本一致。

  • G1_Storage-1 和 G1_Storage-2 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 同一组存储节点的 group_name 设置值必须一致。
group_name = group1

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122
  • G2_Storage-3 和 G2_Storage-4 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 同一组存储节点的 group_name 设置值必须一致。
group_name = group2

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122

5.2.3.部署 FastDFS Client 客户端

在 "FastDFS Client 节点" 上参照章节 "3.FastDFS 单点部署"之第1-6、9步安装、配置 FastDFS 客户端。

集群部署中,在设置客户端配置文件(/etc/fdfs/client.conf)参数时注意:
① tracker_server 属性:必须指定集群中全部可用的调度服务器(Tracker Server)。
② 其他设置与单点部署基本一致。

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122

6.FastDFS+Keepalived+Nginx 集群部署

6.1.集群部署拓补图

FastDFS + Nginx + Keepalived 分布式集群模式部署结构图

网络资源规划:

1、调度服务器节点(Tracker Server Node)

节点名 主机名 IP:PORT 程序 操作系统
Tracker 集群节点-1 Tracker-1 192.168.216.128:22122 FastDFS Tracker CentOS8
Tracker 集群节点-2 Tracker-2 192.168.216.129:22122 FastDFS Tracker CentOS8
Tracker 集群节点≥3 Tracker-3 192.168.216.130:22122 FastDFS Tracker CentOS8

2、Nginx Web 存储服务器节点(Nginx Web+Storage Server Node)

节点名 主机名 IP:PORT 程序 操作系统
Storage 集群节点-1 G1_Storage-1 192.168.216.11:23000 / 80 FastDFS Storage / Nginx CentOS8
Storage 集群节点-2 G1_Storage-2 192.168.216.12:23000 / 80 FastDFS Storage / Nginx CentOS8
Storage 集群节点-3 G2_Storage-3 192.168.216.21:23000 / 80 FastDFS Storage / Nginx CentOS8
Storage 集群节点≥4 G2_Storage-4 192.168.216.22:23000 / 80 FastDFS Storage / Nginx CentOS8

3、Nginx Proxy 高可用服务器节点(Nginx Proxy+Keepalived Server Node)

节点名 主机名 IP:PORT 程序 操作系统
Proxy 集群节点-1 Proxy-1 192.168.216.91:80 / 112 Nginx / Keepalived CentOS8
Proxy 集群节点-2 Proxy-2 192.168.216.92:80 / 112 Nginx / Keepalived CentOS8
Proxy 集群节点≥3 Proxy-3 192.168.216.93:80 / 112 Nginx / Keepalived CentOS8

Keepalived Virtual IP:192.168.216.90。

4、客户端(FastDFS Client)

  • 节点名:FastDFS Client
  • 主机名:Client
  • IP地址:192.168.216.254
  • 程序:FastDFS Client / Firefox
  • 操作系统:CentOS8

注意:调度服务器可以合并部署到 Nginx Web 存储服务器节点上。


6.2.FastDFS+Keepalived+Nginx 集群部署

6.2.1.部署调度服务器

各个 "Tracker 集群节点"(Tracker-1、Tracker-2、Tracker-3)参照章节 "3.FastDFS 单点部署" 之第1-7步安装、配置调度服务器(Tracker Server)。

6.2.2.部署 Nginx Web 存储服务器

1、各个 "Storage 集群节点"(G1_Storage-1、G1_Storage-2、G2_Storage-3、G2_Storage-4)参照章节 "3.FastDFS 单点部署" 之第1-6、8步安装、配置存储服务器(Storage Server)。

集群部署中,在设置存储服务器配置文件(/etc/fdfs/storage.conf)参数时注意:
① group_name 属性:同一组存储节点的 group_name 设置值必须一致。
② tracker_server 属性:必须指定集群中全部可用的调度服务器(Tracker Server)。
③ 其他设置与单点部署基本一致。

  • G1_Storage-1 和 G1_Storage-2 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 同一组存储节点的 group_name 设置值必须一致。
group_name = group1

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122
  • G2_Storage-3 和 G2_Storage-4 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 同一组存储节点的 group_name 设置值必须一致。
group_name = group2

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122

2、各个 "Storage 集群节点"(G1_Storage-1、G1_Storage-2、G2_Storage-3、G2_Storage-4)参照章节 "4.FastDFS+Nginx 单点部署" 安装、配置 Nginx Web 服务器。

集群部署中,在设置 FastDFS Nginx 模块配置文件(/etc/fdfs/mod_fastdfs.conf)参数时注意:
① group_name 属性:本地存储服务器所属的组名称。
② tracker_server 属性:必须指定集群中全部可用的调度服务器(Tracker Server)。
③ 其他设置与单点部署基本一致。

  • G1_Storage-1 和 G1_Storage-2 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 与本地存储服务器配置文件中的 group_name 设置值必须一致。
group_name = group1

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122
  • G2_Storage-3 和 G2_Storage-4 配置文件中的 group_name 和 tracker_server 属性配置如下:
# 与本地存储服务器配置文件中的 group_name 设置值必须一致。
group_name = group2

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122

集群部署中,在设置 Nginx 配置文件(/usr/local/nginx/conf/nginx.conf)参数时注意:
① location 属性:设置本地存储服务器的"/组名/虚拟磁盘名"。
③ 其他设置与单点部署基本一致。

  • G1_Storage-1 和 G1_Storage-2 配置文件中的 location 属性配置如下:
http {
    server {
        ......
        # 设置本地存储服务器的"/组名/虚拟磁盘名"。组名必须与本地存储服务器配置文件中的 group_name 设置值必须一致。
        location /group1/M00/ {
            ......
        }
    }
}
  • G2_Storage-3 和 G2_Storage-4 配置文件中的 location 属性配置如下:
http {
    server {
        ......
        # 设置本地存储服务器的"/组名/虚拟磁盘名"。组名必须与本地存储服务器配置文件中的 group_name 设置值必须一致。
        location /group2/M00/ {
            ......
        }
    }
}

6.2.3.部署 Nginx Proxy 服务器

在各个 "Proxy 集群节点" (Proxy-1、Proxy-2、Proxy-3)安装、配置 Nginx。以 "Proxy-1" 为例:

1、打开 Nginx 下载页面【http://nginx.org/en/download.html】,下载 Nginx 的源代码 tar.gz 包到用户主目录中。

图片.png

2、验证并安装依赖软件。通过源代码编译的方式安装 Nginx,需要依赖软件"make"、"gcc"、"pcre"、"pcre-devel"、"zlib"、"zlib-devel"、"openssl"、"openssl-devel",验证或安装依赖软件。

[centos@Proxy-1 ~]$ sudo dnf install make gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel

补充知识:

① "gcc"是一个C/C++、FORTRAN、JAVA、OBJC、ADA等多种语言的编译器,用来将源代码编译成可发布的软件程序。

② "make"是一个工程管理工具,能够根据 Makefile 中的脚本执行编译、安装流程。

③ "pcre"是一个正则表达式函数库;"pcre-devel"是它的开发库。

④ "zlib"是一个数据压缩函数库;"zib-devel"是它的开发库。

⑤ "openssl"是一个实现安全通信,避免窃听,同时确认另一端连接者身份的软件程序;"openssl-devel"是它的开发库。

3、解压缩 Nginx 的源代码 tar 包到用户主目录下。

[centos@Proxy-1 ~]$ tar -zxvf nginx-1.18.0.tar.gz
[centos@Proxy-1 ~]$ ll
drwxr-xr-x.  8 centos centos    4096 4月  21 22:09 nginx-1.18.0

4、安装 Nginx,进入源代码目录,配置、编译、安装程序。

[centos@Proxy-1 ~]$ cd nginx-1.18.0
[centos@Proxy-1 nginx-1.18.0]$ ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module  --with-stream
Configuration summary
  + using system PCRE library
  + OpenSSL library is not used
  + using system zlib library

  nginx path prefix: "/usr/local/nginx"
  nginx binary file: "/usr/local/nginx/sbin/nginx"
  nginx modules path: "/usr/local/nginx/modules"
  nginx configuration prefix: "/usr/local/nginx/conf"
  nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx/logs/error.log"
  nginx http access log file: "/usr/local/nginx/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"

[centos@Proxy-1 nginx-1.18.0]$ make
[centos@Proxy-1 nginx-1.18.0]$ sudo make install

[centos@Proxy-1 ~]$ ll /usr/local/nginx
drwxr-xr-x. 2 root root 4096 5月  18 09:39 conf
drwxr-xr-x. 2 root root   40 5月  18 09:39 html
drwxr-xr-x. 2 root root    6 5月  18 09:39 logs
drwxr-xr-x. 2 root root   19 5月  18 09:39 sbin

程序安装目录是"/usr/local/nginx"。

5、设置 Nginx 配置文件参数。

使用文本编辑器打开配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /usr/local/nginx/conf/nginx.conf

修改或验证文件中的以下参数并保存:

http {
    
    # 第一组存储服务器负载均衡(G1_Storage-1 和 G1_Storage-2)
    upstream fdfs.group1 {
        server 192.168.216.11:80 weight=1;
        server 192.168.216.12:80 weight=1;
    }
    
    # 第二组存储服务器负载均衡(G2_Storage-3 和 G2_Storage-4)
    upstream fdfs.group2 {
        server 192.168.216.21:80 weight=1;
        server 192.168.216.22:80 weight=1;
    }
    
    server {
        # 监听端口
        listen       80;
        # 服务器域名(主机头)
        server_name  localhost;
        # 第一组存储服务器上的文件资源URL前缀,设置存储服务器的"/组名/虚拟磁盘名"。
        location /group1/M00 {
            # 代理转发的地址,格式为:"协议(http:// 或 https://)" + "upstream 设置值(本例为 upstream fdfs.group1)" 。
            proxy_pass http://fdfs.group1;

            # 代理转发时提交的客户端认证信息(若代理服务已设置客户端身份认证时配置),格式:"<账号>:<密码>" 的 base64 编码。
            # 可以使用 "echo <账号>:<密码> | base64" 获取字符串的 base64 编码。
            # 如:[centos@host ~ ]$  echo root:passwd | base64
            # cm9vdDpwYXNzd2QK
            # proxy_set_header Authorization "Basic cm9vdDpwYXNzd2QK";
        }

        # 第二组存储服务器上的文件资源URL前缀,设置存储服务器的"/组名/虚拟磁盘名"。
        location /group2/M00 {
           # 代理转发的地址,格式为:"协议(http:// 或 https://)" + "upstream 设置值(本例为 upstream fdfs.group2)" 。
            proxy_pass http://fdfs.group2;

            # 代理转发时提交的客户端认证信息(若代理服务已设置客户端身份认证时配置),格式:"<账号>:<密码>" 的 base64 编码。
            # 可以使用 "echo <账号>:<密码> | base64" 获取字符串的 base64 编码。
            # 如:[centos@host ~ ]$  echo root:passwd | base64
            # cm9vdDpwYXNzd2QK
            # proxy_set_header Authorization "Basic cm9vdDpwYXNzd2QK";
        }
    }
}

6、配置 Nginx 开机自启动。

使用文本编辑器创建配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /usr/lib/systemd/system/nginx.service

编写文件内容并保存如下:

[Unit]
Description=Nginx
After=syslog.target network.target

[Service]
Type=forking
User=root
Group=root

ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true

[Install]
WantedBy=multi-user.target

设置开机启动:

[centos@Proxy-1 ~ ]$ sudo systemctl daemon-reload
[centos@Proxy-1 ~ ]$ sudo systemctl enable nginx.service

7、启动 Nginx 服务。

[centos@Proxy-1 ~ ]$ sudo systemctl start nginx.service

8、设置防火墙端口(CentOS8默认安装firewall防火墙),允许"80"端口(Nginx 默认端口)访问服务器。

[centos@Proxy-1 ~ ]$ sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
[centos@Proxy-1 ~ ]$ sudo firewall-cmd --reload

注意:其他 "Proxy 集群节点" 全部需要按照以上步骤配置。

9、扩展 Nginx 客户端身份认证。

1)安装 httpd-tools 。

[centos@Proxy-1 ~ ]$ sudo dnf install httpd-tools

2)创建账户/口令数据文件。

[centos@Proxy-1 ~ ]$ sudo htpasswd -bc /usr/local/nginx/conf/auth.db root password

指令格式为:htpasswd -b[c] <数据文件位置> <账号> <口令>。参数 b 表示创建一组账号/口令,参数 c 表示创建数据文件。

3)设置 Nginx 配置文件参数。

使用文本编辑器打开配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /usr/local/nginx/conf/nginx.conf

修改或验证文件中的以下参数并保存:

http {
    server {
        ......
        location / {
            ......
            auth_basic "input password";
            auth_basic_user_file /usr/local/nginx/conf/auth.db;
        }
    }
}

4)重新启动 Nginx 服务。

[centos@Proxy-1 ~ ]$ sudo systemctl restart nginx.service

10、Nginx 运维管理。

1)启动 Nginx 服务(任选一种方式)

[centos@Proxy-1 ~ ]$ sudo systemctl start nginx.service

或者

[centos@Proxy-1 ~ ]$ sudo -u /usr/local/nginx/sbin/nginx

2)停止 Nginx 服务(任选一种方式)

[centos@Proxy-1 ~ ]$ sudo systemctl stop nginx.service

或者

[centos@Proxy-1 ~ ]$ /usr/local/nginx/sbin/nginx -s quit

3)重启 Nginx 服务

[centos@Proxy-1 ~ ]$ sudo systemctl restart nginx.service

或者

[centos@Proxy-1 ~ ]$ /usr/local/nginx/sbin/nginx -s reload

4)查看 Nginx 服务状态

[centos@Proxy-1 ~ ]$ sudo systemctl status nginx.service

或者

[centos@Proxy-1 ~ ]$ sudo ps -ef | grep nginx
root     119777      1  0 10:16 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx

[centos@Proxy-1 ~ ]$ sudo netstat -ntap | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      119777/nginx: maste

[centos@Proxy-1 ~ ]$ sudo tail /usr/local/nginx/logs/error.log
[centos@Proxy-1 ~ ]$ sudo tail /usr/local/nginx/logs/access.log

6.2.4.部署 Nginx Proxy Keepalived 服务器

在各个 "Proxy 集群节点" (Proxy-1、Proxy-2、Proxy-3)安装、配置 Keepalived。以 "Proxy-1" 为例:

1、安装 EPEL 的 Yum源。

使用文本编辑器创建仓库配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /etc/yum.repos.d/epel.repo

在文件中编写以下内容并保存:

[epel-modular]
name=Extra Packages for Enterprise Linux Modular $releasever - $basearch
baseurl=http://mirrors.aliyun.com/epel/$releasever/Modular/$basearch
enabled=1
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-8

[epel]
name=Extra Packages for Enterprise Linux $releasever - $basearch
baseurl=http://mirrors.aliyun.com/epel/$releasever/Everything/$basearch
enabled=1
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-8

更新 Yum 源:

[centos@Proxy-1 ~]$ sudo dnf clean all
[centos@Proxy-1 ~]$ sudo dnf makecache
Extra Packages for Enterprise Linux Modular 8 - 429 kB/s | 118 kB     00:00    
Extra Packages for Enterprise Linux 8 - x86_64  3.7 MB/s | 6.9 MB     00:01    
元数据缓存已建立。

EPEL(Extra Packages for Enterprise Linux)是企业级 Linux 操作系统的扩展包仓库,为 Redhat/CentOS系统提供大量的额外软件包。

2、安装 Keepalived。

[centos@Proxy-1 ~]$ sudo dnf install keepalived

程序安装目录是"/usr/sbin",配置文件目录是"/etc/keepalived"。

3、设置 Keepalived 配置文件参数。

使用文本编辑器打开配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /etc/keepalived/keepalived.conf

在文件中编写以下内容并保存:

# 定义全局配置
global_defs {
    # 本地节点 ID 标识,一般设置为主机名。
    router_id Proxy-1
}

# 定义周期性执行的脚本,脚本的退出状态码会被调用它的所有的 vrrp_instance 记录。
vrrp_script chk_nginx {
    # 执行脚本的路径。
    script "/etc/keepalived/nginx_check.sh"
    # 脚本执行的间隔(单位是秒)。默认为1s。
    interval 2
    # 当脚本调整优先级,从 -254 到 254。默认为2。
    # 1. 如果脚本执行成功(退出状态码为0),weight大于0,则priority增加。
    # 2. 如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少。
    # 3. 其他情况下,priority不变。
    weight -20
    # 当脚本执行超过时长(单位是秒)则被认为执行失败。
    # 运行脚本的用户和组。
    user root root
    # timeout 30
    # 当脚本执行成功到设定次数时,才认为是成功。
    # rise 1
    # 当脚本执行失败到设定次数时,才认为是失败。
    # fall 3
}

# 定义虚拟路由,可以定义多个。
vrrp_instance VI_1 {
    # 本地节点初始状态,包括 MASTER(主节点) 和 BACKUP (备节点)。
    state MASTER
    # 本地节点绑定虚拟 IP 的网络接口。
    interface ens33
    # 本地节点优先级,优先级高的节点将动态变成 MASTER 节点,接管 VIP 。初始状态下,MASTER 节点的优先级必须高于 BACKUP 节点。
    priority 100
    # VRRP 实例 ID,范围是0-255。同一集群的所有节点应设置一致的值。
    virtual_router_id 216
    # 组播信息发送时间间隔。同一集群的所有节点必须设置一样,默认为1秒。
    advert_int 1
    # 设置验证信息。同一集群的所有节点必须一致
    authentication {
        # 指定认证方式。PASS 表示简单密码认证(推荐);AH:IPSEC认证(不推荐)。
        auth_type PASS
        # 指定认证所使用的密码,最多8位。
        auth_pass 1111
    }

    # 声明调用已定义的 vrrp_script 脚本。
    track_script {
        chk_nginx
    }

    # 定义虚拟 IP 地址。
    virtual_ipaddress {
        192.168.216.90
    }
}

# 定义对外提供服务 LVS (负载均衡)的 VIP 和 端口(当端口号设置为【0】时,表示所有端口),只实现高可用时可不配置。
# 注意:本方案中,通过 Nginx Proxy 实现 Nginx Web 负载均衡,Keepalived 只实现高可用,因此负载均衡既可以不配置,也可以配置成 Nginx Proxy 的负载均衡;当没有 Nginx Proxy 时,可以直接配置成 Nginx Web 的负载均衡。
virtual_server 192.168.216.90 80 {  
    # 设置健康检查时间,单位是秒
    delay_loop 6 
    #负载均衡调度算法
    lb_algo rr
    # 设置LVS实现负载的机制,有NAT、TUN、DR三个模式    
    lb_kind DR
    # VIP 子网掩码
    nat_mask 255.255.255.0
    # 会话保持时间,一定时间之内用户无响应则下一次用户请求时需重新路由,一般设为0,表示不需要    
    persistence_timeout 0
    # 网络协议
    protocol TCP
    # 定义后端 RealServer 的真实服务器属性,IP 地址和端口(当端口号设置为【0】时,表示所有端口)
    real_server 192.168.216.91 80 { 
        # 配置节点权值,数字越大权重越高 
        weight 1
        TCP_CHECK {  
            connect_timeout 10         
            nb_get_retry 3  
            delay_before_retry 3  
            connect_port 80  
        }  
    }  
    real_server 192.168.216.92 80 {
        weight 1
        TCP_CHECK {  
            connect_timeout 10  
            nb_get_retry 3  
            delay_before_retry 3  
            connect_port 80  
        }  
    }  
    real_server 192.168.216.93 80 {
        weight 1
        TCP_CHECK {  
            connect_timeout 10  
            nb_get_retry 3  
            delay_before_retry 3  
            connect_port 80  
        }  
    }  
}

初始化的主节点和备节点的区别体现在以下参数中:

  • 初始主节点
vrrp_instance VI_1 {
    # 必须设置为 MASTER 。
    state MASTER
    # 必须设置为最大值。
    priority 100
}
  • 初始备节点
vrrp_instance VI_1 {
    # 必须设置为 BACKUP 。
    state BACKUP
    # 必须设置为小于主节点的值。
    priority 90
}

4、创建或编辑 Nginx 检测脚本文件。文件路径对应配置文件中 vrrp_script 的 script 设置值。

使用文本编辑器创建脚本文件:

[centos@Proxy-1 ~ ]$ sudo gedit /etc/keepalived/nginx_check.sh

在脚本文件中编写以下内容并保存:

#!/bin/bash
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
    /usr/local/nginx/sbin/nginx
    sleep 2
    counter=$(ps -C nginx --no-heading|wc -l)
    if [ "${counter}" = "0" ]; then
        killall -9 keepalived
    fi
fi

给脚本文件增加可执行权限:

[centos@Proxy-1 ~ ]$ sudo chmod 755 /etc/keepalived/nginx_check.sh

5、配置 Keepalived 系统服务。

使用文本编辑器创建配置文件:

[centos@Proxy-1 ~ ]$ sudo gedit /usr/lib/systemd/system/keepalived.service

验证或修改文件内容并保存如下:

[Unit]
Description=LVS and VRRP High Availability Monitor
After=network-online.target syslog.target nginx.service
Wants=network-online.target
Requires=nginx.service

[Service]
Type=forking
User=root
Group=root
PIDFile=/var/run/keepalived.pid
KillMode=process
EnvironmentFile=-/etc/sysconfig/keepalived
ExecStart=/usr/sbin/keepalived $KEEPALIVED_OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

重新加载系统服务管理器:

[centos@Proxy-1 ~ ]$ sudo systemctl daemon-reload

6、设置防火墙端口(CentOS8默认安装firewall防火墙),允许"112"端口(Keepalived 默认端口)访问服务器。

[centos@Proxy-1 ~ ]$ sudo firewall-cmd --zone=public --add-port=112/tcp --permanent
[centos@Proxy-1 ~ ]$ sudo firewall-cmd --reload

7、启动/重启 Keepalived 服务(不建议设置为开机自启动)。

启动 Keepalived 服务之前,应确保已正确启动了各节点的 Nginx 服务。各节点的启动或重启的顺序为:① 启动 Keepalived 主节点;② 依次启动 Keepalived 备节点。

[centos@Proxy-1 ~ ]$ sudo systemctl restart keepalived.service

8、启动 Keepalived 可能因为各种未知的原因失败,主要是由于引发了 SELinux 异常。有关如何解决 SELinux 引起的异常,请阅读文章《RedHat/CentOS8【SELinux】引起的安全策略问题解决方案》,文章地址【https://www.jianshu.com/p/a13f974f8bae】。

注意:其他 "Proxy 集群节点" 全部需要按照以上步骤配置。

6.2.5.部署客户端

1、 在 "FastDFS Client 节点" 上参照章节 "3.FastDFS 单点部署"之第1-6、9步安装、配置 FastDFS Client 客户端。

集群部署中,在设置客户端配置文件(/etc/fdfs/client.conf)参数时注意:
① tracker_server 属性:必须指定集群中全部可用的调度服务器(Tracker Server)。
② 其他设置与单点部署基本一致。

# 需要指定集群中全部可用的调度服务器(Tracker Server)
tracker_server = 192.168.216.128:22122
tracker_server = 192.168.216.129:22122
tracker_server = 192.168.216.130:22122

2、 在 "FastDFS Client 节点" 上通过浏览器使用虚拟 IP 访问测试文件。


7.Java开发集成示例

fastdfs-client-java 是 FastDFS 的 JavaAPI 包,以下是 Maven 项目的程序案例。

1、从 Maven 库中引入 fastdfs-client-java 包。

<dependency>
  <groupId>org.csource</groupId>
  <artifactId>fastdfs-client-java</artifactId>
  <version>1.27-SNAPSHOT</version>
</dependency>

2、编写 FastDFS 连接文件 "fastdfs.properties"。

#Tracker Server IP地址
fastdfs.tracker_servers=192.168.216.128:22122,192.168.216.129:22122,192.168.216.130:22122
#连接soTimeout设置
fastdfs.soTimeout=10000
#连接超时设置
fastdfs.connectTimeout=5000
#连接池 maxTotal
fastdfs.maxTotal=200
#连接池 maxTotalPerKey
fastdfs.maxTotalPerKey=200
#连接池 maxIdlePerKey 最大空闲连接数(影响并发性能)
fastdfs.maxIdlePerKey=50

3、编写 FastDFS 会话类 "FastDFSSession.java"。

/**
 * 
 * FastDFS Libs
 * 
 * ©2018 张毅
 * 
 */

package zh.fdfs;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

/**
 * FastDFS 会话类 (FastDFS Session)
 */
public class FastDFSSession {

    /** FastDFS Tracker 客户端 */
    private static TrackerClient trackerClient;

    /** FastDFS Tracker 服务器端 */
    private static TrackerServer trackerServer;
    
    /** FastDFS Storage 服务器端 */
    private static StorageServer storageServer;

    /** FastDFS Storage 客户端 */
    private static StorageClient storageClient;
    

    /**
     * 获取FastDFS Tracker 客户端。
     * 
     * @return FastDFS Tracker 客户端。
     */
    protected static TrackerClient getTrackerClient() {
        return trackerClient;
    }

    /**
     * 获取FastDFS Tracker 服务器端。
     * 
     * @return FastDFS Tracker 服务器端。
     */
    protected static TrackerServer getTrackerServer() {
        return trackerServer;
    }
    
    /**
     * 获取FastDFS Storage 服务器端。
     * 
     * @return FastDFS Storage 服务器端。
     */
    protected static StorageServer getStorageServer() {
        return storageServer;
    }

    /**
     * 获取FastDFS Storage 客户端。
     * 
     * @return FastDFS Storage 客户端。
     */
    protected static StorageClient getStorageClient() {
        return storageClient;
    }


    /**
     * 构造器。
     * 
     * @param propFileName 配置文件名字。
     * @param groupName    Storage组名字。
     */
    static {

        try {
            ClientGlobal.initByProperties("fastdfs.properties");
            trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
            trackerServer = trackerClient.getConnection();
            storageServer = trackerClient.getStoreStorage(trackerServer);
            storageClient = new StorageClient1(trackerServer, storageServer);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件。
     * 
     * @param buffer   文件流。
     * @param extName  文件扩展名。
     * @param metaList 文件元数据。
     * @return FastDFS 文件组名和ID。
     */
    public static String[] upload(byte[] buffer, String extName, NameValuePair[] metaList) {

        try {
            String[] fileids = getStorageClient().upload_file(buffer, extName, metaList);
            return fileids;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件。
     * 
     * @param localFilePath 本地文件路径。
     * @param extName       文件扩展名。
     * @param metaList      文件元数据。
     * @return FastDFS 文件组名和ID。
     */
    public static String[] upload(String localFilePath, String extName, NameValuePair[] metaList) {

        try {
            String[] fileids = getStorageClient().upload_file(localFilePath, extName, metaList);
            return fileids;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传文件。
     * 
     * @param file     文件模型。
     * @param extName  文件扩展名。
     * @param metaList 文件元数据。
     * @return FastDFS 文件组名和ID。
     */
    public static String[] upload(File file, String extName, NameValuePair[] metaList) {

        try {

            if (file != null && file.isFile()) {
                byte[] buffer = null;
                FileInputStream fis = new FileInputStream(file);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] b = new byte[1024];
                int n;
                while ((n = fis.read(b)) != -1) {
                    bos.write(b, 0, n);
                }
                fis.close();
                bos.close();
                buffer = bos.toByteArray();
                String[] fileids = getStorageClient().upload_file(buffer, extName, metaList);
                return fileids;
            } else {
                return null;
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 下载文件。
     * 
     * @param group  FastDFS 组名.
     * @param fileid FastDFS 文件ID.
     * @return 文件流。
     */
    public static byte[] download(String group, String fileid) {

        byte[] buffer;
        try {
            buffer = getStorageClient().download_file(group, fileid);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return buffer;
    }

    /**
     * 下载文件。
     * 
     * @param group         FastDFS 组名.
     * @param fileid        FastDFS 文件ID.
     * @param localFilePath 本地文件路径.
     * @return 操作成功返回0,非0则操作失败,返回错误代码。
     */
    public static int download(String group, String fileid, String localFilePath) {

        try {
            int result = getStorageClient().download_file(group, fileid, localFilePath);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取文件信息。
     * 
     * @param group  FastDFS 组名.
     * @param fileid FastDFS 文件ID.
     * @return 文件信息模型。
     */
    public static FileInfo getFileInfo(String group, String fileid) {

        try {
            FileInfo info = getStorageClient().get_file_info(group, fileid);
            return info;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取文件元数据。
     * 
     * @param group  FastDFS 组名.
     * @param fileid FastDFS 文件ID.
     * @return 文件元数据模型。
     */
    public static NameValuePair[] getMetadata(String group, String fileid) {

        try {
            NameValuePair[] metas = getStorageClient().get_metadata(group, fileid);
            return metas;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除文件。
     * 
     * @param group  FastDFS 组名.
     * @param fileid FastDFS 文件ID.
     * @return 操作成功返回0,非0则操作失败,返回错误代码。
     */
    public static int delete(String group, String fileid) {

        try {
            int result = getStorageClient().delete_file(group, fileid);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

4、编写主程序。

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;

public class App {
    public static void main(String[] args) {
        
        // 上传文件
        String localFilePath = "fastdfs.txt";
        NameValuePair wnvps[] = new NameValuePair[] { new NameValuePair("title", "测试文件"),
                new NameValuePair("summary", "摘要信息") };
        String fileIds[] = FastDFSSession.upload(localFilePath, "txt", wnvps);

        System.out.println("组名:" + fileIds[0]);
        System.out.println("文件ID: " + fileIds[1]);

        // 下载文件
        byte[] data = FastDFSSession.download("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.txt");
        System.out.println(data);

        try {
            FileOutputStream fos = new FileOutputStream("fastdfs.txt");
            fos.write(data, 0, data.length);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 获取文件系信息
        FileInfo fi = FastDFSSession.getFileInfo("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.txt");
        System.out.println(fi.getSourceIpAddr());
        System.out.println(fi.getFileSize());
        System.out.println(fi.getCreateTimestamp());
        System.out.println(fi.getCrc32());

        // 获取文件元数据
        NameValuePair rnvps[] = FastDFSSession.getMetadatas("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.txt");
        for (NameValuePair rnvp : rnvps) {
            System.out.println(rnvp.getName() + ":" + rnvp.getValue());
        }

        // 删除文件
        int i = FastDFSSession.delete("group1", "M00/00/00/wKgRcFV_08OAK_KCAAAA5fm_sy874.txt");
        System.out.println(i == 0 ? "删除成功" : "删除失败:" + i);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343