Nginx手册

Nginx基本概念

return和rewrite对比

img

Nginx配置说明

worker_processes
#官方说明:一个CPU配置多于一个worker数,对Nginx而言没有任何益处。所以worker数量尽量不要超过CPU内核数量。一般worker数量设置为CPU内核数量。

#标准配置
worker_processes auto;
worker_connections
#针对每一个worker进程的连接数。已验证在只有一个工作进程时,该连接数的设置除半就是Nginx的并发连接数。当多个worker进程时,并发连接数就会翻倍。

#官方说明每一个worker进程能并发处理和发起的最大连接数(包含所有连接数)。真实的并发连接数不能超过最大文件打开数worker_rlimit_nofile(但是配置时超不超过无所谓),也就是说并发数也受worker_rlimit_nofile数量所限制。

#worker_connections的限制:不能超过最大文件打开数,在linux终端中输入ulimit -a进行查看。

#标准配置
worker_connections 65535;
worker_rlimit_nofile
#针对每一个worker进程的连接数

#官方说明:为nginx工作进程改变打开最多文件描述符数目的限制。用来在不重启主进程的情况下增加限制。

#标准配置
worker_rlimit_nofile 204800;    #需要配置成CPU数和worker_connections乘积还要多

Nginx网络模型

老Nginx模型

​ 主进程 + 多个 worker 子进程,这也是最常用的一种模型。这种方法的一个通用工作模式就是:
主进程执行 bind() + listen() 后,创建多个子进程;
​ 然后,在每个子进程中,都通过 accept() 或 epoll_wait() ,来处理相同的套接字。
​ 比如,最常用的反向代理服务器 Nginx 就是这么工作的。它也是由主进程和多个 worker 进程组成。主进程主要用来初始化套接字,并管理子进程的生命周期;而 worker 进程,则负责实际的请求处理。我画了一张图来表示这个关系。

img

​ 这里要注意,accept() 和 epoll_wait() 调用,还存在一个惊群的问题。换句话说,当网络 I/O 事件发生时,多个进程被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠。

  • 其中,accept() 的惊群问题,已经在 Linux 2.6 中解决了;

  • 而 epoll 的问题,到了 Linux 4.5 ,才通过 EPOLLEXCLUSIVE 解决。

​ 为了避免惊群问题, Nginx 在每个 worker 进程中,都增加一个了全局锁(accept_mutex)。这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒。

新Nginx模型

​ 监听到相同端口的多进程模型 在这种方式下,所有的进程都监听相同的接口,并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中去。这一过程如下图所示。

img

安装Nginx

编译安装Nginx

下载依赖包

[root@kuang-73 ~]# yum -y install zlib zlib-devel pcre pcre-devel openssl openssl-devel automake autoconf gcc gcc-c++

编译安装

[root@kuang-73 ~]# ./configure
–prefix=/usr/local/nginx
–user=nginx
–group=nginx
–without-http_uwsgi_module
–without-http_scgi_module
–without-http_browser_module
–with-pcre
–with-http_addition_module
–with-http_ssl_module
–with-http_realip_module
–with-http_sub_module
–with-http_flv_module
–with-http_mp4_module
–with-http_gzip_static_module
–with-http_gunzip_module
–with-http_secure_link_module
–with-http_stub_status_module
–add-module=…/nginx-rtmp-module-master #没有需要附加的模块就将该行删除
[root@kuang-73 ~]# make -j 4 && make install

模块说明

①实际使用模块
http_gzip_static_module 压缩功能
http_stub_status_module 内置的状态页
http_addition_module 过滤器的支持
http_dav_module 网页输入命令的支持(put等操作)
ngx_http_autoindex_module.o 目录浏览的模块,默认安装
ngx_http_auth_basic_module.o 基础认证,默认安装
mp4或者flv实现了在线MP4播放(已验证成功)
②编译携带参数
–with-http_addition_module #启用支持(作为一个输出过滤器,支持不完全缓冲,分部分相应请求)
–with-http_sub_module #启用支持(允许一些其他文本替换Nginx相应中的一些文本)
–with-http_flv_module #启用支持(提供支持flv视频文件支持)
–with-http_mp4_module #启用支持(提供支持mp4视频文件支持,提供伪流媒体服务端支持)

拓展nginx启动错误

①启动nginx报错
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx
nginx: [emerg] getpwnam(“nginx”) failed
②那是因为编译安装指定了用户nginx,所以要新增该用户
[root@kuang-73 ~]# useradd -s /sbin/nologin -M nginx

Nginx生产环境编译参数

①Nginx生产环境编译参数
–prefix=/data/nginx --user=nginx --group=nginx --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_ssl_module --with-http_mp4_module --with-pcre=/data/tmp/pcre-8.42 --with-openssl=/data/tmp/openssl-1.0.2o --with-zlib=/data/tmp/zlib-1.2.11 --with-http_v2_module --with-http_image_filter_module --with-stream
②编译参数说明
#注意:生产环境下的一些模块是通过编译安装添加的
with-stream用来实现TCP代理(而不是仅仅代理80或443端口),对应的模块为ngx_stream_core_module,默认是不安装,需要编译时增加–with-stream
③stream模块相比http模块的功能
只有http模块时,虽然在http代码块下的server代码块中可以写任意端口(除了80或443),但是Nginx代理你去访问的时候只会使用Http协议,所以使用http模块不可能实现其他服务的代理(比如MySQL的代理);
当有stream模块时,则可以通过stream模块实现TCP任意端口的代理(比如实现MySQL的代理)。

编译安装LNMP

安装LNMP架构需要的依赖包

[root@kuang-75 ~]# yum -y install make gcc gcc-c++ flex bison file libtool libtool-libs autoconf kernel-devel libjpeg libjpeg-devel libpng libpng-devel gd freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glib2 glib2-devel bzip2 bzip2-devel libevent ncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5-devel libidn libidn-devel openssl openssl-devel gettext gettext-devel ncurses-devel gmp-devel unzip libcap lsof

部署Nginx

1、安装Nginx
①安装Nginx依赖包
[root@kuang-75 ~]# yum -y install gcc gcc-c++ autoconf automake zlib zlib-devel openssl openssl-devel pcre*
②创建Nginx运行账户
[root@kuang-75 ~]# useradd -M -s /sbin/nologin nginx
③下载Nginx和pcre
http://nginx.org/download/nginx-1.14.1.tar.gz
ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.41.tar.gz
④解压Nginx和pcre到指定目录
[root@kuang-75 ~]# tar xf pcre-8.41.tar.gz -C /usr/local/src/
[root@kuang-75 ~]# tar xf nginx-1.14.2.tar.gz -C /usr/local/src/
⑤编译安装Nginx
[root@kuang-75 ~]# cd /usr/local/src/nginx-1.14.2/
[root@kuang-75 nginx-1.14.2]# ./configure --prefix=/usr/local/nginx --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module --with-http_flv_module --with-http_mp4_module --with-pcre=/usr/local/src/pcre-8.41 --user=nginx --group=nginx
[root@kuang-75 nginx-1.14.2]# make -j 4 && make install
⑥创建Nginx启动快捷键
[root@kuang-75 ~]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/

2、启用PHP支持
①备份配置文件
[root@kuang-75 ~]# cp /usr/local/nginx/conf/nginx.conf{,.bak}
②编辑配置文件支持PHP
[root@kuang-75 ~]# vim /usr/local/nginx/conf/nginx.conf
步骤1、修改用户nobody为nginx
步骤2、取消注释启用PHP段的location
步骤3、将/scripts替换成/usr/local/nginx/html
③创建PHP页面
[root@kuang-75 ~]# echo “<?php phpinfo(); ?>” > /usr/local/nginx/html/index.php
④启动Nginx
[root@kuang-75 ~]# nginx

部署MySQL5.6版本

#注要源码编译安装MySQL需要大于13G的磁盘空间
1、创建MySQL安装环境
①添加一块20G磁盘
②把系统自带的boost库卸载,源码编译安装高版本
[root@kuang-75 ~]# yum -y remove boost-* #MySQL5.6不依赖于该boost库,这步可省???
③卸载系统自带的mysql
[root@kuang-75 ~]# yum -y remove mysql mariadb-*
④安装依赖包
[root@kuang-75 ~]# yum install -y cmake make gcc gcc-c++ bison ncurses ncurses-devel #cmake版本要2.8以上
⑤添加用户和组
[root@kuang-75 ~]# groupadd mysql
[root@kuang-75 ~]# useradd -M -s /sbin/nologin -r -g mysql mysql

2、下载和解压MySQL源码包
①mysql程序包下载地址
http://www.mysql.com/Downloads/MySQL-5.6/mysql-5.6.35.tar.gz #MySQL5.6版本不需要boost库
②解压源码包
[root@kuang-75 ~]# tar xf mysql-5.6.35.tar.gz -C /usr/local/src/

3、规划安装目录
①安装目录: /var/lib/mysql
[root@kuang-75 ~]# mkdir -p /var/lib/mysql
②将新添磁盘挂载到mysql目录
[root@kuang-75 ~]# mount /dev/sdb1 /var/lib/mysql/
③创建数据目录: /var/lib/mysql/data
[root@kuang-75 ~]# mkdir -p /var/lib/mysql/data #第一步不能创建data目录,因为会被挂载覆盖
④修改权限
[root@kuang-75 ~]# chown -R mysql:mysql /var/lib/mysql

4、编译安装MySQL
①mysql-5.6.35的构建命令和参数
[root@kuang-75 mysql-5.6.35]# cmake -DCMAKE_INSTALL_PREFIX=/var/lib/mysql
-DMYSQL_DATADIR=/var/lib/mysql/data
-DSYSCONFDIR=/etc
-DWITH_MYISAM_STORAGE_ENGINE=1
-DWITH_INNOBASE_STORAGE_ENGINE=1
-DWITH_MEMORY_STORAGE_ENGINE=1
-DWITH_READLINE=1
-DMYSQL_UNIX_ADDR=/var/lib/mysql/mysql.sock
-DMYSQL_TCP_PORT=3306
-DENABLED_LOCAL_INFILE=1
-DWITH_PARTITION_STORAGE_ENGINE=1
-DEXTRA_CHARSETS=all
-DDEFAULT_CHARSET=utf8
-DDEFAULT_COLLATION=utf8_general_ci
②编译和安装
[root@kuang-75 mysql-5.6.35]# make -j 4 #编译过程很可能由于磁盘、内存和CPU不足出问题
[root@kuang-75 mysql-5.6.35]# make install
③修改MySQL文件的属主
[root@kuang-75 ~]# chown -R mysql:mysql /var/lib/mysql/

5、MySQL初始化配置
①编辑配置文件
[root@kuang-75 mysql]# cp support-files/my-default.cnf /etc/my.cnf
[root@kuang-75 ~]# vim /etc/my.cnf #前四项配置必须有,后面可省略
[mysqld]
basedir=/var/lib/mysql
datadir=/var/lib/mysql/data
port=3306
socket=/var/lib/mysql/mysql.sock #socket位置需按照编译安装时指定的socket位置
character-set-server=utf8
log-error=/var/log/mysqld.log
pid-file=/tmp/mysqld.pid
[mysql]
socket=/var/lib/mysql/mysql.sock
[client]
socket=/var/lib/mysql/mysql.sock
②编辑启动脚本
[root@kuang-75 mysql]# cp support-files/mysql.server /etc/init.d/mysqld
[root@kuang-75 ~]# chmod +x /etc/init.d/mysqld
[root@kuang-75 ~]# vim /etc/init.d/mysqld
basedir=/var/lib/mysql
datadir=/var/lib/mysql/data
③添加其他MySQL自带命令的快捷启动
[root@kuang-75 ~]# ln -s /var/lib/mysql/bin/* /usr/sbin/ #或者添加环境变量方式
④初始化数据库
[root@kuang-75 scripts]# ./mysql_install_db --basedir=/var/lib/mysql --datadir=/var/lib/mysql/data --user=mysql
⑤启动mysql
[root@kuang-75 ~]# /etc/init.d/mysqld start #启动后会自动生成socket文件
⑥安全初始化
[root@kuang-75 ~]# /var/lib/mysql/bin/mysql_secure_installation
⑦修改初始密码(如果未进行安全初始化)
[root@kuang-75 ~]# mysql
mysql> set password for root@localhost = password(‘123456’);
mysql> flush privileges;

部署PHP5.6

1、创建PHP安装环境
①依赖包下载地址(可省略,通过yum安装)
http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
http://iweb.dl.sourceforge.net/project/mcrypt/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz
http://iweb.dl.sourceforge.net/project/mcrypt/MCrypt/2.6.8/mcrypt-2.6.8.tar.gz
②下载依赖包
[root@kuang-75 ~]# yum -y install php-mcrypt libmcrypt libmcrypt-devel php-pear libxml2 libxml2-devel curl curl-devel libjpeg libjpeg-devel libpng libpng-devel freetype-devel

2、编译安装PHP
①下载并上传PHP
http://jp2.php.net/distributions/php-7.1.24.tar.gz #替换版本号,即可替换所下载的软件包
②解压到指定目录
[root@kuang-75 ~]# tar xf php-5.6.36.tar.gz -C /usr/local/src/
[root@kuang-75 ~]# cd /usr/local/src/php-5.6.36/
③编译安装PHP
[root@kuang-75 php-5.6.36]# ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/ --enable-fpm --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-iconv-dir --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --with-mcrypt --enable-ftp --with-gd --enable-gd-native-ttf --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip --enable-soap --without-pear --with-gettext --disable-fileinfo --enable-maintainer-zts
[root@kuang-75 php-5.6.36]# make -j 4 && make install

3、PHP初始化配置
①编辑配置文件
[root@kuang-75 php-5.6.36]# cp php.ini-production /usr/local/php/php.ini #PHP主配置文件
[root@kuang-75 php-5.6.36]# cd /usr/local/php/etc/
[root@kuang-75 etc]# mv php-fpm.conf.default php-fpm.conf #php-fpm配置文件
②修改运行用户
[root@kuang-75 etc]# vim php-fpm.conf
user = nginx
group = nginx
③编辑启动脚本
[root@kuang-75 ~]# cp /usr/local/src/php-5.6.36/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
[root@kuang-75 ~]# chmod +x /etc/init.d/php-fpm
[root@kuang-75 ~]# chkconfig php-fpm on #设置为开机启动
④启动php-fpm
[root@kuang-75 ~]# /etc/init.d/php-fpm start
[root@kuang-75 ~]# netstat -antup | grep 9000 #到此结束,Nginx已经可将PHP脚本提交上来处理

4、给user授权可修改配置和重启nginx权限
chown -R root:users /data/home/user00/nginx/sbin/nginx
chmod +s /data/home/user00/nginx/sbin/nginx

依赖使用源码包

1、上传和解压源码包
[root@kuang-75 soft]# ls #有两个pcre版本
nginx-1.15.6 pcre2-10.32 pcre-8.39 zlib-1.2.11

2、yum安装依赖包
[root@kuang-75 ~]# yum -y install gcc gcc-c++ openssl openssl-devel #openssl编译必装依赖,gcc-c++也是依赖的编译软件

3、指定依赖包位置build
①使用最新版的pcre来构建
[root@kuang-75 nginx-1.15.6]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre=…/pcre2-10.32 --with-zlib=…/zlib-1.2.11 #没报错,其中省略了其他模块选项,并且ssl可省略
[root@kuang-75 nginx-1.15.6]# make -j 4 #报如下错误
make[2]: 进入目录“/root/soft/pcre2-10.32”
make[2]: *** 没有规则可以创建目标“libpcre.la”。 停止。

②使用老版版的pcre来编译
[root@kuang-75 nginx-1.15.6]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre=…/pcre-8.39 --with-zlib=…/zlib-1.2.11
[root@kuang-75 nginx-1.15.6]# make -j 4 #顺利构建和编译
[root@kuang-75 nginx-1.15.6]# make install #安装后Nginx可用

Nginx重新编译

①重新build源码
[root@kuang-73 ~]# ./configure --prefix=/usr/local/nginx --user=nobody --group=nobody --with-pcre --with-http_ssl_module #继承原来所有的配置,另外添加额外想添加的模块
②重新编译源码
[root@kuang-73 ~]# make -j 4 #不能make install,否则就覆盖安装了
③生成新可执行二进制文件
[root@kuang-73 ~]# ls nginx-1.15.6/objs/nginx #新版本程序
④用新程序将旧程序覆盖
[root@kuang-73 ~]# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak #备份旧程序
[root@kuang-73 ~]# cp ./objs/nginx /usr/local/nginx/sbin/nginx #替换旧程序
⑤检查新科执行二进制文件
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -V #检查新添加的模块
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -t #检查新程序的语法

隐藏版本号

①修改版本号
[root@kuang-73 ~]# vim nginx-1.14.2/src/core/nginx.h
#define NGINX_VERSION “6.6.6”
#define NGINX_VER “kuang.cn/” NGINX_VERSION
#define NGINX_VAR “kuang.cn”
②避免头部信息暴露版本
[root@kuang-73 ~]# vim nginx-1.14.2/src/http/ngx_http_header_filter_module.c
static u_char ngx_http_server_string[] = “Server: KUANG” CRLF;
③修改http错误码的返回时携带的版本号
[root@kuang-73 nginx-1.15.6]# vim src/http/ngx_http_special_response.c
static u_char ngx_http_error_full_tail[] =


IIS
” CRLF
③http上下文中
server_tokens off;

调度算法

Nginx负载均衡算法

①轮询RR(默认)
每个请求按时间顺序逐一分配到不同的后端服务,如果后端某台服务器死机,自动剔除故障系统,使用户访问不受影响。
②加权轮询WRR
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。或者仅仅为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。
③加权最小连接数WLC(阿里云)
Nginx上实现加权最小连接还没有试验过???
④ip_hash
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题。
#注意:Nginx自带的这类方式对于小公司,并且也没有实现Cookie会话保持的,可以使用此方式,除此之外不推荐这类负载均衡方式。就连阿里云的SLB中都没有这类负载均衡的选项,而是基于cookie的会话保持。
⑤url_hash(第三方)
按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。(比喻:按公司业务分类,某一个业务找指定的部门)
⑥session stiky(第三方或收费)
会话保持基于cookie,推荐使用的方式
⑦fair(第三方)
比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。

Nginx负载均衡调度状态

①down,表示当前的server暂时不参与负载均衡
②backup,预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因此这台机器的访问压力最低
③max_fails,允许请求失败的次数,默认为1,当超过最大次数时,返回proxy_next_upstream模块定义的错误。
④fail_timeout,请求失败超时时间,在经历了max_fails次失败后,暂停服务的时间。max_fails和fail_timeout可以一起使用。

替换可执行二进制文件

①重新build源码
[root@kuang-73 ~]# ./configure --prefix=/usr/local/nginx --user=nobody --group=nobody --with-pcre --with-http_ssl_module #继承原来所有的配置,另外添加额外想添加的模块
②重新编译源码
[root@kuang-73 ~]# make -j 4 #不能make install,否则就覆盖安装了
③生成新可执行二进制文件
[root@kuang-73 ~]# ls nginx-1.15.6/objs/nginx #新版本程序
④用新程序将旧程序覆盖
[root@kuang-73 ~]# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak #备份旧程序
[root@kuang-73 ~]# cp ./objs/nginx /usr/local/nginx/sbin/nginx #替换旧程序
⑤检查新科执行二进制文件
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -V #检查新添加的模块
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -t #检查新程序的语法

添加systemctl开机启动脚本

1、配置systemctl的Unit控制脚本
[root@kuang-75 ~]# vim /usr/lib/systemd/system/nginx.service #同/lib/systemd/system/目录
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target

2、使用systemctl命令控制Nginx
①Nginx的一般控制
[root@kuang-75 ~]# systemctl status nginx
[root@kuang-75 ~]# systemctl start nginx
[root@kuang-75 ~]# systemctl stop nginx
[root@kuang-75 ~]# systemctl reload nginx #修改配置后加载成功
②添加到开机启动
[root@kuang-75 ~]# systemctl enable nginx.service #脚本中定义了WantedBy,所以创建如下开机启动规则
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

删除Nginx

①make命令删除
[root@kuang-73 nginx-1.15.6]# make uninstall #部分软件支持该命令,而且有时删除不干净
②直接删除安装目录
#安装时指定了安装目录
[root@kuang-73 ~]# rm -rf /usr/local/nginx #卸载或者备份时直接对目录进行操作
③查看makefile文件里有没提供卸载命令
[root@kuang-73 nginx-1.15.6]# vim Makefile
④whereis找出所有目录
[root@kuang-73 nginx-1.15.6]# whereis nginx | xargs rm -rf #注该命令只找Nginx软件运行时用到的几个目录
⑤使用find找出所有包含nginx的文件和目录
[root@kuang-73 ~]# find / -name nginx | xargs rm -rf

基础使用

server_name匹配

server_name匹配规则

①匹配IP地址和listen指令指定的IP和端口;
②将Host头字段作为字符串匹配server_name指令;
③将Host头字段与server_name指令值字符串的开始部分做匹配,选出最长泛匹配;
④将Host头字段与server_name指令值字符串的结尾部分做匹配,选出最长泛匹配;
⑤将Host头字段与server_name指令值进行正则表达式匹配,选择第一个匹配到的;
⑥如果所有Host头匹配失败,那么将会转向listen指令标记的default server;
⑦如果所有Host头匹配失败,并且没有default_server,那么将会转向满足第一步的第一个server的listen指令。

server_name格式

①精确匹配
server_name www.xuegod.cn
②泛解析
server_name .xuegod.cn; #替代部分子域名
server_name www.xuegod.
; #替代部分顶级域
③正则表达式匹配
#注意:正则匹配时~和要匹配的字符不要带空格,和location匹配有点区别
server_name ~^www.example.comKaTeX parse error: Undefined control sequence: \d at position 22: …ver_name ~^www(\̲d̲+)\ .example\.(… ;
server_name ~^.*.xuegod.cnKaTeX parse error: Expected 'EOF', got '#' at position 6: ; #̲以任何字符开头并xuegod.…;
return 301 https:// h o s t host hostrequest_uri; #注意使用正则匹配servername时这里强制跳转就要用host,而不用server_name

丢弃无host字段的请求

server {
listen 80;
server_name “”;
return 444;
}

location匹配

CLB的location匹配规则

①如果命中精确匹配,则优先精确匹配,并终止匹配;
②如果命中多个前缀匹配,则记住最长的前缀匹配,并继续匹配;
③如果最长的前缀匹配是优先前缀匹配,则命中此最长的优先前缀匹配,并终止匹配;
④如果最长的前缀匹配是非优先前缀匹配,并且命中多个正则匹配,使用第一个命中的正则匹配,并终止匹配。否则匹配最长的前缀匹配,终止匹配。

URL尾部的/需不需要

关于URL尾部的/有三点也需要说明一下。第一点与location配置有关,其他两点无关。

  • location中的字符有没有/都没有影响。也就是说/user/和/user是一样的。
  • 如果URL结构是https://domain.com/的形式,尾部有没有/都不会造成重定向。因为浏览器在发起请求的时候,默认加上了/。虽然很多浏览器在地址栏里也不会显示/。这一点,可以访问baidu验证一下。
  • 如果URL的结构是https://domain.com/some-dir/。尾部如果缺少/将导致重定向。因为根据约定,URL尾部的/表示目录,没有/表示文件。所以访问/some-dir/时,服务器会自动去该目录下找对应的默认文件。如果访问/some-dir的话,服务器会先去找some-dir文件,找不到的话会将some-dir当成目录,重定向到/some-dir/,去该目录下找默认文件。可以去测试一下你的网站是不是这样的。

#注意:通过URL访问的Nginx的目录为root+location

location的作用

给自己树立一个目录结构,并对每一个目录设置对应的权限和定位等配置。
通过URL访问的Nginx的目录为root+location。

location匹配规则详解

①精确匹配
方法1、等号为精确匹配
location = / #匹配到了马上停止匹配,如果一个网站访问这个比较多,可以提高访问速度
方法2、另一种精确匹配(完整的URI)
location /data/image.html #隐式的精确匹配
②模糊匹配
location /synchrony #模糊匹配,URI里已/synchrony开头的,已验证
例1、http://localhost/synchrony/app/index.html
例2、http://localhost/synchrony-pass/index.html
location ^~ / #指的非正则匹配,意思当被选为最长普通匹配时,就不要匹配正则了(我当老大了就没正则什么事了)
③正则匹配
区分大小写
location ~
④正则匹配,但是不区分大小写
location ~* .TXT$
⑤命名location
location @fallback #不是用来处理普通的HTTP请求的,专门用来内部重定向的(仅对内部访问重定向)
#在server上下文中配置
error_page 404 = @fallback;
location @fallback {
proxy_pass http://www.nginx.org;
}
#当访问不存在的http://192.168.7.3/en/ 时将会重定向到http://www.nginx.org/en/

location嵌套

​ location /htdocs {
​ alias /usr/share/phpldapadmin/htdocs;
​ index index.php;
​ location ~ .php$ {
​ alias /usr/share/phpldapadmin;
​ fastcgi_pass 127.0.0.1:9000;
​ fastcgi_index index.php;
​ fastcgi_param SCRIPT_FILENAME d o c u m e n t r o o t document_root documentrootfastcgi_script_name;
​ include fastcgi_params;
​ }
​ }

Nginx分离location

img

localtion优先级案例

img

Web服务共享目录

共享非根目录

①创建共享目录
[root@kuang-73 ~]# cd /usr/local/nginx/html/
[root@kuang-73 html]# mkdir -p good/soso/sdf/
②配置共享目录

        location /soso {
            root html/good;    #每个location有自己的根目录,如未申明,就用上级代码块中的根目录;这里写绝对路径也可以/usr/local/nginx/html/good;
            autoindex on;
        }

③测试共享目录
http://192.168.7.3/soso/
④分析实现过程
步骤1、URI被location /soso所匹配到,所以要对此提供服务
步骤2、去它定义的根html/good(这是个相对路径,相对/usr/local/nginx)找soso
步骤3、如果soso是个文件则直接输出文件内容,如果soso是个目录则输出目录(ftp站点效果)

通过软链方式共享目录

①配置Nginx服务器支持自动索引
[root@kuang-73 nginx]# vim conf/nginx.conf #在http上下文中配置如下
location / {
root html;
index index.html index.htm;
autoindex on; #开启自动索引即可
}
②创建URI的软链接
[root@kuang-73 ~]# ln -s /mnt/ /usr/local/nginx/html/centos
③测试访问
http://192.168.7.3/centos/

通过alias方式共享目录

①配置虚拟目录指向光盘挂载点
[root@kuang-73 nginx]# vim conf/nginx.conf #在http上下文中配置如下
location /my {
alias /mnt;
autoindex on;
}
②访问测试
http://192.168.7.3/my

使用Django实现上传文件

1、下载upload模块并编译安装Nginx
①nginx-upload-module模块下载地址
https://github.com/fdintino/nginx-upload-module
②编译安装Nginx

[root@kuang-73 nginx-1.15.6]# ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --add-module=/root/nginx-upload-module-2.3.0/
[root@kuang-73 nginx-1.15.6]# make -j 4 && make install

2、Nginx配置调用Django
①Nginx配置脚本

server {
    listen *:80 default_server;
    server_name 192.168.1.251;
    client_max_body_size 20m;
    client_body_buffer_size 512k;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header REMOTE_ADD $remote_addr;
    location /upload {
	upload_pass @python;
	upload_store /tmp/nginx_upload;
	upload_store_access user:rw group:rw all:rw;
	set $upload_field_name "file";
	upload_set_form_field "${upload_field_name}_name" $upload_file_name;
	upload_set_form_field "${upload_field_name}_content_type" $upload_content_type;
	upload_set_form_field "${upload_field_name}_path" $upload_tmp_path;
	upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5;
	upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size;
	upload_pass_form_field "^.*$";
	upload_limit_rate 0;
	upload_cleanup 400 404 499 500-505;
	upload_pass_args on;  
    }
    location @python {
	proxy_pass http://localhost:9999;
    }
}

②创建Django项目
未完待续

使用stream模块实现四层代理

1、使用stream模块实现四层代理
①stream模块实现ssh代理方法
#stream模块用法跟http模块类似,编译安装时添加参数–with-stream
[root@kuang73 ~]# vim /usr/local/nginx/conf/nginx.conf #添加如下配置

stream {    
        upstream ssh {
                server 192.168.7.6:22;
        }
        server {
                listen 22;
                proxy_pass ssh;
                proxy_connect_timeout 1h;
                proxy_timeout 1h;
        }
}

注意:stream没有http模块的很多功能,比如基本的location和root都没有
②将作反向代理服务器的SSH服务关闭
[root@kuang73 ~]# systemctl stop sshd.service
③重新加载Nginx配置文件
[root@kuang73 ~]# /usr/local/nginx/sbin/nginx -s reload
[root@kuang73 ~]# netstat -antup | grep 22 #此时为Nginx监听22端口
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 3239/nginx: master
④验证Nginx代理SSH服务(成功跳转到kuang-76)
kuang@kuang-ubuntu:~$ ssh root@192.168.7.3
root@192.168.7.3’s password:
Last login: Sat Jul 13 12:44:04 2019 from 192.168.7.2
[root@kuang-76 ~]#

获取客户端请求的真实IP

获取客户端请求的真实IP地址:

location / {
    proxy_pass https://192.168.10.3:443/;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

rewrite和return跳转

rewrite基本操作

1、rewrite常见用法
①方便统一管理,一个目录对应一个子域名,将连接跳转到其他服务器上
案例1、将www.myweb.com/connect 跳转到connect.myweb.com
rewrite ^/connect$ http://connect.myweb.com permanent;
rewrite ^/connect/(.)$ http://connect.myweb.com/ 1 p e r m a n e n t ; ②网站转移后重定向到新的服务器 r e w r i t e / ( . ∗ ) 1 permanent; ②网站转移后重定向到新的服务器 rewrite ^/(.*) 1permanent;网站转移后重定向到新的服务器rewrite/(.) http://www.baidu.com/ 1 p e r m a n e n t ; r e w r i t e / ( . ∗ ) 1 permanent; rewrite ^/(.*) 1permanent;rewrite/(.) https://$hostKaTeX parse error: Expected 'EOF', got '#' at position 82: …w.myweb.com #̲用户输入URL时忘记带www了…host != ‘www.myweb.com’ ) {
rewrite ^/(.
)$ http://www.myweb.com/KaTeX parse error: Expected 'EOF', got '}' at position 19: …ermanent; }̲ ④实现伪静态,隐蔽真实目录结…request_uri ~ “/?gid=6”){return http://www.myweb.com/123.html;}
⑤静态页面跳转到动态页面
案例1、www.myweb.com/admin/ 下跳转为www.myweb.com/admin/index.php?s=
if (!-e KaTeX parse error: Expected '}', got 'EOF' at end of input: …te ^/admin/(.*) /admin/index.php?s=/KaTeX parse error: Expected 'EOF', got '}' at position 13: 1 last; }̲ 案例2、rewrite ^/… /resizer/$1.$4?width=$2&height=KaTeX parse error: Undefined control sequence: \- at position 161: …s/activies/2014\̲-̲([0-9]+)\-([0-9… http://www.myweb.com/news/activies/ 3 p e r m a n e n t ; ⑦多条件重定向 r e w r i t e 案例 1 、需要打开带有 p l a y 的链接就跳转到 p l a y ,不过 / a d m i n / p l a y 这个不能跳转 i f ( 3 permanent; ⑦多条件重定向rewrite 案例1、需要打开带有play的链接就跳转到play,不过/admin/play这个不能跳转 if ( 3permanent;多条件重定向rewrite案例1、需要打开带有play的链接就跳转到play,不过/admin/play这个不能跳转if(request_filename ~ (.)/play){ set KaTeX parse error: Expected 'EOF', got '}' at position 12: payvar '1';}̲ if (request_filename ~ (.)/admin){ set KaTeX parse error: Expected 'EOF', got '}' at position 12: payvar '0';}̲ if (payvar ~ ‘1’){
rewrite ^/ http://play.myweb.com/ break;
}

2、rewrite其他应用
①使用全站加密,http自动跳转https
方法1、在页面里加js脚本
方法2、后端程序里写重定向
方法3、Web服务器实现重定向跳转
在http的server上下文里添加rewrite ^(.*) https://$host$1 permanent;

3、rewrite重写规则
①条件语句if中使用正则表达式匹配
~ 为区分大小写匹配
~* 为不区分大小写匹配
!和!*分别为区分大小写不匹配及不区分大小写不匹配
②文件及目录匹配
-f和!-f用来判断是否存在文件
-d和!-d用来判断是否存在目录
-e和!-e用来判断是否存在文件或目录
-x和!-x用来判断文件是否可执行

4、rewrite的flag标记
①last 相当于apache里面的[L]标记,表示rewrite。
步骤1、结束当前的请求处理,用替换后的URI重新匹配location;
步骤2、可理解为重写(rewrite)后,发起了一个新请求,进入server模块,匹配location;
步骤3、如果重新匹配循环的次数超过10次,nginx会返回500错误;
步骤4、返回302 http状态码 ;
步骤5、浏览器地址栏显示重地向后的url
②break本条规则匹配完成后,终止匹配,不再匹配后面的规则。
步骤1、结束当前的请求处理,使用当前资源,不在执行location里余下的语句;
步骤2、返回302 http状态码 ;
步骤3、浏览器地址栏显示重地向后的url
③redirect 返回302临时重定向,浏览器地址会显示跳转后的URL地址。
步骤1、临时跳转,返回302 http状态码;
步骤2、浏览器地址栏显示重地向后的url
④permanent 返回301永久重定向,浏览器地址会显示跳转后的URL地址。
步骤1、永久跳转,返回301 http状态码;
步骤2、浏览器地址栏显示重定向后的url
⑤last和break区别
使用last和break实现URI重写,浏览器地址栏不变;
使用alias指令必须用last标记;
使用proxy_pass指令时,需要使用break标记;
Last标记在本条rewrite规则执行完毕后,会对其所在server{…}标签重新发起请求;
break标记则在本条规则匹配完成后,终止匹配。

5、Apache和Nginx规则的对应关系
Apache的RewriteCond对应Nginx的if
Apache的RewriteRule对应Nginx的rewrite
Apache的[R]对应Nginx的redirect
Apache的[P]对应Nginx的last
Apache的[R,L]对应Nginx的redirect
Apache的[P,L]对应Nginx的last
Apache的[PT,L]对应Nginx的last

6、Nginx全局变量
arg_PARAMETER #这个变量包含GET请求中,如果有变量PARAMETER时的值。
args #这个变量等于请求行中(GET请求)的参数,如:foo=123&bar=blahblah;
binary_remote_addr #二进制的客户地址。
body_bytes_sen t #响应时送出的body字节数数量。即使连接中断,这个数据也是精确的。
content_length #请求头中的Content-length字段。
content_type #请求头中的Content-Type字段。
cookie_COOKIE #cookie COOKIE变量的值
document_root #当前请求在root指令中指定的值。
document_uri #与uri相同。
host #请求主机头字段,否则为服务器名称。
hostname #Set to themachine’s hostname as returned by gethostname
http_HEADER
is_args #如果有args参数,这个变量等于”?”,否则等于”",空值。
http_user_agent #客户端agent信息
http_cookie #客户端cookie信息
limit_rate #这个变量可以限制连接速率。
query_string #与args相同。
request_body_file #客户端请求主体信息的临时文件名。
request_method #客户端请求的动作,通常为GET或POST。
remote_addr #客户端的IP地址。
remote_port #客户端的端口。
remote_user #已经经过Auth Basic Module验证的用户名。
request_completion #如果请求结束,设置为OK. 当请求未结束或如果该请求不是请求链串的最后一个时,为空(Empty)。
request_method #GET或POST
request_filename #当前请求的文件路径,由root或alias指令与URI请求生成。
request_uri #包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。不能修改。
scheme #HTTP方法(如http,https)。
server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
server_addr #服务器地址,在完成一次系统调用后可以确定这个值。
server_name #服务器名称。
server_port #请求到达服务器的端口号。

rewrite实现com跳转到cn

在vhost配置文件添加如下跳转配置

server {
        listen       80;
        server_name  fenxiao-test.edsmall.com;
        return 301 https://$server_name$request_uri;#return301高效一些
    }

server {
        listen       443;
        server_name  fenxiao-test.edsmall.com;
        location / {
            #rewrite ^(.*)\.edsmall\.com/(.*)$ $1\.edstao\.com/$2 permanent;
            rewrite ^/(.*)$ http://fenxiao-test.edstao.com/$1 permanent;
        }
}
rewrite配置手机和电脑访问不同页面

[root@kuang-73 nginx]# vim conf/nginx.conf #上下文为location

        set $mobile_rewrite do_not_perform;
        ## chi http_useragent for mobile /smart phones ##
        if ($http_user_agent ~* "(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian
|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino") {
            set $mobile_rewrite perform;
        }   
        if ($http_user_agent ~* "^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|c
dm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-
c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|
ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|u
c)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v 
)|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|
yas\-|your|zeto|zte\-)") {
            set $mobile_rewrite perform;
        }   
            

        if ($mobile_rewrite = perform) {
            return 301 $scheme://tao.edsmall.cn;
        }   

return的使用

1、return使用说明
该指令一般用于对请求的客户端直接返回响应状态码。在该作用域内return后面的所有nginx配置都是无效的。 可以使用在server、location以及if配置中。 除了支持跟状态码,还可以跟字符串或者url链接。

2、直接返回状态码

server{
    listen 80;
    server_name www.aming.com;
    return 403;
    rewrite /(.*) /abc/$1;    #该行配置不会被执行。
}

3、返回字符串
①要想返回字符串,必须要加上状态码,否则会报错

server{
    listen 80;
    server_name www.aming.com;
    return 200 "hello";
}

②还可以支持json数据

location ^~ /aming {
    default_type application/json ;
    return 200  '{"name":"aming","id":"100"}';
}

③也支持写一个变量

location /test {
    return 200 "$host $request_uri";
}

4、返回URL

server{
    listen 80;
    server_name www.aming.com;
    return 301 http://www.aminglinux.com/123.html;
    rewrite /(.*) /abc/$1;    #该行配置不会被执行。
}

#注意:return后面的url必须是以http://或者https://开头的。

proxy实现错误码跳转页面

普通404页面跳转

①定义404跳转页面
[root@AWKD-LinuxTest2-10 ~]# vim /etc/nginx/nginx.conf #这里只显示精简代码内容

    server {
        listen       80 default_server;
        root         /usr/share/nginx/html;
        location / {
        #error_page 404 /404.html;    #错误页面跳转也可以在location中,因为是在此截获的404错误码
        }
        error_page 404 /404.html;    #指定错误码404跳转后的错误页面
            location = /404.html {
        }

②创建404测试页面
[root@AWKD-LinuxTest2-10 ~]# echo “deny” >> /usr/share/nginx/html/404.html #假装友好错误界面内容是deny
③测试跳转
http://47.97.2.118/good #浏览器中输入一个服务器中没有的资源,实现如下404错误跳转

img

Nginx作反向代理时的错误页面跳转

①定义404跳转页面
[root@AWKD-ZABBIX-172 nginx]# vim conf/vhost/71-pre.edsmall.com.conf

server {
        listen       443;
        ... ...
        root /data/nginx/html;
        location / {
        proxy_pass http://71-pre;
        ... ...
        proxy_intercept_errors on;    #重点:Nginx作反向代理时默认情况下,Nginx不支持自定义404错误页面,只有这个指令被设置为on,Nginx才支持将404错误重定向
        #error_page 404 /404.html;    #错误页面跳转也可以在location中,因为是在此截获的404错误码
    }
    error_page 404 /404.html;    #当上游服务器响应头回来后,可以根据响应状态码的值进行拦截错误处理,与error_page 指令相互结合。用在访问上游服务器出现错误的情况下。
    location = /404.html {
        root /data/nginx/html;
    }

②创建404测试页面
[root@AWKD-ZABBIX-172 nginx]# echo “deny” >> /data/nginx/html/404.html #假装友好错误界面内容是deny
③测试跳转
https://71-pre.edsmall.com/soso #浏览器中输入一个服务器中没有的资源,实现如下404错误跳转

img

连接超时

①上下文为http

keepalive_timeout  65;    #该项依赖系统没有设置立即回收资源,效果是65秒内客户端一直向服务端发送keepalive,服务端也回应keepalive的ack包,确认保持联系。一到65秒服务端就发送FIN信号。
client_header_timeout  60;    #指定等待客户端发送请求头的超时时间
client_body_timeout  60;    #设置请求体读取超时时间

②连接超时作用
在企业网站中,为避免同一个客户长时间占用连接,造成服务器资源浪费,可以设置相应的连接超时参数,实现控制连接访问时间

③系统设置的tcp_keepalive_timeout时间
[root@kuang-75 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_time #默认为一个小时
7200

配置Nginx支持PHP

①安装使用php的包
[root@kuang-73 ~]# yum -y install php php-fpm
[root@kuang-73 ~]# systemctl start php-fpm
[root@kuang-73 ~]# ss -lntup #端口9000起来了

②在server代码模块当中增加如下locatin上下文模块

        location ~ \.php$ {
            root /usr/share/nginx/html;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;    #这条可以注释掉,不懂意思
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

③添加简单PHP页面
[root@kuang-73 ~]# echo “<?php phpinfo(); ?>” > /usr/local/nginx/html/index.php
echo “<?php phpinfo(); ?>” > index.php
④测试PHP页面访问
http://192.168.7.3/index.php

fastcgi配置和使用

fastcgi配置和使用

fastcgi也是一个反代,和proxy语法类似
①安装使用php的包
[root@kuang-73 ~]# yum -y install php php-fpm
[root@kuang-73 ~]# systemctl start php-fpm
[root@kuang-73 ~]# ss -lntup #端口9000起来了
②上下文http中配置fastcgi

    fastcgi_connect_timeout 300;    #连接到后端fastcgi的超时时间
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;    #应答的超时时间
    fastcgi_buffer_size 64k;    #默认等于内存页面大小
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;
    fastcgi_cache_path /tmp/nginx_temp levels=2:2 keys_zone=ngx_fcgi_cache:512m inactive=1d max_size=20g;
③在server上下文中使用fastcgi
        location ~ \.php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;   #导入变量名文件
            fastcgi_param  SCRIPT_FILENAME  /usr/local/nginx/html$fastcgi_script_name;
            fastcgi_cache ngx_fcgi_cache;
            fastcgi_cache_valid 200 302 1h;
            fastcgi_cache_valid 301 1d;
            fastcgi_cache_valid any 1m;
            fastcgi_cache_min_uses 1;
            fastcgi_cache_use_stale error timeout invalid_header http_500;
            fastcgi_cache_key http://$host$request_uri;
        }

④配置使用php
[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -s reload #重载配置文件
[root@kuang-73 ~]# echo “<?php phpinfo(); ?>” > /usr/local/nginx/html/index.php
#此时已可以访问http://192.168.7.3/index.php
⑤使用fastcgi缓存功能
[root@kuang-73 ~]# systemctl stop php-fpm #此时关闭php-fpm服务,换一个浏览器,依然可以访问http://192.168.7.3/index.php
#原因是fastcgi生成了缓存
[root@kuang-73 ~]# ls /tmp/nginx_temp/ #nginx缓存目录
#将以上缓存目录删除之后,则不再能访问http://192.168.7.3/index.php

查看缓存机制运行状态

①专门生成一个进程处理cache
[root@kuang-73 ~]# ps -ef | grep nginx
nginx 123058 123053 0 15:57 ? 00:00:00 nginx: cache manager process
②目录的名称是基于请求URL通过哈希算法获取到的
/usr/local/nginx/cache/d/91/972fbe600d30f1cc92495981969ff91d

参数说明

①buffer相关参数

fastcgi_buffering 默认开启缓冲,为了Nginx能尽最快速度的接收FastCGI服务器的响应,将响应内容放入内存的buffer中。但是如果buffer放不下某一整个响应,其中一部分会被放入临时文件。
fastcgi_buffer_size 指Nginx一次性能从FastCGI服务器接收的最大的响应数据
fastcgi_busy_buffers_size 指在Nginx还没有读完一个响应时,指定这部分大小buffer可以用来忙于响应客户端
fastcgi_temp_file_write_size 指一次性能写多大的数据到临时文件,被fastcgi_buffer_size和fastcgi_buffers两个共同限制(比如buffer_size为64k,数量buffers为4,那么最大为256k)
fastcgi_max_temp_file_size 指最大缓存文件大小

②设置内存cache和磁盘cache

#设置web缓存区的名字为cache_one ,内存缓存空间大小为200M, 自动清除超过1天没被访问的缓存数据,磁盘缓存空间大小为30GB
fastcgi_cache_path /data/fastcgi_cache_path levels=2:2 keys_zone=cache_one:200m inactive=1d max_size=30g;
fastcgi_cache_path 跟Redis类似,Key-Value都能放得下则都放在内存,实在放不下,就会将Value放在磁盘文件
keys_zone 用来存Key(热点内容放在内存)
inactive  失效时间1天,相当于Redis的LRU
max_size  最大磁盘占用缓存空间

③引用内存cache并配置相应参数

fastcgi_cache ngx_fcgi_cache 引用名为ngx_fcgi_cache命名空间作为cache的内存
fastcgi_cache_valid 200 302 1h 状态码为200和302的响应在磁盘中保存1小时
fastcgi_cache_min_uses 1 第几次访问时就将响应缓存起来(这里指缓存到磁盘)
fastcgi_cache_use_stale error timeout invalid_header http_500 如果客户访问的某个页面出现error之后的这些错误,那么Nginx服务器将使用本地的缓存来响应客户
fastcgi_cache_key 指定Key的对象是什么
引入fastcgi_param参数

需要在conf目录中包含env_release文件。

server  {
        listen   8080   ;
        server_name  _;
        access_log  logs/message-notifier.access.log  main;
        error_log    logs/message-notifier.error.log    error;
        root /data/home/user00/nginx/html/message-notifier/;
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param  ITOP_INCLUDE_PATH $document_root/../../include;
            include fastcgi_params;
            include env_release;
        }
}

Nginx添加Perl支持

Perl的使用场景:如果对于一个绝大部分内容是静态的网站,只有极少数的地方需要动态显示,碰巧你又了解一点perl知识,那么nginx + perl的结合就能很好解决问题。

①安装Nginx添加Perl支持
[root@kuang-75 nginx-1.14.2]# yum -y install perl-devel perl-ExtUtils-Embed #安装依赖包
[root@kuang-75 nginx-1.14.2]# ./configure --with-http_perl_module
[root@kuang-75 nginx-1.14.2]# make -j 4 && make install

②配置把perl脚本写在外部文件中
#位于http配置中
perl_modules perl/lib;
perl_require test.pm;
#位于server配置中
location /user/ {
perl pkg_name::process;
}
#配置解释
上述配置是把所有来自http://servername/user/下的请求交由test.pm脚本中定义的process方法来处理。

③进入根目录添加Perl库目录
[root@kuang-75 nginx]# mkdir -p perl/lib && cd perl/lib
[root@kuang-75 lib]# vim test.pm
package pkg_name;
use Time::Local;
use nginx;
sub process {
my $r = shift;
$r->send_http_header(‘text/html; charset=utf-8’);
my @arr = split(‘/’, $r->uri);
my u s e r n a m e = @ a r r [ 2 ] ; i f ( ! username = @arr[2]; if (! username=@arr[2];if(!username || ($username eq “”)) {
$username = “Anonymous”;
}
$r->print('Hello, You name is : ’ . $username . ‘’);
$r->rflush();
return;
}
1;
③访问测试
http://192.168.7.5/user/kuang
Hello, You name is : kuang

Nginx使用lua

Nginx安装lua支持

#注:需要LuaJIT-2.0.4.tar.gz,ngx_devel_kit,lua-nginx-module
1.下载安装LuaJIT-2.0.4.tar.gz
[root@kuang-74 ~]# wget -c http://luajit.org/download/LuaJIT-2.0.4.tar.gz
[root@kuang-74 ~]# tar xzvf LuaJIT-2.0.4.tar.gz
[root@kuang-74 ~]# cd LuaJIT-2.0.4
[root@kuang-74 ~]# make install PREFIX=/usr/local/luajit
[root@kuang-74 ~]# export LUAJIT_LIB=/usr/local/luajit/lib #增加环境变量
[root@kuang-74 ~]# export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
2.下载解压ngx_devel_kit
[root@kuang-74 ~]# cd /usr/local
[root@kuang-74 ~]# wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
[root@kuang-74 ~]# tar -xzvf v0.3.0.tar.gz
3.下载解压lua-nginx-module
[root@kuang-74 ~]# cd /usr/local
[root@kuang-74 ~]# wget https://github.com/openresty/lua-nginx-module/archive/v0.10.8.tar.gz
[root@kuang-74 ~]# tar -xzvf v0.10.8.tar.gz
4.下载安装nginx-1.10.3.tar.gz(注意:新版本Nginx安装不成功,可能需要新版lua模块)
[root@kuang-74 ~]# cd /usr/local
[root@kuang-74 ~]# wget http://nginx.org/download/nginx-1.10.3.tar.gz
[root@kuang-74 ~]# tar -xzvf nginx-1.10.3.tar.gz
[root@kuang-74 ~]# cd nginx-1.10.3
[root@kuang-74 nginx-1.10.3]# ./configure --add-module=…/ngx_devel_kit-0.3.0 --add-module=…/lua-nginx-module-0.10.8
[root@kuang-74 nginx-1.10.3]# make -j 4 && make install
5、配置使用lua
①Nginx主配置文件中使用lua
[root@kuang-74 ~]# vim /usr/local/nginx/conf/nginx.conf
location /hello { #使用lua方式一直接在Nginx主配置中使用
default_type ‘text/plain’;
content_by_lua ‘ngx.say(“hello, lua”)’;
}
location /lua { #使用lua方式二在文本中调用
default_type ‘text/html’;
content_by_lua_file conf/lua/test.lua; #相对于Nginx安装目录
}
[root@kuang-75 ~]# mkdir /usr/local/nginx/conf/lua
[root@kuang-74 ~]# vim /usr/local/nginx/conf/lua/test.lua
ngx.say(“hello world”);
②创建库的链接
[root@kuang-74 ~]# ln -s /usr/local/luajit/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
6、测试访问
①访问http://127.0.0.1/hello
显示:hello, lua
②访问http://127.0.0.1/lua
显示:hello world

Nginx结合lua动态选择upstream

①配置Nginx

http {
    lua_shared_dict _ups_zone 64m;
    ...
    upstream qq_backend{
        server 192.168.7.6;
        server 192.168.7.7;
    }
 
    server {
        listen       80;
        server_name  www.kuang.com;
        access_log  off;
        location = /ups_update {
            content_by_lua '
                local ups = ngx.req.get_uri_args()["ups"]
                if ups == nil then
                    ngx.say("usage: /ups_update?ups=x.x.x.x")
                    return
                end
                local host = ngx.var.http_host
                local ups_from = ngx.shared._ups_zone:get(host);
                ngx.log(ngx.WARN, host, " update upstream from ", ups_from, " to ", ups)
                ngx.shared._ups_zone:set(host, ups);
                ngx.say(host, " update upstream from ", ups_from, " to ", ups)
            ';
        }
        location / {
            set_by_lua $cur_ups '
                local ups = ngx.shared._ups_zone:get(ngx.var.http_host)
                if ups ~= nil then
                    ngx.log(ngx.ERR, "get [", ups, "] from ngx.shared._ups_zone")
                    return ups
                end
                --ngx.log(ngx.INFO, "use default upstream");
                return "qq_backend";
            '
            proxy_next_upstream off;
            proxy_set_header    Host $host;
            proxy_http_version  1.1;
            proxy_set_header    Connection  "";
            proxy_pass $scheme://$cur_ups;
        }
    }
}

②配置upstream服务器
[root@kuang-76 ~]# systemctl start ngtinx
[root@kuang-76 ~]# echo kuang-76 > /usr/share/nginx/html/index.html
[root@kuang-77 ~]# systemctl start nginx.service
[root@kuang-77 ~]# echo kuang-77 > /usr/share/nginx/html/index.html

3、测试效果

①先看默认访问效果

默认负载均衡为轮训

img

②绑定后端服务器

[root@kuang-75 ~]# curl -x 127.0.0.1:80 www.kuang.com/ups_update?ups=192.168.7.6

img

再次访问,该客户端的请求永远都被转发到绑定的后端服务器上

img

③修改后端绑定的服务器

[root@kuang-75 ~]# curl -x 127.0.0.1:80 www.kuang.com/ups_update?ups=192.168.7.7

选项x代表使用指定的代理和端口,也可以在hosts当中映射好www.kuang.com

img

在浏览器中测试效果一致

Nginx结合lua动态代理端口

1、动态代理端口
①需求
需求大致如下:通过url传参的方式,让Nginx代理到不同的服务器
浏览器输入:http://127.0.0.1/remote?port=8081被代理到:http://192.168.108.2:8081

②Nginx配置

server {
    location /remote {
        set_form_input $remotePort '';
        access_by_lua '
            local arg = ngx.req.get_post_args()
            ngx.var.remotePort = arg["port"]
        ';
        proxy_pass http:192.168.108.2://$remotePort;
    }
}

Nginx支持Confluence的Websocket连接

1、Websocket相关模块
①Nginx支持Websocket所需模块
map指令使用ngx_http_map_module模块提供的,默认情况下,Nginx有加载这个模块,除非人为的 --without-http_map_module。
②ngx_http_map_module模块作用
ngx_http_map_module模块可以创建变量,这些变量的值与另外的变量值相关联。允许分类或者同时映射多个值到多个不同值并储存到一个变量中,map指令用来创建变量,但是仅在变量被接受的时候执行视图映射操作,对于处理没有引用变量的请求时,这个模块并没有性能上的缺失。

2、Nginx主配置修改
根据客户端请求中 h t t p u p g r a d e 的值来构造改变 http_upgrade 的值来构造改变 httpupgrade的值来构造改变connection_upgrade的值
[root@AWKD-ZABBIX-172 ~]# vim /data/nginx/conf/nginx.conf #http代码模块中添加如下配置信息

http {
    ... ...
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }
}
#如果$http_upgrade 不为 '' (空),则$connection_upgrade为upgrade
#如果$http_upgrade 为 '' (空),则$connection_upgrade为close

3、特定二级域名的Server-Location配置部分
①新增Websocket反代配置
[root@AWKD-ZABBIX-172 ~]# vim /data/nginx/conf/vhost/confluence.edsmall.com.conf

upstream confluence {
       server 172.16.239.243:8090 max_fails=10 fail_timeout=5;
    }
upstream synchrony {
       server 172.16.239.243:8091 max_fails=10 fail_timeout=5;
    }

        location / {
            proxy_pass http://confluence;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            #prwxy_set_header X-Real-IP $remote_addr;
            proxy_set_header x-forwarded-for $remote_addr;
            #proxy_set_header X-Forwarded-For $http_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
            proxy_intercept_errors on;
        }
        location /synchrony {
            proxy_set_header Host $host;
            #prwxy_set_header X-Real-IP $remote_addr;
            proxy_set_header x-forwarded-for $remote_addr;
            #proxy_set_header X-Forwarded-For $http_x_forwarded_for;
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
            proxy_intercept_errors on;

            #Websocket configuration
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_pass http://synchrony;
        }

②Websocket反代配置说明

proxt_http_version 1.1;    #表示反向代理发送的HTTP协议的版本是1.1,HTTP1.1支持长连接
proxy_set_header Upgrade $http_upgrade;    #表示设置Upgrade不变
proxy_set_header Connection $connection_upgrade;    #这里提供另外一种写法,但是实际生产中还是按照上面配置的来
proxy_pass http://synchrony;    #反向代理的URI

4、重新加载Nginx配置
[root@AWKD-ZABBIX-172 ~]# /data/nginx/sbin/nginx -s reload

5、官网参考文档
http://nginx.org/en/docs/http/websocket.html

Nginx并发测试

Nginx并发预估

①预估算法
{(内存G)*1024-System}/请求大小(单位M)

②参数含义
(?G):表示内存大小
1024:表示内存容量标准进制
system:表示系统和服务占用的额外内存和需要预留的内存
请求大小:表示静态(一般为KB)或动态(一般为MB)的请求大小

③压测经验
16核32G服务器,可以抗住4万多用于负载均衡的并发,最多可以抗住5-6万,跑满文件描述符。
#注意:以上并发预估地方仅做参考,真实情况,Nginx压测到达瓶颈2700并发时,CPU已经耗尽,内存却还有很多剩余。

④其他查看并发的方法
[root@kuang ~]# netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
img
#注意:以上方法是查看每种连接的总数,比如ESTABLISHED中包含了反向代理Nginx与前端和后端的连接总数

Nginx压力测试

①配置Nginx最大并发数
以下配置Nginx只有一个worker,且每个worker进程最大连接数100,作为反向代理的话则最大并发数为50
img

②使用ab长连接对Nginx进行压测
[root@kuang-77 ~]# ab -c 50 -t 60 -k http://192.168.7.5/index.html #并发50持续60秒
③查看Nginx并发数
[root@kuang-75 ~]# netstat -antup | grep 192.168.7.5:80 | grep ES | wc -l
img
[root@kuang-75 ~]# curl http://192.168.7.5/status #还可以通过Nginx状态模块查看
其中curl本身这一次连接也计算入Active connections中,所以这里为51
img
#注意:ab压测设置并发参数要是超过了50,则在压测一开始就会报错,被对方的apr_socket_recv接收数据的进程给中断了。说明数据堆积在apr_socket_recv没有被取走,导致溢出异常。有两种可能:①apr_socket_recv速度太慢,没有及时将数据传递;②下家接收数据速度太慢,导致堆积。

img

查找限制Nginx并发的问题

①查看Nginx的error日志
#发现报错全是一下日志,说worker_connections连接不够了

2019/08/18 19:18:39 [alert] 1433#0: *60191 100 worker_connections are not enough while connecting to upstream, client: 192.168.7.7, server: _, request: “GET / HTTP/1.1”, upstream: “http://192.168.7.6:80/”, host: “192.168.7.5”

使用wrk稳定的对Nginx进行压测

①配置Nginx最大并发数
以下配置,计算得出Nginx当前的最大并发数为150

img

②使用wrk对Nginx进行压力测试
[root@kuang-77 ~]# wrk -c300 -t4 -d100s http://192.168.7.5

img

#注意:以上压测数据比较准确,无论wrk的并发设置为多大,在Nginx这边看到的并发都稳定的保持在最大的并发150以下。

持续对Nginx提高压测力度

①配置Nginx最大并发数

如下图,计算Nginx最大并发数,理论上应该是4000,但是能否达到呢?

img

②使用wrk对Nginx进行1500并发的压测

[root@kuang-77 ~]# wrk -c1500 -t4 -d100s http://192.168.7.5

img

③查看Nginx实际并发数

如下图可知,并发数始终在1024之下,显而易见,是因为最大的文件数限制了连接数

img

④增加用户最大能打开的数量

[root@kuang-75 ~]# ulimit -n 65535 #Nginx反向代理服务器上需要增加
[root@kuang-77 ~]# ulimit -n 65535 #客户端也需要增加
[root@kuang-77 ~]# wrk -c1500 -t4 -d100s http://192.168.7.5
此时在对Nginx做压力测试,已经可以达到wrk设置的并发数了

img

⑤增加压测并发数为1万

[root@kuang-77 ~]# wrk -c10000 -t4 -d100s http://192.168.7.5
如下图可知,Nginx的实际并发数维持在2700左右,就连curl想要去访问连接数也很难建立起和服务器的连接,后续排查是哪里的瓶颈限制?

img

#注意:目前已发现当压测并发为1千和1万时本地主机的CPU使用率都是100%,只是1千的能够扛得起来,1万的却只能抗起2700左右的并发

#注意:以下使用ab对Nginx进行压测数据有误,所以以下Nginx压测结果不做参考,也许是ab运行的机制影响了Nginx压测数据,可能是因为ab写数据太勤奋了。

四万并发记录

img

优化Nginx-绑定内核

①配置Nginx双内核

img

②使用ab对双核Nginx进行压测

如图,Nginx为双核时,按理说应该能抗起100的并发,可是连90的并发都没有扛起来,最后70的并发才扛起来,后面再用80的并发就抗不起来了。说明Nginx还有很大的优化空间。并且后面会发现内核越多反而性能并没有按趋势上升,而是亏了很多性能。

img

③将Nginx进程绑定内核

img

Nginx进程绑定内核好了一点,80的并发能够扛得起来,但是90的并发还是抗不起来。

img

优化Nginx-关闭日志

①将Nginx的access日志关闭

因为压测时会记录大量的日志,所以将日志记录关闭能提高部分性能,但是生产环境不能关闭日志

img

②再次对Nginx进行压测

此时Nginx性能又提高了一部分

img

进程控制

Nginx进程控制

Nginx重新加载配置文件

#要让 nginx 重新加载配置文件,需要给 nginx 主进程发送一个 HUP 信号。nginx 主进程收到信号之后,首先检查配置的语法,然后尝试应用新配置,比如:打开日志文件,或者建立新的监听套接字。如果应用新配置失败,nginx 对改动进行回滚,并继续以旧的配置进行工作。如果成功,nginx 启动新的 worker 进程,并发送关闭消息给旧的 worker 进程要求它们优雅地关闭。旧的 worker 进程关闭监听套接字,并继续完成当前的用户请求,等用户的请求完成后,就会关闭。
[root@localhost ~]# kill -s HUP 22650 #向nginx主进程发送一个HUP信号
[root@localhost ~]# ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep ‘(nginx|PID)’
PID PPID USER %CPU VSZ WCHAN COMMAND
22650 1 root 0.0 125632 sigsus nginx: master process /usr/sbin/nginx
22981 22650 nginx 0.0 138264 ep_pol nginx: worker process
22982 22650 nginx 0.0 138264 ep_pol nginx: worker process
22983 22650 nginx 0.0 138264 ep_pol nginx: worker process
22985 22650 nginx 0.0 138264 ep_pol nginx: worker process
#除了主进程外,其所有4个工作进程的PID全变了

手动切割日志

①日志需要被重命名
[root@localhost nginx]# mv access.log access.log.back #重命名后不影响Nginx正常运行,并且新的访问日志会继续往back文件中写(通过tailf验证),因为内核是根据文件描述符来找文件的-已测

②发送USR1 信号给Nginx 主进程
方法1、[root@localhost nginx]# nginx -s reopen #主进程会让所有工作进程关闭old文件,打开new文件-已测,成功
方法2、[root@localhost nginx]# kill -s USR1 4108 #与nginx -s reopen 效果相同-已测
③此时已经生成了新的access.log日志文件
[root@kuang-73 logs]# ll
-rw-r–r-- 1 nginx root 0 4月 2 22:01 access.log #用户为nginx,于是工作进程可写
-rw-r–r-- 1 root root 1064 4月 2 22:00 access.log.back

动态升级可执行文件

①用新版本Nginx可执行程序覆盖旧版本可执行程序
[root@kuang-76 ~]# cp objs/nginx /usr/local/nginx/sbin/nginx #执行这条命令,可能会报以下异常,提示文件被占用
xueliang@dev:~/download/nginx-1.11.1$ sudo cp objs/nginx /usr/local/nginx/sbin/nginx
cp: cannot create regular file ‘/usr/local/nginx/sbin/nginx’: Text file busy
xueliang@dev:~/download/nginx-1.11.1$
①可以使用以下命令进行强制覆盖
[root@kuang-76 ~]# cp -rfp objs/nginx /usr/local/nginx/sbin/nginx
②动态升级可执行文件
[root@kuang-76 ~]# kill -s USR2 4108 #对nginx主进程发送信号
③所有新老主进程和worker进程共存
[root@kuang-76 ~]# ps -ef | grep nginx
root 4108 1 0 2月26 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 86100 4108 0 17:33 ? 00:00:00 nginx: worker process
nginx 86101 4108 0 17:33 ? 00:00:00 nginx: worker process
nginx 86102 4108 0 17:33 ? 00:00:00 nginx: worker process
nginx 86103 4108 0 17:33 ? 00:00:00 nginx: worker process
root 87120 4108 1 17:47 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 87126 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87127 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87128 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87129 87120 0 17:47 ? 00:00:00 nginx: worker process
④将old master下的worker 进程优雅关闭
[root@kuang-76 ~]# kill -s WINCH 4108
⑤old master只有自己,而没有工作进程了
[root@kuang-76 ~]# ps -ef | grep nginx
root 4108 1 0 2月26 ? 00:00:00 nginx: master process /usr/sbin/nginx
root 87120 4108 0 17:47 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 87126 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87127 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87128 87120 0 17:47 ? 00:00:00 nginx: worker process
nginx 87129 87120 0 17:47 ? 00:00:00 nginx: worker process
⑥升级后的进程运行有问题
[root@kuang-73 ~]# kill -s HUP 76152 #将old master开启它的所有worker进程,并开始工作
[root@kuang-73 ~]# kill -s QUIT 77434 #将有问题的new master从容关闭
⑦升级后的进程运行稳定
[root@kuang-73 ~]# kill -s QUIT 76152 #完成工作交接就可以将old master关闭
⑧平滑升级使用说明
可以在升级前将配置提前修改好,平滑升级后会使用新的配置文件,因为平滑升级后原来的主进程也是会重新替换的

查看Nginx所有进程的优雅格式

[root@localhost ~]# ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep ‘(nginx|PID)’
PID PPID USER %CPU VSZ WCHAN COMMAND
22650 1 root 0.0 124964 sigsus nginx: master process /usr/sbin/nginx
22651 22650 nginx 0.0 137668 ep_pol nginx: worker process
22652 22650 nginx 0.0 137668 ep_pol nginx: worker process
22653 22650 nginx 0.0 137668 ep_pol nginx: worker process
22654 22650 nginx 0.0 137668 ep_pol nginx: worker process

Nginx支持的六类信号

①主进程可以处理以下的信号
TERM,INI 快速关闭
QUIT 从容关闭
HUP 重载配置,用新的配置开始新的工作进程,从容关闭旧的工作进程
USR1 重新打开日志文件
USR2 平滑升级可执行程序
WINCH 从容关闭工作进程
②工作进程也支持一些信号,尽管你不必自己操作
TERM,INI 快速关闭
QUIT 从容关闭
USR1 重新打开日志文件
③查看所有信号
[root@kuang-73 ~]# kill -l #Nginx只是支持其中的几个信号

杀死Nginx进程的方法

方法1、[root@kuang-73 ~]# pkill -9 nginx #强制关闭Nginx所有进程
方法2、[root@kuang-73 ~]# kill -TERM 75562 #强制关闭Nginx所有进程
方法3、[root@kuang-73 ~]# kill -15 75562 #和上条命令是同一条
方法4、[root@kuang-73 ~]# kill -QUIT 75562 #从容关闭Nginx主进程和子进程
方法5、[root@kuang-73 ~]# kill -QUIT cat /usr/local/nginx/nginx.pid

Nginx监控

监控Nginx后端服务器

重新编译安装Nginx

①下载nginx-module-vts模块

[root@kuang-76 ~]# cd /usr/local/src/
[root@kuang-76 src]# git clone git://github.com/vozlt/nginx-module-vts.git

②构建Nginx

[root@kuang-76 src]# cd nginx-1.14.2/
[root@kuang-76 nginx-1.14.2]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --without-http_uwsgi_module --without-http_scgi_module --without-http_browser_module --with-pcre --with-http_addition_module --with-http_ssl_module --with-http_realip_module --with-http_sub_module --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_gunzip_module --with-http_secure_link_module --with-http_stub_status_module --add-module=/usr/local/src/nginx-module-vts

③编译Nginx
[root@kuang-76 nginx-1.14.2]# make -j 4
④替换Nginx二进制执行程序
[root@kuang-76 nginx-1.14.2]# cp objs/nginx /usr/local/nginx/sbin/nginx

配置Nginx主配置

[root@kuang-76 ~]# vim /usr/local/nginx/conf/nginx.conf

http {
    vhost_traffic_status_zone;
    vhost_traffic_status_filter_by_host on;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        location /status {
            vhost_traffic_status_display;
            vhost_traffic_status_display_format html;
        }
include vhost/*.conf;
}
配置两个Vhost虚拟机配置

[root@kuang-76 ~]# vim /usr/local/nginx/conf/vhost/kuang.edsmall.com.conf

upstream proxy {
       server 192.168.7.7:80 max_fails=10 fail_timeout=5;
}
server {
        listen       80;
        server_name  kuang.edsmall.com;
        location / {
            proxy_pass http://proxy;
            proxy_set_header Host $host;
            proxy_set_header x-forwarded-for $remote_addr;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
            proxy_intercept_errors on;
        }
#        location /status {
#            vhost_traffic_status_display;
#            vhost_traffic_status_display_format html;
#        }
}

[root@kuang-76 ~]# vim /usr/local/nginx/conf/vhost/zhilu.edsmall.com.conf

upstream zhilu {
       server 192.168.7.7:81 max_fails=10 fail_timeout=5;
}
server {
        listen       80;
        server_name  zhilu.edsmall.com;
        location / {
            proxy_pass http://zhilu;
            proxy_set_header Host $host;
            proxy_set_header x-forwarded-for $remote_addr;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
            proxy_intercept_errors on;
        }
#        location /status {
#            vhost_traffic_status_display;
#            vhost_traffic_status_display_format html;
#        }
}
建议配置

①打开vhost过滤

vhost_traffic_status_filter_by_host on;
#开启此功能,在Nginx配置有多个server_name的情况下,会根据不同的server_name进行流量的统计,否则默认会把流量全部计算到第一个server_name上。

②在不想统计流量的server区域禁用vhost_traffic_status

server {
...
vhost_traffic_status off;
...
}
访问Nginx后端服务器监控页面

①通过Web访问Nginx后端服务器监控

img

②验证Nginx的json格式
[root@kuang-76 ~]# curl http://localhost/status/format/json

安装nginx-vts-exporter
#下载最新版nginx-vts-exporter
[root@kuang-76 ~]# cd /usr/local/src/
[root@kuang-76 src]# wget -c https://github.com/hnlq715/nginx-vts-exporter/releases/download/v0.10.3/nginx-vts-exporter-0.10.3.linux-amd64.tar.gz
[root@kuang-76 src]# tar xf nginx-vts-exporter-0.10.3.linux-amd64.tar.gz 

#启动nginx-vts-exporter
[root@kuang-76 src]# cd nginx-vts-exporter-0.10.3.linux-amd64
[root@kuang-76 nginx-vts-exporter-0.10.3.linux-amd64]# nohup ./nginx-vts-exporter -nginx.scrape_timeout 10 -nginx.scrape_uri http://127.0.0.1/status/format/json &

#配置nginx-vts-exporter服务
[Unit]
Description=nginx-vts-exporter
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/nginx-vts-exporter/nginx-vts-exporter -nginx.scrape_uri=http://127.0.0.1/vts_status/format/json
Restart=on-failure
[Install]
WantedBy=multi-user.target

#启动服务,nginx-vts-exporter 默认监听端口为 9913
[root@kuang-76 ~]# systemctl enable nginx-vts-exporter
[root@kuang-76 ~]# systemctl start nginx-vts-exporter
[root@kuang-76 ~]# systemctl status nginx-vts-exporter
Prometheus中添加Nginx监控

①添加Nginx监控配置文件
[root@kuang-75 ~]# vim /usr/local/Prometheus/prometheus.yml

  - job_name: 'Yunwei-Environment'
    static_configs:
    - targets: ['192.168.7.6:9913']
      labels:
        instance: 192.168.7.6

②热重启Prometheus
[root@kuang-77 ~]# curl -XPOST http://localhost:9090/-/reload

配置Nginx监控Dashboard

①下载Nginx监控Dashboard
https://grafana.com/dashboards/2949

②导入Nginx监控的Json文件

img

③查看Nginx监控信息

img

使用nginx-vts监控后端upstream

①查看nginx-module-vts模块获取的信息
http://172.16.68.172:8888/status
②查看nginx-vts-exporter获取的信息
http://172.16.68.172:9913/metrics
nginx_upstream_responseMsec

img

在Prometheus上监控此upstream后端响应时间,应该可以做后端端口检测

监控Nginx状态和连接

查看Nginx状态

①Nginx状态查看模块
http_stub_status_module
②Nginx使用状态模块

        location = /status {
            stub_status;
        }

③测试模块运行
http://192.168.7.3/status

④查看结果说明
Active connections #当前Nginx正处理的活动连接数(已有的连接)
server accepts handled requests #总共处理了5452191个连接,成功创建5452191次握手,总共处理了10970248个请求
Reading #Nginx正在读取客户端的Header信息数
Writing #Nginx正在返回客户端的Header信息数
Waiting #开启keep-alive的情况下,这个值等于active-(reading+writing),意思就是Nginx已经处理完正在等候下一次请求指令的驻留连接
⑤AWKD现网配置

        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            allow 172.16.239.242;    #只允许Zabbix服务器访问
            deny all;
        }
监控Nginx运行状态

①查看Nginx运行状态命令(包含状态码)
[root@kuang-73 ~]# curl -m 5 -s -w %{http_code} http://192.168.7.6/status

img

②参数说明
选项m最大传输时间,选项s静默执行没有杂七杂八输出,选项w命令完成后再输出什么参数
③优化输出(只输出状态码)
[root@kuang-73 ~]# curl -m 5 -s -w %{http_code} http://192.168.7.3/status -o /dev/null

④查看线上Nginx的并发连接
由于Nginx上都做有80强制跳转443,所以查看443的连接最直接最准确。
#注意:其中TIME_WAIT、FIN_WAIT1和FIN_WAIT2在Nginx中不记作连接数内,看,这类连接的后面都没有对应的nginx:worker在维护这个连接

img

img

⑤查看Nginx与后端建立的连接
#注意:由于Nginx并不是和后端统一的建立80端口的连接,所以这里只能针对每一个vhost来查找其连接情况

img

Nginx的并发连接数不包括和后端服务器的连接

①查看Nginx的Established状态的并发连接数(为3)

img

②查看所有和Nginx前后端有关的连接
由下图可知,Nginx的Established并发连接数只是统计了有多少个客户端,而没有将和后端服务器建立的连接一起统计。

img

监控Nginx状态脚本

①监控Nginx状态脚本
[root@kuang-73 ~]# vim chk_nginx.sh

#!/bin/bash
Resettem=$(tput sgr0)
Nginxserver='http://192.168.7.3/status'
Check_Nginx_Server()
{
    Status_code=$(curl -m 5 -s -w %{http_code} ${Nginxserver} -o /dev/null)
    if [ $Status_code -eq 000 -o $Status_code -ge 500 ] ; then
        echo -e '\E[31m' "check http server error! Response status code is" $Resettem $Status_code
    else
        Http_content=$(curl -s ${Nginxserver})
        echo -e '\E[32m' "check http server ok! \n" $Resettem $Http_content
    fi
}
Check_Nginx_Server

②脚本说明
说明1、‘\E[31m’和’\E[32m’分别代表红色和绿色;
说明2、tput sgr0指令表示恢复到正常屏幕(取消之前修改的颜色);
说明3、一般来说状态码为000或者大于等于500为Web服务器运行不正常。

监控Nginx后端健康状况

​ 严格来说,Nginx是没有针对负载均衡后端节点的健康检查的,但是可以通过proxy_next_upstream来间接实现,但这个还是会把请求转发给故障服务器的,然后再转发给别的服务器,这样就浪费了一次转发。(默认该功能是运行的,实验中出现后端服务器down了,看起来Nginx没有再发送请求到该后端服务器,就是该功能实现的效果,其实是转发了给down的那台后端服务器,请求不成功再转发给别的后端服务器的)

Nginx后端健康监测

①下载Nginx健康监测模块
https://github.com/yaoweibin/nginx_upstream_check_module
②上传并将该模块打入Nginx补丁

[root@kuang-77 ~]# unzip nginx_upstream_check_module-master.zip
[root@kuang-77 ~]# cd /usr/local/nginx-1.14.2/
[root@kuang-77 nginx-1.14.2]#patch -p1 </root/nginx_upstream_check_module-master/check_1.14.0+.patch

推荐下一款模块,因为不需要打补丁这一步骤,只需指向该目录即可

②其他nginx_upstream_check_module模块
[root@kuang-77 ~]# git clone https://github.com/xiaokai-wang/nginx_upstream_check_module.git

③获取原Nginx编译参数

[root@kuang-77 nginx-1.14.2]# /usr/local/nginx/sbin/nginx -V
--prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_ssl_module --with-http_mp4_module --with-pcre --with-http_v2_module --with-stream

④重新构建Nginx并带上补丁模块

[root@kuang-77 nginx-1.14.2]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_ssl_module --with-http_mp4_module --with-pcre --with-http_v2_module --with-stream --add-module=/root/nginx_upstream_check_module-master/

⑤重新编译Nginx
[root@kuang-77 nginx-1.14.2]# make -j 4
⑥替换原Nginx二进制执行程序

[root@kuang-77 ~]# pgrep nginx
[root@kuang-77 ~]# pkill nginx
[root@kuang-77 ~]# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
[root@kuang-77 ~]# cp /usr/local/nginx-1.14.2/objs/nginx /usr/local/nginx/sbin/nginx

⑦修改Nginx配置支持后端健康监测
[root@kuang-77 ~]# vim /usr/local/nginx/conf/nginx.conf #在http代码块中添加如下配置

    upstream backend {
        server 192.168.7.5;
        server 192.168.7.6;
        check interval=3000 rise=2 fall=3 timeout=3000 type=http;
    }
    server {
        location /nginx_status {
            stub_status on;
            access_log off;
        }
        location /nstatus {
            check_status;
            access_log off;
        }
    }

⑧查看后端健康监测效果

server number是后端服务器的数量,generation是Nginx reload的次数

img

⑨测试后端健康监测
[root@kuang-75 ~]# systemctl stop httpd #关闭其中一个后端服务

img

日志管理

自定义日志格式

[root@kuang-73 ~]# vim /usr/local/nginx/conf/nginx.conf #上下文为http

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  logs/access.log  main;
	#以上的main为格式名称可以修改,然后在引用处也要修改,并且没有冒号说明是一行的内容

脚本切割Nginx日志

①日志分割脚本
[root@AWKD-NGINX-172 ~]# vim /usr/local/nginx/sbin/nginx_log_cut.sh

#!/bin/sh
logs_path=/usr/local/nginx/logs
yesterday=`date -d 'yesterday' +%F`
olddate=`date -d '30 days ago' +%F`
mkdir -p $logs_path/log-bak/$yesterday
cd $logs_path

for nginx_logs in `ls *.log`;do
        mv $nginx_logs log-bak/${yesterday}/${yesterday}-${nginx_logs}
        gzip log-bak/${yesterday}/${yesterday}-${nginx_logs}
        kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
done

②日志分割定时任务

[root@AWKD-NGINX-172 ~]# crontab -l
10 0 * * * sh /usr/local/nginx/sbin/nginx_log_cut.sh

系统logrotate切割日志

①为什么要分割日志文件
针对于源码编译安装的Nginx需要配置日志切割,yum安装的会自动产生Nginx切割配置文件;
②编写Nginx日志切割配置文件

  1. 第一种配置文件写法
    [root@kuang-73 ~]# vim /etc/logrotate.d/nginx
/var/log/nginx/*log {    #指定正确的日志位置
create 0644 nginx nginx
daily
rotate 10
missingok
notifempty
compress
sharedscripts
postrotate
   /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
endscript
}
  1. 第二种配置文件写法
    [root@kuang-73 ~]# vim /etc/logrotate.d/nginx
/var/log/nginx/*log {    #指定正确的日志位置
daily
rotate 30
missingok
dateext
compress
delaycompress
notifempty
sharedscripts
postrotate
   if [ -f /usr/local/nginx/nginx.pid ]; then    #编译安装的文件位置
       kill -USR1 `cat /usr/local/nginx/nginx.pid`    #重新加载配置文件
   fi
endscript
}

优化日志

①如果一个网页有100个图片,那么就会产生100条日志,这样非常不可取

        location ~* .*\.(gif|png|jpg|jpeg|bmp|zip|swf)$ {
            expires 10d;
            access_log off;
        }
        location ~* .*\.(js|css)$ {
            expires 5d;
            access_log off;
        }

②但是好像并没有关闭访问http://192.168.7.3/index.php时产生的访问图片的日志
[root@kuang-73 ~]# tailf /usr/local/nginx/logs/access.log #动态查看

性能调优

设置CPU亲和力

1、进程数设置
一般我们设置CPU的核心或者两倍核心数

2、设置进程亲和力

[root@kuang-73 ~]# vim /usr/local/nginx/conf/nginx.conf

worker_cpu_affinity 0001 0010 0100 1000;
#其他配置方式
worker_processes  4;
worker_cpu_affinity auto;
#[root@kuang-73 ~]# /usr/local/nginx/sbin/nginx -s reload

2、查看亲和力
①一个进程绑定一个CPU核心

[root@kuang-73 ~]# ps -aux | grep nginx
root      60761  0.0  0.1  46024  1980 ?        Ss   20:26   0:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody    61666  0.0  0.1  48536  2020 ?        S    20:51   0:00 nginx: worker process
nobody    61667  0.0  0.1  48536  2020 ?        S    20:51   0:00 nginx: worker process
nobody    61668  0.0  0.1  48536  2020 ?        S    20:51   0:00 nginx: worker process
nobody    61669  0.0  0.1  48536  2020 ?        S    20:51   0:00 nginx: worker process
[root@kuang-73 ~]# taskset -cp 60761
pid 60761's current affinity list: 0-3
[root@kuang-73 ~]# taskset -cp 61666
pid 61666's current affinity list: 0
[root@kuang-73 ~]# taskset -cp 61667
pid 61667's current affinity list: 1
[root@kuang-73 ~]# taskset -cp 61668
pid 61668's current affinity list: 2

资源使用设置

#所有进程最多能打开的文件总数
worker_rlimit_nofile 65535;    
#文件数资源限制,所有Nginx进程能打开的文件总数,不管你谁打开的更多或更少,还可以设置更大(理论上一个Nginx工作进程能打开的为该总数除以进程数,但是Nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致)

#单个进程最大连接数
events {
    worker_connections  65535;    #单个进程最大连接数(实际运行时该值*进程数不能大于文件总数限制)
}
#[root@kuang-73 ~]# ulimit -n    #系统限制一个进程最多能打开的文件总数
#1024    #系统默认限制为1024
#[root@kuang-73 ~]# ulimit -SHn 65535    #设置为和Nginx设置同步

#Nginx事件处理模型
events { 
use epoll; 
worker_connections 65535; 
multi_accept on; 
}

#一次性可以接受多个新连接
multi_accept默认关闭为off
#情况1、如果关闭,一个进程一次只能接受一个新连接
#情况2、如果开启,一个进程一次接受所有新连接(epoll可以,kqueue不行)

连接超时时间

1、设置连接超时作用
主要目的是保护服务器资源,CPU,内存,控制连接数,因为建立连接也是需要消耗资源的

2、各类连接超时相关参数

keepalive_timeout 60; 
tcp_nodelay on; 
client_header_buffer_size 4k; 
open_file_cache max=102400 inactive=20s; 
open_file_cache_valid 30s; 
open_file_cache_min_uses 1; 
client_header_timeout 15;    #TCP连接后客户端发送header的超时时间,超时返回408
client_body_timeout 15;    #发送header后,等待body的超时时间,超时返回408
reset_timedout_connection on; 
send_timeout 15;    #两次请求间隔
server_tokens off; 
client_max_body_size 10m;

3、制造408错误码
①设置服务器超时
client_header_timeout 15; #TCP连接后客户端发送header的超时时间,超时返回408
client_body_timeout 15; #发送header后,等待body的超时时间,超时返回408
②客户端去连接服务器确不发送请求
[root@tke-node2 ~]# nc 193.112.180.221 80

4、keepalive_timeout

语法 keepalive_timeout timeout [ header_timeout ]
默认值 75s
上下文 http server location

​ 说明:第一个参数指定了与client的keep-alive连接超时时间。服务器将会在这个时间后关闭连接。可选的第二个参数指定了在响应头Keep-Alive: timeout=time中的time值。这个头能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。没有这个参数,nginx不会发送Keep-Alive响应头(尽管并不是由这个头来决定连接是否“keep-alive”)
​ 两个参数的值可并不相同,不同浏览器怎么处理“keep-alive”头:

  • MSIE和Opera忽略掉"Keep-Alive: timeout=" header

  • MSIE保持连接大约60-65秒,然后发送TCP RST

  • Opera永久保持长连接

  • Mozilla keeps the connection alive for N plus about 1-10 seconds

  • Konqueror保持长连接N秒

5、proxy_upstream_fail_timeout(fail_timeout)

语法 server address [fail_timeout=30s]
默认值 10s
上下文 upstream

​ 说明:Upstream模块下 server指令的参数,设置了某一个upstream后端失败了指定次数(max_fails)后,该后端不可操作的时间,默认为10秒。

后端服务器长连接

1、配置长连接目的
Nginx在反向代理HTTP协议的时候,默认使用的是HTTP1.0去向后端服务器获取响应的内容后在返回给客户端。(注意:生产中并没有开启,是不是为了减少Nginx的连接数量)
2、Nginx和后端服务器长连接配置

http{
    upstream www{
        server 192.168.7.7:8080;
        keepalive 50;    #必须配置,建议50-100之间
    }
    server {
        location / {
            proxy_pass http://www;
            proxy_http_version 1.1;    #后端配置支持HTTP1.1必须配置
            proxy_set_header Connection "";    #后端配置支持HTTP1.1必须配置-已测试
            proxy_set_heaser Host www.baidu.com;    #需要将Host设置成为后端可以接收的host,否则这里会变成www,导致访问异常
    }
}

3、长连接效果
当在客户端浏览器访问192.168.7.7的时候,在Nginx代理服务器上保持了两个长连接

img

在后端服务器保持了一个与Nginx代理服务器的连接

img

gzip压缩

1、gzip压缩(上下文为http)

gzip  on;    #开启gzip压缩输出
gzip_min_length 1k;    #从1k起开始压缩,不然有些小于1k的压缩后还比1k大
gzip_buffers 4 32k;    #表示申请4个单位为16k的内存作为压缩结果流缓存,默认值是申请与原始数据大小相同的内存空间来储存gzip压缩结果
gzip_http_version 1.1;    #设置识别http协议版本,默认就是1.1版本
gzip_comp_level 6;    #压缩等级5和6就行了,不要压到9
gzip_types text/css /text/xml appalication/javascript;    #压缩哪些媒体,text/html默认就压缩
gzip_vary on;    #宣告我压缩了

2、线上配置

gzip on;
gzip_min_length  1k;
gzip_buffers     4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types       text/plain application/x-javascript text/css application/xml;
gzip_vary on;

防盗链

1、防盗链
①在客户端添加域名解析
192.168.7.3 www.linuxfan.cn
192.168.7.4 www.linuxren.cn
②在防盗链机器上添加测试图片
[root@kuang-73 ~]# cp -rp //usr/share/backgrounds/night.jpg /usr/share/nginx/html/
[root@kuang-73 ~]# systemctl restart nginx
③在盗链机器上链接防盗链图片
[root@kuang-74 ~]# vim /usr/share/nginx/html/index.html

<html>
<head>
<title>hello world</title>
</head>
<body>
www.linuxren.cn
<img src="http://www.linuxfan.cn/night.jpg"/>
</body>
</html>

④客户端测试正常访问
可通过访问http://www.linuxren.cn/查看到未处理的防盗链图片
⑤防盗链机器上配置防盗链策略
[root@kuang-73 ~]# vim /etc/nginx/nginx.conf

location ~* .*\.(wma|wmv|asf|mp3|mmf|zip|rar|gif|jpg|jpeg|png|bmp|swf)$ {
    valid_referers none blocked *.linuxfan.cn linuxfan.cn;
    if ($invalid_referer) {
        rewrite ^/ http://www.otherdomin.com/404.jpg;
        #return 404;
    }
}

[root@kuang-73 ~]# systemctl restart nginx
⑥客户端测试防盗链访问
通过访问http://www.linuxren.cn/已经看不到已处理的防盗链图片
#注配置防盗链不能和expire缓存同存

sendfile的优化

​ Nginx做静态服务器的优化。
​ sendfile是个比 read 和 write 更高性能的系统接口, 不过需要注意的是,sendfile 是将 in_fd 的内容发送到 out_fd 。而 in_fd 不能是 socket , 也就是只能文件句柄。 所以当 Nginx 是一个静态文件服务器的时候,开启 SENDFILE 配置项能大大提高 Nginx 的性能。 但是当 Nginx 是作为一个反向代理来使用的时候,SENDFILE 则没什么用了,因为 Nginx 是反向代理的时候。 in_fd 就不是文件句柄而是 socket,此时就不符合 sendfile 函数的参数要求了。

访问控制

目录访问控制

①配置访问控制:要放在匹配php的前面
location ~* ^/images/.*.(php|php5|sh|py|pl)$ {
deny all;
}
②创建控制目录下的PHP文件
[root@kuang-73 ~]# cd /usr/local/nginx/html/
[root@kuang-73 html]# mkdir images
[root@kuang-73 html]# cp index.php images/
③测试访问
http://192.168.7.3/images/index.php不能访问 #403 Forbidden禁止访问
④想增加可以访问的用户
在拒绝所有前添加allow 192.168.7.2;

设置密码访问目录

①下载密码生成工具
[root@kuang-76 ~]# yum -y install httpd-tools
②生成账号密码
[root@kuang-76 ~]# htpasswd -cb /usr/local/nginx/conf/user.conf zs 123 #创建zs用户,并添加密码
[root@kuang-76 ~]# vim /usr/local/nginx/conf/nginx.conf #添加以下内容

        location /status {    
            stub_status on;    #有的版本直接不需要on这个参数
            access_log off;
            auth_basic "Nginx Status";    #认证提示文字
            auth_basic_user_file /usr/local/nginx/conf/user.conf;   #认证用户文件,用htpasswd生成
            allow 192.168.7.2;
            deny 192.168.7.0/24;
        }

各云环境白名单控制

Tencent 加白

server {
        set $clientip $remote_addr;

        if ( $http_x_real_ip ){
        set $clientip $http_x_real_ip;
        }
        proxy_set_header remote-user-ip     $clientip;
  location ~ ^/v2/gdosapi {
                allow 101.32.128.230;
  }
}

Azure 加白

http {
   map $http_x_forwarded_for $access{
    default false;
    ~*101.32.128.230 true;
    ~*101.32.162.39 true;
    ~*20.94.227.90 true;
  }
    server {
        set $clientip $http_x_forwarded_for;
        proxy_set_header remote-user-ip     $clientip;
        set $x_forwarded_for_noport "1.1.1.1";
        if ( $http_x_forwarded_for ~ ^(.*):(.*)$ ) {
                set $x_forwarded_for_noport $1;
        }
        proxy_set_header X-Real-IP  $x_forwarded_for_noport;
  }
}

AWS 加白

http {
    proxy_set_header  remote-user-ip     $remote_addr;
    server {
        set $clientip $http_x_forwarded_for;
        proxy_set_header remote-user-ip     $clientip;
        proxy_set_header X-Real-IP $http_x_forwarded_for;
  }

传输加密

本地搭建CA并颁发证书

CA认证中心配置

1、配置CA认证机构
[root@kuang-73 ~]# vim /etc/pki/tls/openssl.cnf
basicConstraints=CA:TRUE
2、创建一个证书颁发机构
[root@kuang-73 ~]# /etc/pki/tls/misc/CA -newca #生成CA的公私钥,和配置其信息
①CA证书文件名:回车使用默认的名字
②给CA的私钥添加保护密码:123456
③国家:CN
④省份:GuangDong
⑤城市:ShenZhen
⑥组织名:XueGod
⑦组织单位名:IT
⑧服务器名:ca.xuegod.cn
⑨邮箱:1@xuegod.cn
⑩challenge password:拓展的属性,可能某些过程比如申请时用的到,不需要的话就回车
⑩公司名:回车
⑩输入CA私钥的保护密码:123456
3、查看CA生成的公私钥
①生成公私钥后的目录结构
[root@kuang-73 ~]# cd /etc/pki/CA/
[root@kuang-73 CA]# ll
-rw-r–r–. 1 root root 4422 1月 4 15:23 cacert.pem #主要包含了CA服务器信息和其根证书,最下面是CA的根证书,即CA的公钥,需要被用户信任和添加的证书
-rw-r–r–. 1 root root 1033 1月 4 15:23 careq.pem
drwxr-xr-x. 2 root root 6 4月 11 2018 certs
drwxr-xr-x. 2 root root 6 4月 11 2018 crl
-rw-r–r–. 1 root root 111 1月 4 15:23 index.txt
-rw-r–r–. 1 root root 21 1月 4 15:23 index.txt.attr
-rw-r–r–. 1 root root 0 1月 4 15:21 index.txt.old
drwxr-xr-x. 2 root root 34 1月 4 15:23 newcerts
drwx------. 2 root root 23 1月 4 15:21 private #存放CA的私钥
-rw-r–r–. 1 root root 17 1月 4 15:23 serial
②生成公私钥前的目录结构
[root@kuang-73 ~]# ls /etc/pki/CA/ #以下四个空目录
certs crl newcerts private

Web服务器端生成证书请求文件

1、安装nginx和支持ssl的模块
[root@kuang-74 ~]# yum install -y nginx mod_ssl.x86_64
2、生成web server的私钥
①方法1、生成加密的私钥
[root@kuang-74 ~]# openssl genrsa -des3 -out /etc/httpd/conf.d/server.key #使用des3加密
[root@kuang-74 ~]# cd /etc/httpd/conf.d/
[root@kuang-74 conf.d]# cp server.key server.key.org
[root@kuang-74 conf.d]# openssl rsa -in server.key.org -out server.key #去掉私钥的加密,因为私钥有加密后面不能使用systemctl启动Nginx,而可以使用Nginx启动程序启动,所以在加载SSL支持的Nginx并使用私钥时除去必须的口令
②方法2、生成不加密的私钥
[root@kuang-74 ~]# openssl genrsa -out /etc/httpd/conf.d/test.key #推荐使用该方法,没有问题
3、生成证书请求文件
[root@kuang-74 ~]# openssl req -new -key /etc/httpd/conf.d/server.key -out /root/server.csr
#-key指定私钥文件位置,-out导出位置,直接通过私钥去找公钥
#录入证书请求文件信息:如果要去CA请求数字签名,必须和CA录入信息保持一致,就算城市写错了也不行(CA服务器生成CA证书时会报错)
①输入CA私钥的保护密码:123456
②国家:CN
③省份:GuangDong
④城市:ShenZhen
⑤组织名:XueGod
⑥组织单位名:IT
⑦服务器名:www.xuegod.cn
⑧邮箱:2@xuegod.cn
⑨challenge password:拓展的属性,可能某些过程比如申请时用的到,不需要的话就回车
⑩公司名:回车
4、将证书请求文件上传给CA
[root@kuang-74 ~]# scp server.csr 192.168.7.3:/root #证书里包含了web服务器信息和公钥

CA认证中心生成证书

1、CA认证中心生成证书
[root@kuang-73 ~]# openssl ca -keyfile /etc/pki/CA/private/cakey.pem -cert /etc/pki/CA/cacert.pem -in /root/server.csr -out /root/server.crt #-keyfile指定私钥,-cert指定根证书即CA的公钥
2、将证书传给Web服务器
[root@kuang-73 ~]# scp server.crt 192.168.7.4:/root

Web服务器使用证书配置

1、证书文件存储位置
[root@kuang-74 ~]# mv server.crt /etc/httpd/conf.d/
2、配置Web服务器支持ssl
[root@kuang-74 ~]# vim /etc/nginx/conf.d/default.conf
server {
listen 443 ssl;
#server_name www.xuegod.cn; #注意这里没有指定主机名,后面也能成功
#ssl on; #注意这里没有开启ssl,后面也能成功,可能默认开启
keepalive_timeout 70;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_certificate /etc/httpd/conf.d/server.crt; #证书存储位置,阿里下载下来的后缀是.pem
ssl_certificate_key /etc/httpd/conf.d/server.key; #密钥存储位置
#注意:生产中一个域名如edsmall.com下的所有主机如sf.edsmall.com用的是同一个证书和密钥
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
3、重启Web服务器使用ssl
①测试Nginx配置语法
[root@kuang-74 ~]# nginx -t
②重启Nginx服务器
[root@kuang-74 ~]# systemctl restart nginx #如果这里起不来,可能的原因是Web的密钥有密码保护,按上面的方法去掉密码保护
[root@kuang-74 ~]# nginx #以上不能起来,直接使用Nginx的启动程序却能起来
③查看ssl是否启动
[root@kuang-74 ~]# ss -lntup | grep 443
4、确保模块被加载
①确保主配置文件导入了子配置文件
[root@kuang-74 ~]# vim /etc/nginx/nginx.conf #上下文为http
include /etc/nginx/conf.d/*.conf;
②确保存在ssl模块
[root@kuang-74 ~]# nginx -V

客户端浏览器配置使用根证书

1、修改windows客户端的hosts文件
C:\Windows\System32\drivers\etc
192.168.7.4 www.xuegod.cn
2、到CA认证中心下载根证书
[root@kuang-73 ~]# sz /etc/pki/CA/cacert.pem #保存到桌面
3、IE浏览器中添加根证书
①打开Internet选项,点击证书

img

②导入证书到“受信任的根证书”
4、测试使用根证书
①访问Web服务器
https://www.xuegod.cn

Nginx证书使用

尽量用相对路径,相对于root根目录,提升配置的可移植性。

upstream 71-pre {
       server 10.81.67.129:80 max_fails=10 fail_timeout=5;
    }

server {
        listen       80;
        server_name  71-pre.edsmall.com;
        return 301 https://$server_name$request_uri;    #return301高效一些
        #rewrite ^(.*)$  https://$host$1 permanent;
        access_log  logs/71-pre.edsmall.com.access.log  main;
        error_log    logs/71-pre.edsmall.com.error.log    error;
    }

server {
        listen       443;
        server_name  71-pre.edsmall.com;
        access_log  logs/https-71-pre.edsmall.com.access.log  main;
        error_log    logs/https-71-pre.edsmall.com.error.log    error;
        ssl on;
        ssl_certificate sslkey/edsmall.com.pem; #(证书公钥)
        ssl_certificate_key sslkey/edsmall.com.key; #(证书私钥)
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;#停掉SSLv3杜绝贵宾犬漏洞
        #ssl_ciphers ECDH:AESGCM:HIGH:!RC4:!DH:!MD5:!aNULL:!eNULL;
        ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
        ssl_prefer_server_ciphers on;
        root /data/nginx/html;


        location / {
            proxy_pass http://71-pre;
            proxy_set_header Host $host;
            #prwxy_set_header X-Real-IP $remote_addr;
            proxy_set_header x-forwarded-for $remote_addr;
            #proxy_set_header X-Forwarded-For $http_x_forwarded_for;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
            proxy_intercept_errors on;
            #error_page 403 404 /404.html;
        }
        location = /404.html {
            root /data/nginx/html;
        }

        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /opt;
        }
    }

排错

Nginx后端检查路径写错导致502问题

1、故障信息
https://xqlive.yetingfm.com/live-admin-v1报502错误
http://106.52.179.250:5001/live-admin-v1可以直接访问

2、故障定位
由于是502错误,但是后端服务可直接被调用访问,所以初步判断是Nginx配置问题

3、查看Nginx配置
发现chech_http_send后端检查的uri有问题,分析:是因为后端检查的uri写错,导致后端检测失败,Nginx认为无后端,于是向客户端相应502。

4、修改Nginx为正确配置
修改之后访问正常

img

Nginx环境下http和https(ssl)共存的方法

1、问题描述
给nginx配置SSL证书之后,https可以正常访问,http访问显示400错误,报错如下:

400 Bad Request
The plain HTTP requset was sent to HTTPS port. Sorry for the inconvenience.
Please report this message and include the following information to us.
Thank you very much!
#说明:http的请求被发送到https的端口上去了,所以才会出现这样的问题。

nginx的配置如下:

server {
            listen 80 default backlog=2048;
            listen 443;
            server_name domain.cn;
            root /var/www/html;
            ssl on;
            ssl_certificate /etc/nginx/ssl/domain.cn.crt;
            ssl_certificate_key /etc/nginx/ssl/domain.cn.key;
        }

2、解决方案
把**ssl on;**这行去掉,ssl写在443端口后面,这样http和https的链接都可以用。

server {
            listen 80 default backlog=2048;
            listen 443 ssl;
            server_name domain.cn;
            root /var/www/html;
            ssl_certificate /etc/nginx/ssl/domain.cn.crt;
            ssl_certificate_key /etc/nginx/ssl/domain.cn.key;
        }

截断问题

问题现象:返回数据很长的情况下,数据会被截断。

keepalive_timeout 0;
proxy_connect_timeout 1000;
proxy_send_timeout 1000;
proxy_read_timeout 1000;
send_timeout 1000;
proxy_http_version 1.1;
    proxy_buffering on;
    proxy_buffers 16 512k;
    proxy_buffer_size 512k;

    keepalive_timeout 0;
    proxy_connect_timeout 1000;
    proxy_send_timeout 1000;
    proxy_read_timeout 1000;
    send_timeout 1000;
    proxy_http_version 1.1;

Consul服务动态发现

下载Nginx模块

①下载nginx_upstream_check_module模块
[root@kuang-75 ~]# git clone https://github.com/xiaokai-wang/nginx_upstream_check_module.git
②下载nginx-upsync-module模块
[root@kuang-75 ~]# git clone https://github.com/weibocom/nginx-upsync-module.git

安装Consul

①下载并安装Consul
[root@kuang-75 ~]# wget https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip
[root@kuang-75 ~]# unzip consul_1.6.1_linux_amd64.zip
[root@kuang-75 ~]# mv consul /usr/local/
②启动Consul
[root@kuang-75 ~]# cd /usr/local/
[root@kuang-75 local]# nohup ./consul agent -server -bootstrap-expect 1 -data-dir=/data/consul -node=server1 -bind=192.168.7.5 -client=0.0.0.0 -ui >>consul.log 2>&1 &

安装Nginx

①安装Nginx依赖
[root@kuang-75 ~]# yum -y install zlib zlib-devel pcre pcre-devel openssl openssl-devel automake autoconf gcc gcc-c++
②构建Nginx
[root@kuang-75 nginx-1.14.2]# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_ssl_module --with-http_mp4_module --with-pcre --with-http_v2_module --with-stream --add-module=/root/nginx_upstream_check_module/ --add-module=/root/nginx-upsync-module
③安装Nginx
[root@kuang-75 nginx-1.14.2]# make -j 4 && make install

配置动态获取upstream信息

①server中配置

        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass http://pic_backend;
        }
        location /nstatus {
            check_status;
            access_log off;
        }
        location = /upstream_show {
            upstream_show;
        }
        location = /upstream_status {
            stub_status on;
            access_log off;
        }

②upstream配置

    upstream pic_backend {
        server 192.168.7.5:82;
        upsync 192.168.7.5:8500/v1/kv/upstreams/pic_backend upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
        upsync_dump_path /usr/local/nginx/conf/servers/servers_pic_backend.conf;
        check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
        check_http_send "HEAD / HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
    }
#注意:后三行需要添加,否则在upstream check status中看不到
[root@kuang-75 ~]# mkdir /usr/local/nginx/conf/servers/
[root@kuang-75 ~]# touch /usr/local/nginx/conf/servers/servers_pic_backend.conf
[root@kuang-75 ~]# chown -R nginx.nginx /usr/local/nginx/
#启动Nginx
[root@kuang-75 ~]# /usr/local/nginx/sbin/nginx -t
[root@kuang-75 ~]# /usr/local/nginx/sbin/nginx

实现服务动态发现

①Consul中添加upstream服务器
[root@kuang-75 ~]# curl -X PUT -d ‘{“weight”:10, “max_fails”:2, “fail_timeout”:10, “down”:0}’ http://192.168.7.5:8500/v1/kv/upstreams/pic_backend/192.168.7.6:80
[root@kuang-75 ~]# curl -X PUT -d ‘{“weight”:10, “max_fails”:2, “fail_timeout”:10, “down”:0}’ http://192.168.7.5:8500/v1/kv/upstreams/pic_backend/192.168.7.7:80
②查看Consul中upstream键值对信息

img

③查看Nginx中upstream信息
[root@kuang-75 ~]# curl http://192.168.7.5/upstream_show

img

④查看备份的upstream配置
[root@kuang-75 ~]# cat /usr/local/nginx/conf/servers/servers_pic_backend.conf

img

⑤访问Nginx服务测试负载均衡

img

拓展Consul其他命令

①修改upstream信息
#注意:应用场景,切断流量,然后在该后端服务器上进行上线发布
[root@kuang-75 ~]# curl -X PUT -d ‘{“weight”:10, “max_fails”:2, “fail_timeout”:10, “down”:1}’ http://192.168.7.5:8500/v1/kv/upstreams/pic_backend/192.168.7.7:80
②Consul中删除K/V信息
[root@kuang-75 ~]# curl -X DELETE http://192.168.7.5:8500/v1/kv/upstreams/pic_backend/192.168.7.6:80

反代和负载均衡

简单反向代理和负载均衡

简单反向代理

1、带有URI部分的proxy_pass指令将会发生替换
location /uri {
proxy_pass http://192.168.7.4/newuri;
}
①newuri将会替换掉整个URL中的uri
②访问http://192.168.7.3/uri/a.html将会代理到http://192.168.7.4/newuri/a.html
#两个特殊情况,不能发生替换
情况1、location定义了一个正则表达式,比如location ~ ^/uri { } 这种情况语法测试会报错:proxy_pass不能获取在location内由正则表达式给予的URI部分。
情况2、proxy_pass前有rewrite命令

简单负载均衡

1、最简单的负载均衡
①上下文为http
upstream backend {
server 192.168.7.4;
server 192.168.7.5;
}
server {
location / {
proxy_pass http://backend;
}
②测试RS的健康监测
将192.168.7.5的端口修改,或者服务关闭
Nginx将不再调度到192.168.7.5,而只会调度到192.168.7.4;

2、配置一台服务器作为backup
①配置backup服务器
upstream backend {
server 192.168.7.4;
server 192.168.7.5:8000 backup;
server 192.168.7.6;
}
②使用说明
Nginx只会调度到服务器1和3;
而且只有当1和3都挂掉(主服务器全部宕机),才会调度到backup服务器

标准反向代理配置

①反向代理

        location / {
            proxy_pass http://sxy;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header x-forwarded-for $remote_addr;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";    #
            proxy_http_version 1.1;
            proxy_connect_timeout 600;
            proxy_read_timeout 600;
            proxy_send_timeout 600;
        }

②长连接反向代理

http{
    upstream www{
        server 192.168.7.7:8080;
        keepalive 50;    #必须配置,建议50-100之间
    }
    server {
        location / {
            proxy_pass http://www;
            proxy_http_version 1.1;    #后端配置支持HTTP1.1必须配置
            proxy_set_header Connection "";    #后端配置支持HTTP1.1必须配置-已测试,上面增加upgrade也可以
    }
}

动静分离

网络TOP

Nginx:192.168.7.3
RS:192.168.7.4
RS:192.168.7.5
RS:192.168.7.6

1、Nginx负载均衡服务器配置
①配置反向代理部分
[root@kuang-73 nginx]# vim conf/nginx.conf

        location / {    #在其原有的localtion中添加或修改为如下
            root   html;
            index  index.html index.htm;
            if ($request_uri ~* \.php$){    #uri指的是除去域名的后面一段
                proxy_pass http://phpservers;
        }
            if ($request_uri ~* \.html$){
                proxy_pass http://htmlservers;
        }
                proxy_pass http://picservers;
        }

②配置负载均衡部分

upstream phpservers {
        server 192.168.7.4;
        server 192.168.7.5;
}
upstream htmlservers {
        server 192.168.7.5;
        server 192.168.7.6;
}
upstream picservers {
        server 192.168.7.4;
        server 192.168.7.6;
}

③重启Nginx服务器加载配置

[root@kuang-73 nginx]# ./sbin/nginx -t    #测试一下语法
[root@kuang-73 nginx]# ./sbin/nginx -s reload    #重新加载一下

2、三台RS配置

[root@kuang-74 ~]# yum -y install httpd php    #可以处理动态php页面
[root@kuang-74 ~]# echo "<?php phpinfo(); ?>" > /var/www/html/1.php
[root@kuang-74 ~]# cp /usr/share/backgrounds/night.jpg /var/www/html/1.jpg
[root@kuang-74 ~]# systemctl restart httpd

[root@kuang-75 ~]# yum -y install httpd php    #可以处理动态php页面
[root@kuang-75 ~]# echo "<?php phpinfo(); ?>" > /var/www/html/1.php
[root@kuang-75 ~]# echo "From 7.5 HTML" > /var/www/html/1.html
[root@kuang-75 ~]# systemctl restart httpd

[root@kuang-76 ~]# yum -y install httpd
[root@kuang-76 ~]# echo "From 7.6 HTML" > /var/www/html/1.html
[root@kuang-76 ~]# cp /usr/share/backgrounds/day.jpg /var/www/html/1.jpg
[root@kuang-76 ~]# systemctl restart httpd

3、测试负载均衡
①在CMD中使用curl测试静态页面的负载均衡调度
C:\Windows\system32>curl http://192.168.7.3/1.html
From 7.6 HTML
C:\Windows\system32>curl http://192.168.7.3/1.html
From 7.5 HTML
②在谷歌无痕浏览器中测试图片页面的负载均衡调度
http://192.168.7.3/1.jpg

4、测试反向代理——动静分离
#注意:动静分离主要用在同一个网站中拥有动态和静态的内容,需要交给不同服务器处理
①Nginx调度器上删除负载均衡条目
[root@kuang-73 ~]# vim /usr/local/nginx/conf/nginx.conf

upstream phpservers {
        server 192.168.7.4;
}
upstream htmlservers {
        server 192.168.7.5;
}
upstream picservers {
        server 192.168.7.6;
}

②重新加载配置
[root@kuang-73 nginx]# ./sbin/nginx -s reload

③RS上删除用于负载均衡的条目
[root@kuang-75 ~]# rm -rf /var/www/html/1.php
[root@kuang-76 ~]# rm -rf /var/www/html/1.html
[root@kuang-74 ~]# rm -rf /var/www/html/1.jpg
[root@kuang-74 ~]# vim /var/www/html/index.php #在RS1上额外增加动静分离页面

<iframe src="1.html"></iframe>
<img src="1.jpg">
<?php phpinfo(); ?>

④测试动静分离效果
访问192.168.8.3/index.php也可看见效果

获知后端服务IP地址

①获知后端IP地址目的
服务器上线时,测试可用性时,确认某一个访问跳转到后端哪台服务器上
②Nginx配置

location / {
    #root   html;
    #index  index.html index.htm;
    add_header backendIP $upstream_addr;
    add_header backendCode $upstream_status;
    proxy_pass http://192.168.7.6; 
}

③浏览器访问效果

img

反向代理设置头部字段

​ 如果启用缓存,来自之前请求的头字段“If-Modified-Since”, “If-Unmodified-Since”, “If-None-Match”, “If-Match”, “Range”, 和 “If-Range” 将不会被代理服务器传递。

日志字段说明
#默认日志格式包含字段
$remote_addr    #客户端地址
$remote_user    #客户端用户名称
$request    #请求的URI和HTTP协议
$http_referer    #url跳转来源
$status    #HTTP请求状态,日志中默认包含

#默认日志格式未包含字段
$upstream_status    #upstream状态,可在Nginx反向代理服务器上增加日志字段来查看
$http_host    #请求地址,即浏览器中你输入的地址(可以通过proxy_set_header Host $http_host;来设置HTTP的Host字段),日志中默认不包含
$host    #同上
$upstream_addr    #后台upstream的地址,即真正提供服务的主机地址

#Nginx其他相关变量
$proxy_host    #跟客户端请求的Host不同,这里是配置文件中upstream代理的Host名称
Nginx日志中显示客户端IP为真实IP地址

①默认情况下Nginx代理服务器会将客户端IP修改为自己的IP地址

img

②在Nginx代理服务器中添加X-Real-IP字段记录原remote_addr地址

prwxy_set_header X-Real-IP $remote_addr;

③在Nginx服务器的日志格式中添加显示X-Real-IP字段

$http_x_real_ip    #后端通过X-REAL-IP或者HTTP_X_REAL_IP变量获取

④验证Nginx服务器记录客户端信息(确实变为了客户端真实IP地址)

img

Nginx日志中显示Host为真实请求Host

①默认情况下Nginx代理服务器会将Host修改为代理配置中upstream的名称

img

②在Nginx代理服务器中修改代理Host字段为原Host
proxy_set_header Host $host;
③在Nginx服务器的日志格式中添加显示Host字段
KaTeX parse error: Expected 'EOF', got '#' at position 14: http_host #̲注意:host效果也是一样的
④验证Nginx服务器日志记录Host信息

img

Nginx日志中显示客户端请求经过的路径

#注意:想要实现本实验需要增加二级Nginx反向代理
①在一级Nginx代理服务器中向X-Forwarded-For增加客户端IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
②在二级Nginx代理服务器中向X-Forwarded-For增加一级Nginx代理服务器IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
③在Nginx服务器的日志格式中添加显示X-Forwarded-For字段
$http_x_forwarded_for #默认是已添加的
④验证验证Nginx服务器日志记录X-Forwarded-For信息

正向代理

正向代理概念

1、HTTP/HTTPS正向代理的分类
①按客户端有无感知的分类
普通代理:在客户端需要在浏览器中或者系统环境变量手动设置代理的地址和端口。如squid,在客户端指定squid服务器IP和端口3128。
透明代理:客户端不需要做任何代理设置,“代理”这个角色对于客户端是透明的。如企业网络链路中的Web Gateway设备。
②按代理是否解密HTTPS的分类
隧道代理 :也就是透传代理。代理服务器只是在TCP协议上透传HTTPS流量,对于其代理的流量的具体内容不解密不感知。客户端和其访问的目的服务器做直接TLS/SSL交互。本文中讨论的NGINX代理方式属于这种模式。
中间人(MITM, Man-in-the-Middle)代理:代理服务器解密HTTPS流量,对客户端利用自签名证书完成TLS/SSL握手,对目的服务器端完成正常TLS交互。在客户端-代理-服务器的链路中建立两段TLS/SSL会话。如Charles,简单原理描述可以参考文章。
#注意:这种情况客户端在TLS握手阶段实际上是拿到的代理服务器自己的自签名证书,证书链的验证默认不成功,需要在客户端信任代理自签证书的Root CA证书。所以过程中是客户端有感的。如果要做成无感的透明代理,需要向客户端推送自建的Root CA证书,在企业内部环境下是可实现的。

2、为什么正向代理处理HTTPS流量需要特殊处理
作为反向代理时,代理服务器通常终结 (terminate) HTTPS加密流量,再转发给后端实例。HTTPS流量的加解密和认证过程发生在客户端和反向代理服务器之间。
而作为正向代理在处理客户端发过来的流量时,HTTP加密封装在了TLS/SSL中,代理服务器无法看到客户端请求URL中想要访问的域名,如下图。所以代理HTTPS流量,相比于HTTP,需要做一些特殊处理。

img

3、NGINX的解决方案
根据前文中的分类方式,NGINX解决HTTPS代理的方式都属于透传(隧道)模式,即不解密不感知上层流量。具体的方式有如下7层和4层的两类解决方案。

Nginx七层正向代理

​ 默认Nginx只能实现http的正向代理,不能实现https的正向代理。本地可实现Nginx正向代理,可是在阿里云ECS上只能代理国内网站,不能代理国外网站。

1、历史背景
早在1998年,也就是TLS还没有正式诞生的SSL时代,主导SSL协议的Netscape公司就提出了关于利用web代理来tunneling SSL流量的INTERNET-DRAFT。其核心思想就是利用HTTP CONNECT请求在客户端和代理之间建立一个HTTP CONNECT Tunnel,在CONNECT请求中需要指定客户端需要访问的目的主机和端口。Draft中的原图如下:

img

2、Nginx添加ngx_http_proxy_connect_module模块
①NGINX ngx_http_proxy_connect_module模块说明
NGINX作为反向代理服务器,官方一直没有支持HTTP CONNECT方法。但是基于NGINX的模块化、可扩展性好的特性,阿里的@chobits提供了ngx_http_proxy_connect_module模块,来支持HTTP CONNECT方法,从而让NGINX可以扩展为正向代理。
②下载ngx_http_proxy_connect_module模块
[root@kuang ~]# git clone https://github.com/chobits/ngx_http_proxy_connect_module.git
③根据Nginx版本号选择补丁的版本
#注意:Github中有模块使用说明
[root@kuang nginx-1.14.2]# patch -p1 < /root/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1014.patch
④编译安装Nginx
[root@kuang nginx-1.14.2]# yum -y install zlib zlib-devel pcre pcre-devel openssl openssl-devel
[root@kuang nginx-1.14.2]# ./configure --add-module=/root/ngx_http_proxy_connect_module/
[root@kuang nginx-1.14.2]# make -j 4 && make install

3、Nginx正向代理配置
①添加Nginx支持正向代理配置
[root@kuang ~]# vim /usr/local/nginx/conf/nginx.conf

    server {
        listen       80;
        server_name  localhost;
        resolver 8.8.8.8;
        resolver_timeout 30s;

        set $proxy_remote_address "";
        set $proxy_local_address "";

        proxy_connect;
        proxy_connect_connect_timeout 10s;
        proxy_connect_read_timeout 150;
        proxy_connect_send_timeout 10s;
        proxy_connect_send_lowat 0;
        proxy_connect_address $proxy_remote_address;
        proxy_connect_bind $proxy_local_address;

        access_log logs/proxy.access.log;

        location / {
            proxy_pass http://$http_host;
            proxy_set_header Host $host;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

②正向代理配置说明
端口只需任意指定一个,通过这一个端口可以去代理80和443端口;
协议类型直接指定http,也不像网上教程说的那样要使用 s c h e m e ;(使用 scheme;(使用 scheme;(使用scheme只是说用户如果使用的http协议,那么正向代理去访问服务的时候也继承并使用http协议,如果是https那么就继承https,但是不影响正向代理帮你实现http到https的跳转)
③开启路由功能
[root@kuang ~]# echo 1 > /proc/sys/net/ipv4/ip_forward

4、Linux上使用七层正向代理
7层需要通过HTTP CONNECT来建立隧道,属于客户端有感知的普通代理方式,需要在客户端手动配置HTTP(S)代理服务器IP和端口(配置之后,客户端就知道使用connect方法跟Nginx正向代理服务器建立隧道连接)。在客户端用curl 加-x参数访问如下:

img

​ 从上面-v参数打印出的细节,可以看到客户端先往代理服务器39.105.196.164建立了HTTP CONNECT隧道,代理回复HTTP/1.1 200 Connection Established后就开始交互TLS/SSL握手和流量了。

5、Windows客户端配置正向代理
①指定Nginx正向代理服务器上监听的端口即可

img

②验证客户端使用正向代理
此时已经可以随意访问强制跳转HTTPS类的网站了

img

Nginx四层正向代理

1、Nginx四层正向代理的问题

​ 用NGINX stream在TCP层面上代理HTTPS流量肯定会遇到本文一开始提到的那个问题:代理服务器无法获取客户端想要访问的目的域名。因为在TCP的层面获取的信息仅限于IP和端口层面,没有任何机会拿到域名信息。要拿到目的域名,必须要有拆上层报文获取域名信息的能力,所以NGINX stream的方式不是完全严格意义上的4层代理,还是要略微借助些上层能力。那就要借助到ngx_stream_ssl_preread_module模块。

​ 要在不解密的情况下拿到HTTPS流量访问的域名,只有利用TLS/SSL握手的第一个Client Hello报文中的扩展地址SNI (Server Name Indication)来获取。NGINX官方从1.11.5版本开始支持利用ngx_stream_ssl_preread_module模块来获得这个能力,模块主要用于获取Client Hello报文中的SNI和ALPN信息。对于4层正向代理来说,从Client Hello报文中提取SNI的能力是至关重要的,否则NGINX stream的解决方案无法成立。同时这也带来了一个限制,要求所有客户端都需要在TLS/SSL握手中带上SNI字段,否则NGINX stream代理完全没办法知道客户端需要访问的目的域名。

2、Nginx四层代理环境搭建
①编译安装Nginx

[root@kuang-75 nginx-1.15.6]# ./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module

[root@kuang-75 nginx-1.15.6]# make -j 4 && make install
②配置Nginx支持四层正向代理
[root@kuang-75 ~]# vim /usr/local/nginx/conf/nginx.conf

stream {
    resolver 114.114.114.114;
    server {
        listen 443;
        ssl_preread on;
        proxy_connect_timeout 5s;
        proxy_pass $ssl_preread_server_name:$server_port;
    }
}
#注意:对于4层正向代理,NGINX对上层流量基本上是透传,也不需要HTTP CONNECT来建立隧道。适合于透明代理的模式,比如将访问的域名利用DNS解定向到代理服务器。我们可以通过在客户端绑定/etc/hosts来模拟。

3、使用四层正向代理服务器
①绑定正向代理服务器
#注意:需要访问的网站,都需要一个一个绑定,而不是直接配置正向代理服务器
[root@kuang-76 ~]# vim /etc/hosts
192.168.7.5 www.baidu.com
②通过正向代理访问百度
[root@kuang-76 ~]# curl https://www.baidu.com -svo /dev/null

img

4、常见问题
①客户端手动设置代理导致访问不成功
4层正向代理是透传上层HTTPS流量,不需要HTTP CONNECT来建立隧道,也就是说不需要客户端设置HTTP(S)代理。如果我们在客户端手动设置HTTP(s)代理是否能访问成功呢? 我们可以用curl -x来设置代理为这个正向服务器访问测试,看看结果:

img

​ 可以看到客户端试图于正向NGINX前建立HTTP CONNECT tunnel,但是由于NGINX是透传,所以把CONNECT请求直接转发给了目的服务器。目的服务器不接受CONNECT方法,所以最终出现"Proxy CONNECT aborted",导致访问不成功。

②客户端没有带SNI导致访问不成功
上文提到用NGINX stream做正向代理的关键因素之一是利用ngx_stream_ssl_preread_module提取出Client Hello中的SNI字段。如果客户端客户端不携带SNI字段,会造成代理服务器无法获知目的域名的情况,导致访问不成功。
在透明代理模式下(用手动绑定hosts的方式模拟),我们可以在客户端用openssl来模拟:
[root@kuang-76 ~]# openssl s_client -connect www.baidu.com:443 -msg

img

#注意:openssl s_client默认不带SNI,可以看到上面的请求在TLS/SSL握手阶段,发出Client Hello后就结束了。因为代理服务器不知道要把Client Hello往哪个目的域名转发。
如果用openssl带servername参数来指定SNI,则可以正常访问成功,命令如下:
[root@kuang-76 ~]# openssl s_client -connect www.baidu.com:443 -servername www.baidu.com

5、拓展在Windows上使用Nginx四层正向代理
①添加域名映射
C:\Windows\System32\drivers\etc\hosts
192.168.7.5 www.baidu.com
②Windows访问百度抓包情况如下
#注意:火狐浏览器发送Client Hello都会带上SNI字段,所以能成功访问
客户端把192.168.7.5当成百度进行访问

img

③拓展抓包查看SNI字段

img

接着正向代理服务器将这个包代理给了百度

img

Keepalived实现高可用

主备服务器安装Nginx和Keepalived

1、安装Nginx

#①安装所需的依赖软件
[root@kuang-76 ~]# yum -y install gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel

#②安装Nginx
[root@kuang-76 ~]# tar -zxvf nginx-1.14.0.tar.gz    #上传并解压Nginx安装包
[root@kuang-76 ~]# cd nginx-1.14.0
[root@kuang-76 ~]# ./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi
[root@kuang-76 ~]# mkdir -p /var/temp/nginx    #安装前创建所需目录
[root@kuang-76 ~]# make && make install    #编译安装Nginx

#③启动Nginx
[root@kuang-76 ~]# /usr/local/nginx/sbin/nginx

2、安装和配置Keepalived

#①安装Keepalived
[root@kuang76 ~]# tar -xf keepalived-2.0.12.tar.gz    #上传并安装Keepalived
[root@kuang76 ~]# cd keepalived-2.0.12/
[root@kuang76 keepalived-2.0.12]# ./configure --prefix=/usr/local/keepalived --sysconf=/etc
#然后系统出现以下警告
*** WARNING - this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.
[root@kuang76 keepalived-2.0.12]# yum -y install libnl libnl-devel
[root@kuang76 keepalived-2.0.12]# ./configure --prefix=/usr/local/keepalived --sysconf=/etc
[root@kuang76 ~]# make && make install

#②Keepalived初始化配置
[root@kuang76 ~]# ln -s /usr/local/keepalived/sbin/keepalived /sbin/    #创建命令快捷键
[root@kuang76 ~]# cp /root/keepalived-2.0.12/keepalived/etc/init.d/keepalived.rh.init
/etc/init.d/keepalived    #安装启动控制脚本
[root@kuang76 ~]# cp /root/keepalived-2.0.12/keepalived/etc/sysconfig/keepalived /etc/sysconfig/   #新版本Keepalived安装后自动会安装该配置文件,此步骤可省略
[root@kuang76 ~]# cp /root/keepalived-2.0.12/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/    #新版本Keepalived安装后自动会安装该配置文件,此步骤可省略

#③添加Keepalived到系统服务(此步骤省略,会出问题)
[root@kuang76 ~]# chkconfig --add keepalived    #添加到系统服务
[root@kuang76 ~]# systemctl enable keepalived.service
[root@kuang76 ~]# chkconfig keepalived on    #检测是否添加成功

#④步骤三添加Keepalived到系统服务的回退方案(此步骤同步骤三省略)
[root@kuang76 ~]# chkconfig --del keepalived
[root@kuang76 ~]# rm -rf /lib/systemd/system/keepalived.service
[root@kuang76 ~]# rm -rf /etc/init.d/keepalived 

#⑤启动Keepalived
[root@kuang76 ~]# /etc/init.d/keepalived start

配置主备Nginx心跳检测

1、简单Keepalived实现
①主Nginx的Keepalived配置
[root@kuang73 ~]# vim /etc/keepalived/keepalived.conf

global_defs {
    router_id kuang73    #设置Nginx Master的id,在一个网络应该是唯一的
}
vrrp_instance VI_1 {
    state MASTER    #指定Keepalived的角色为主
    interface ens33    #当前进行Vrrp通讯的网络接口卡
    virtual_router_id 51    #虚拟路由组编号,主从要一致
    priority 100    #优先级,数值越大,获取处理请求的优先级越高
    advert_int 1    #检查间隔,默认为1s(Vrrp组播周期秒数)
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.7.100
    }
}

[root@kuang-73 ~]# /etc/init.d/keepalived restart #重启Keepalived
②备Nginx的Keepalived配置
[root@kuang-76 ~]# vim /etc/keepalived/keepalived.conf

global_defs {
    router_id kuang76
}
vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.7.100
    }
}

[root@kuang-76 ~]# /etc/init.d/keepalived restart #重启Keepalived
③正常访问虚拟IP验证
[root@kuang73 ~]# netstat -antup | grep 80 #通过是否新增连接判断访问的Nginx服务器

img

④主Nginx的Keepalived挂掉时访问虚拟IP
[root@kuang73 ~]# pkill keepalived

img

注意:当我们配置了Keepalived,当主Keepalived的宕机了。会自动更改路由到Backup的Keepalived,但是如果Nginx挂掉的话,Keepalived就会找不到Nginx服务这样还是会造成Nginx服务不可用(访问异常)。那么我们就可以在Keepalived中添加心跳script,用来检测Nginx心跳,如果检测不到Nginx服务,就关闭掉Keepalived的进程。这样从机的Keepalive服务就可以被调用。

2、Nginx心跳检测
①Nginx的心跳检测脚本
[root@kuang73 ~]# vim /usr/local/nginx/sbin/check_nginx_alive.sh

#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ]
   then
     echo 'nginx server is died'
     killall keepalived
fi

[root@kuang73 ~]# chmod 755 /usr/local/nginx/sbin/check_nginx_alive.sh
②Keepalived配置文件中应用心跳检测脚本
[root@kuang73 ~]# vim /etc/keepalived/keepalived.conf

vrrp_script check_nginx_alive {
    script "/usr/local/nginx/sbin/check_nginx_alive.sh"
    interval 3    #检测脚本执行的间隔,单位是秒
    weight -10
}
global_defs {
    router_id kuang73
}
vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.7.100
    }
    track_script {
        check_nginx_alive    #调用检测脚本
    }
}

③测试Nginx高可用
[root@kuang73 ~]# pkill nginx #主Ningx宕掉后,客户端访问跳到备Nginx

img

注意:上面是主机kuang-73的配置,可以看到一共新添加了两个地方,主要是添加了一个Shell脚本用于检测Nginx是否存活,然后再在Keepalived的配置文件中,添加这个这个检测心跳的文件。这个是主机的配置,那么从机也需要做相应的修改。那么不管是Keepalived宕机还是Nginx宕机都会切换到可以热备的机器中去,这样就达到了Nginx的可高用。

Keepalived脑裂检测

#检测思路:正常情况下Keepalived的VIP地址是在主节点上的,如果在从节点发现了VIP,就设置报警信息
1、Keepalived脑裂检测脚本
[root@kuang-76 ~]# vim check_split_brain.sh

#!/bin/bash
# 检查脑裂的脚本,在备节点上进行部署
LB01_VIP=192.168.7.100
LB01_IP=192.168.7.3
LB02_IP=192.168.7.6
while true
do
  ping -c 2 -W 3 $LB01_VIP &>/dev/null
    if [ $? -eq 0 -a `ip add|grep "$LB01_VIP"|wc -l` -eq 1 ];then
        echo "ha is brain."
    else
        echo "ha is ok"
    fi
    sleep 5
done

2、正常执行结果如下
[root@kuang-76 ~]# bash check_split_brain.sh
ha is ok
ha is ok
3、当发现异常时候的执行结果
[root@kuang-76 ~]# bash check_split_brain.sh
ha is ok
ha is ok
ha is brain.
ha is brain.

Keepalived拓展

1、配置Keepalived的日志
①修改Keepalived配置文件
[root@kuang73 ~]# vim /etc/sysconfig/keepalived #修改如下配置内容
KEEPALIVED_OPTIONS=“-D -d -S 0”
②Keepalived配置说明
“-D” 就是输出日志的选项
这里的“-S 0”表示local0.* 具体的还需要看一下/etc/syslog.conf文件
③修改Syslog配置文件
[root@kuang73 ~]# vim /etc/rsyslog.conf #添加以下代码
local0.* /var/log/keepalived.log
④重新启动Keepalived和Syslog服务
[root@kuang73 ~]# systemctl restart rsyslog
[root@kuang73 ~]# /etc/ini.d/keepalived restart
⑤然后就可以查看Keepalived的日志了
[root@kuang73 ~]# tail -f /var/log/keepalived.log
2、进程控制软件安装包
[root@kuang-76 ~]# rpm -qf which killall
psmisc-22.20-15.el7.x86_64
[root@kuang-76 ~]# rpm -qf which pkill
procps-ng-3.3.10-17.el7.x86_64
3、产生脑裂情况
#在主备Nginx的/etc/keepalived/keepalived.conf最后添加如下
#实验不成功,未实现脑裂现象

virtual_server 192.168.64.100 80 {
    delay_loop 6
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP
real_server 192.168.64.128 80 {
    weight 1
    TCP_CHECK{
        connect_timeout 3
        nb_get_retry 3
        delay_before_retry 3
        connect_port 80
    }
  }
}

SessionSticky实现会话保持

​ 此模块是让Nginx基于Cookie实现的会话保持,通过在Cookie中写上route路径,客户端再次请求时需要带上该Cookie,就通过route字段区分要转发的upstream。

​ 会话粘滞解决了ip_hash两个问题,一个是ip_hash只能通过ip来固定负载,负载没那么均衡,而且经过同一网关的私网网段用户都被固定到一台后端服务器上;另一个是当Nginx不在最前端,并且有多台Nginx时(七层负载均衡前有四层负载均衡的情况),Nginx之间就没法保持会话统一了。

Nginx实现SessionSticky调度

①下载第三方SessionSticky模块
[root@kuang-75 ~]# cd /usr/local/src/
[root@kuang-75 src]# git clone https://github.com/devsbb/nginx-sticky-module-ng.git #要选择较新的版本,老版本在编译时很多都会出错
②编译安装Nginx支持SessionSticky
[root@kuang-75 nginx-1.14.2]# ./configure --with-http_perl_module --with-http_stub_status_module --add-module=…/nginx-sticky-module #保留原来的参数
[root@kuang-75 nginx-1.14.2]# make -j 4 && make install
③配置实用SessionSticky功能
[root@kuang-75 ~]# vim /usr/local/nginx/conf/nginx.conf

#上下文为server
location / {
    proxy_pass http://backend;
}
#上下文为http
upstream backend {
  sticky;
  server 192.168.7.6;
  server 192.168.7.7;
}

SessionStiky实现原理

  • 用户第一次的请求使用RR调度到一个upstream上
  • Session Sticky 模块在upstream 返回响应后,向客户的浏览器写入Cookie ,默认名为route,保存的内容是一个md5 码
  • 之后模块接收到客户浏览器的请求时,就根据route来决定将请求转发到upstream中哪台服务器上
  • 如果发现客户Cookie中不带有route字段,则按RR方式调度

缓存

缓存知识总结

1、浏览器的对不同字段的反应
不缓存:no-store
需要重新验证:no-cache、max-age=0
缓存静态资源:public、private、max-age≠0
2、代理服务器(CDN节点)对不同字段的反应
不缓存:no-store、no-cache、max-age=0、private
缓存静态资源:public、max-age≠0
#注意:浏览器和代理服务器(CDN节点)在对待cache超时完全不同,cache超时后浏览器只是问一下if-modified-since,而代理服务器(CDN节点)是要真实的(200状态码的)再请求一下。
3、默认情况下各类缓存控制字段的优先级
对缓存的过期与清除起作用的因素的优先级从高到低一次为:
inactive配置项、源服务器设置的Expires、源服务器设置的Max-Age、proxy_cache_valid配置项。
(由于腾讯云CDN配置proxy_ignore_headers "max-age"和proxy_ignore_headers “expires”,则只收到proxy_cache_valid直接影响,inactive配置时间待后续确定)

#以下为腾讯云CDN平台策略
4、腾讯云CDN对Cache-Control字段默认策略
当用户请求您某一业务资源时,源站对应的 HTTP Response Header 中存在 Cache-Control 字段,此时默认平台策略如下:
①Cache-Control 字段为 Max-Age,对该资源的缓存时间以配置的节点缓存过期时间为主,不继承 Max-Age 指定时间。(Nginx代理缓存中实现的方式为proxy_ignore_headers “max-age”,CDN平台也会缓存任何max-age为任何的资源,包括max-age=0的资源)
②Cache-Control 字段为 no-cache 、 no-store 或 private,此时 CDN 节点对此资源不做缓存。
③无 Cache-Control 字段,CDN 会默认添加:Cache-Control:max-age = 600头部。
5、腾讯云CDN状态码缓存策略
若返回非2XX状态码,除404状态码默认缓存10秒,其他状态码均默认为不缓存。若源站无法迅速解决非2XX状态码,且不希望所有请求全部透传回源站,可通过配置状态码缓存过期时间,由 CDN 节点直接响应非2XX状态码,减轻源站压力。

6、开启缓存后不再透传一些字段
如果启用缓存,来自客户端请求的头字段“If-Modified-Since”, “If-Unmodified-Since”, “If-None-Match”, “If-Match”, “Range”, 和 “If-Range” 将不会被代理服务器传递。比如If-Modified-Since就不会代理过去了,CDN回源只能是200的状态码(没有304状态码)。包括腾讯云CDN也是一样的(不能缓存的,不可能给客户透传304请求,而是全部都要200请求)。另外腾讯云CLB没有开启缓存(所以都会透传客户端的304请求,这一点可以证明CLB没有开启缓存)。
另外可以使用proxy_set_header自定义回源的头字段。
7、当客户端询问if-modified-since,CDN节点没有该资源回源时
①能缓存,返回304
②不能缓存,返回200
8、代理服务器(CDN节点)缓存资源超时的情况
代理服务器上的cache超过了源服务器打上的cache-control时间,并不会主动的去再次请求源服务器,而是在客户端发出请求后再去源服务器请求,然后响应一个304,但是X-cache字段打上EXPIRED(客户端就知道,这是在代理服务器上缓存过期了,然后回源取到其本地给我响应的)。
9、有缓存的校验过期机制图

img

各类缓存字段的优先级

1、由于expires是http/1.0版本,max-age是http/1.1版本的
①所以在浏览器(一般用http/1.1)上max-age覆盖expires
②而在Nginx因为回源默认是用http/1.0,所以expires覆盖max-age

2、各字段的缓存优先级
①以下是对Nginx代理有效
对缓存的过期与清除起作用的因素的优先级从高到低一次为:
inactive配置项、源服务器设置的Expires、源服务器设置的Max-Age、proxy_cache_valid配置项

3、浏览器和代理服务器的不同

浏览器和代理服务器在对待cache超时有截然的不同,cache超时浏览器只是问一下if-modified-since,而代理服务器是要真实的(200状态码的)再请求一下。

客户端缓存操作

public
    新打开一个窗口: 不会去访问服务器
    原页面回车:不会去访问服务器,取自缓存
    刷新:浏览器重新请求服务器
    单击返回按钮:页面取自缓存
private
    新打开一个窗口:浏览器重新发送请求到服务器
    原页面回车:第一次会去请求服务器,以后均是来自于缓存
    刷新:浏览器重新请求服务器
    单击返回按钮:页面取自缓存

no-cache/no-store
    打开新窗口、回车 、刷新、单击返回:均会访问请求服务器;
    这里要说明的是:no-cache 不是没有缓存,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性;
    Cache-Control: no-store:这个才是响应不被缓存的意思。 

must-revalidation/proxy-revalidation
    打开新窗口:浏览器重新发送请求到服务器
    原页面回车:第一次请求服务器,以后均是来自缓存页面
    刷新:浏览器重新请求服务器
    单击返回按钮:页面取自缓存

max-age=xxx
    刷新按钮:重新发送请求到服务器;其他的均是在访问页面XXX秒后一直来自缓存;

总结:

	打开新窗口 如果指定cache-control的值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如:Cache-control: max-age=5 表示当访问此网页后的5秒内不会去再次访问服务器.
	在地址栏回车 如果值为private或must-revalidate,则只有第一次访问时会访问服务器,以后就不再访问。如果值为no-cache,那么每次都会访问。如果值为max-age,则在过期之前不会重复访问。
	按后退按扭 如果值为private、must-revalidate、max-age,则不会重访问,而如果为no-cache,则每次都重复访问.
	按刷新按扭 无论为何值,都会重复访问.

HTTP协议的Cache -Control指定请求和响应遵循的缓存机制:

在请求消息或响应消息中设置 Cache-Control并不会影响另一个消息处理过程中的缓存处理过程。
请求时的缓存指令包括:
	no-cache、no-store、max-age、 max-stale、min-fresh、only-if-cached等。
响应消息中的指令包括:
	public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age。

浏览器中关于Cache的3属性:

  1. Cache-Control:
    设置相对过期时间, max-age指明以秒为单位的缓存时间. 若对静态资源只缓存一次, 可以设置max-age的值为315360000000 (一万年).
    Http协议的cache-control的常见取值及其组合释义:
    no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器.
    no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源)
    private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
    public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等
    max-age: 相对过期时间, 即以秒为单位的缓存时间.
    no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器.
    private, 正数的max-age: 后退时候不会访问服务器
    no-cache, 正数的max-age: 后退时会访问服务器
    点击刷新: 无论如何都会访问服务器.
  2. Expires:
    设置以分钟为单位的绝对过期时间, 优先级比Cache-Control低, 同时设置Expires和Cache-Control则后者生效.
  3. Last-Modified:
    该资源的最后修改时间, 在浏览器下一次请求资源时, 浏览器将先发送一个请求到服务器上, 并附上If-Unmodified-Since头来说明浏览器所缓存资源的最后修改时间, 如果服务器发现没有修改, 则直接返回304(Not Modified)回应信息给浏览器(内容很少), 如果服务器对比时间发现修改了, 则照常返回所请求的资源.

​ Last-Modified属性通常和Expires或Cache-Control属性配合使用,因为即使浏览器设置缓存,当用户点击”刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时Last-Modified将能够很好的减小回应开销。

基于proxy_cache的反向代理缓存

1、特别注意
开启反向代理缓存之后,很多字段(从客户端发过来)就不能透传到源服务器了,比如If-Modified-Since就不会代理过去了,回源只能是200的状态码(没有304状态码)
连腾讯云CDN也是一样的(不能缓存的,不可能给客户透传304,而是全部都要200请求),腾讯云LB没有开启缓存(所以很多都是透传304,这一点可以证明CLB没有开启缓存)

1、当客户端询问if-modified-since,而proxy服务器上没有该资源回源时,分两种情况:
①能缓存,返回304
②不能缓存,返回200

#注意:整个HIT缓存的关键点在三,第一客户端请求的方式(第一次直接请求,第二次问是否改变),第二Nginx此时是否有请求内容的缓存,第三真实服务器端其实没那么多考虑的,只管响应请求(不过其有个关键的地方,指挥某些资源需不需要在客户端缓存,需要缓存的客户端才会在第二次询问资源是否改变,否则每次都是直接请求)
#注意:反向代理缓存的实现,跟指令的顺序没关系
1、基于proxy_cache的缓存(此验证成功)
①配置Nginx使用proxy_cache缓存
[root@kuang-74 ~]# vim /usr/local/nginx/conf/nginx.conf
proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g; #此配置在http上下文中,并且要在include导入命令前
add_header X-Cache $upstream_cache_status; #添加之后会显示是否命中,只有在以下配置中开启了代理缓存才会显示该字段
location / {
proxy_cache cache_one;
proxy_cache_key h o s t host hosturi i s a r g s is_args isargsargs; #这个在中途加进去会出问题,因为原先可能不是按照这个索引格式来写到缓存里的
proxy_cache_valid 200 304 302 24h; #这里有没有任何状态码都没任何影响
proxy_pass http://192.168.7.3/;
}

腾讯云CDN策略:若返回非2XX状态码,除404状态码默认缓存10秒,其他状态码均默认为不缓存。若源站无法迅速解决非2XX状态码,且不希望所有请求全部透传回源站,可通过配置状态码缓存过期时间,由 CDN 节点直接响应非2XX状态码,减轻源站压力。

②参数说明
levels=1:2 表示缓存目录的第一级目录是1个字符,第二级目录是2个字符
The levels parameter defines hierarchy levels of a cache: from 1 to 3, each level accepts values 1 or 2.(最多支持三级,字符只能是1或2)
inactive=1d 表示这个zone中的缓存文件如果在1天内都没有被访问,那么文件会被cache manager进程删除掉
max_size=30g 表示这个zone的硬盘容量为30GB
proxy_cache_valid 200 304 302 24h 设置状态码为200、304和302的响应可以进行缓存,并且缓存时间为24小时;其目的是为不同的HTTP返回状态码的资源设置不同的缓存时长。
③缓存访问说明
#前面状态码跟客户端的关联最大,200表示客户端第一次访问某页面,后面变为304过程是客户端问服务器这个资源有改变没(然后服务器回没有变化,不管是Nginx回,还是后端服务器回)
#HIT才是和缓存服务器有紧密联系,命中了某索引的缓存就是HIT,没有命中就是MISS
#注意:下图不是第一次访问,因为不可能出现第一次访问就HIT的情况

img

④客户端的no-cache访问(按Ctrl+F5)
#no-cache最准确的解释:客户端表示我这次访问和cache没关,你直接回我内容就行了
#有cache时,是因为客户端询问服务器某资源是否有变更,而服务器回复的cache相关描述信息(包括上次修改时间,和过期时间)
客户端的no-cache访问跟缓存服务器不是一回事,前者指的是不使用本地的cache(问服务器某文件是否改变),而是直接请求资源。Nginx有该资源,自然就直接返回响应你了,而不会去后端请求真实服务器。
#此时的真实服务器已经无关紧要了,关掉真实服务器的服务,Nginx依然能提供服务,只要Nginx上的缓存文件没有删除

img

⑤删除Nginx上的缓存

[root@kuang-76 ~]# rm -rf /usr/local/nginx/cache/

此时直接就报错了,因为找不到相应资源,后端服务器又关闭了

img

⑥启动后端真实服务器

[root@kuang-77 ~]# systemctl start nginx

再刷新两次页面,则分别是200的MISS和304的HIT

img

img

⑦测试缓存内容即使更新能力

#以下在真实服务器上更改某页面内容

[root@kuang-77 html]# echo haha >> /usr/share/nginx/html/index.html

使用F5刷新和Ctrl+F5更新,同样访问的是Nginx缓存中的旧内容

img

img

proxy_cache_path  /var/www/cache levels=1:2 keys_zone=my-cache:8m max_size=1000m inactive=600m;
proxy_temp_path /var/www/cache/tmp;
real_ip_header X-Forwarded-For;

server {
        listen 80;
        server_name _;
        server_tokens off;
        location / {
                proxy_pass              http://127.0.0.1:8080/;
                proxy_set_header        Host                    $host;
                proxy_set_header        X-Real-IP               $remote_addr;
                proxy_set_header        X-Forwarded-For         $proxy_add_x_forwarded_for;
                proxy_cache  my-cache;
                proxy_cache_valid 3s;
                proxy_no_cache $cookie_PHPSESSID;
                proxy_cache_bypass $cookie_PHPSESSID;
                proxy_cache_key         "$scheme$host$request_uri";
                add_header X-Cache $upstream_cache_status;
        }
}

清除反向代理缓存

1、Nginx缓存配置及nginx ngx_cache_purge模块的使用
①ngx_cache_purge模块的作用
用于清除指定url的缓存
②模块下载地址
http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
③编译安装Nginx

[root@kuang-77 ~]# ./configure --prefix=/app/nginx --with-http_stub_status_module --with-http_ssl_module --add-module=../ngx_cache_purge-2.3
[root@kuang-77 ~]# make
[root@kuang-77 ~]# make install

④Nginx配置如下

proxy_cache_path  /app/proxy_cache_dir  levels=1:2 keys_zone=cache1:200m inactive=1d max_size=10g;
server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        location ~ /purge(/.*) {
            allow           127.0.0.1;
            allow           192.168.116.0/24
            deny            all;
            proxy_cache_purge    cache1 $host$1$is_args$args;
        }
        location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ {
            proxy_set_header Host  $host;
            proxy_set_header X-Forwarded-For  $remote_addr;
            proxy_pass http://127.0.0.1:8080;
            proxy_cache cache1; #设置资源缓存的zone
            proxy_cache_key $host$uri$is_args$args; 
            proxy_cache_valid 200 304 12h;  #对不同的HTTP状态码设置不同的缓存时间
            expires 7d; #缓存时间
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
}

⑤启动Nginx,查看进程,发现新增了两个进程

img

2、验证缓存及手动清除指定URL缓存功能
如果在缓存时间之类需要更新被缓存的静态文件怎么办呢,这时候就需要手动来清除缓存了
①访问一张图片

img

②更换图片内容,然后清除缓存后再访问
http://192.168.116.130/purge/test/n.jpg #访问此URL可以清除缓存

img

③再次访问原来的URL,图片已更新

img

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值