目录
一、Nginx 高级配置
1.1 Nginx 状态页
1.2 Nginx 压缩功能
1.3 Nginx的版本隐藏
1.4 Nginx 变量使用
1.4.1 内置变量
二、 Nginx Rewrite 相关功能
2.1 ngx_http_rewrite_module 模块指令
2.1.1 if 指令
2.1.2 set 指令
2.1.3 break 指令
2.1.4 return 指令
2.2 rewrite 指令
2.2.1 rewrite flag 使用介绍
2.2.2 rewrite案例: 域名永久与临时重定向
2.2.2.1 永久重定向304
2.2.2.2 临时重定向301
2.2.3 rewrite 案例: break 与 last
2.2.4 rewrite案例: 自动跳转 https
2.2.5 rewrite 案例: 判断文件是否存在
2.3 Nginx 防盗链
2.3.1 实现盗链
2.3.2 实现防盗链
二、 Nginx 反向代理功能
2.1 实现 http 反向代理
2.1.1 http 协议反向代理
2.1.1.1 反向代理配置参数
2.1.1.2 实战案例: 反向代理单台 web 服务器
2.1.1.3 实战案例: 指定 location 实现反向代理
2.1.1.3.1 针对指定的 location
2.1.1.3.2 针对特定的资源实现代理
2.1.1.4 反向代理示例: 缓存功能
2.1.1.4.1 非缓存场景压测
2.1.1.4.2 准备缓存配置
2.1.1.4.3 访问并验证缓存文件
2.1.2 http 反向代理负载均衡
2.1.2.1 http upstream配置参数
2.1.2.2 反向代理示例: 后端多台 web服务器
2.2 实现 Nginx 四层负载均衡
2.2.1 tcp负载均衡配置参数
2.2.2 负载均衡实例: MySQL
2.2.3 udp 负载均衡实例: DNS
2.3 实现 FastCGI
2.3.1 FastCGI配置指令
2.3.2 FastCGI实战案例 : Nginx与php-fpm在同一服务器
2.3.3 php的动态扩展模块(php的缓存模块)
2.3.4 php高速缓存
3.1 openresty
一、Nginx 高级配置
1.1 Nginx 状态页
- 基于nginx 模块 ngx_http_stub_status_module 实现,
- 在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module
- 否则配置完成之后监测会是提示法错误
注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态
# 配置示例:location /nginx_status {stub_status;auth_basic "auth login";auth_basic_user_file /apps/nginx/conf/.htpasswd;allow 192.168.0.0/16;allow 127.0.0.1;deny all;}# 状态页用于输出 nginx 的基本状态信息:# 输出信息示例:Active connections: 291server accepts handled requests16630948 16630948 31070465上面三个数字分别对应 accepts,handled,requests 三个值Reading: 6 Writing: 179 Waiting: 106Active connections: #当前处于活动状态的客户端连接数#包括连接等待空闲连接数=reading+writing+waitingaccepts: #统计总值,Nginx 自启动后已经接受的客户端请求连接的总数。handled: #统计总值,Nginx自启动后已经处理完成的客户端请求连接总数#通常等于accepts,除非有因worker_connections限制等被拒绝的连接requests: #统计总值,Nginx自启动后客户端发来的总的请求数Reading: #当前状态,正在读取客户端请求报文首部的连接的连接数#数值越大,说明排队现象严重,性能不足Writing: #当前状态,正在向客户端发送响应报文过程中的连接数,数值越大, 说明访问量很大Waiting: #当前状态,正在等待客户端发出请求的空闲连接数开启 keep-alive的情况下,这个值等于active –(reading+writing)
1.2 Nginx 压缩功能
# 启用或禁用 gzip 压缩,默认关闭gzip on | off;# 压缩比由低到高从 1 到 9 ,默认为 1 ,值越高压缩后文件越小,但是消耗 cpu 比较高。基本设定未 4 或者 5gzip_comp_level 4;# 禁用 IE6 gzip 功能,早期的 IE6 之前的版本不支持压缩gzip_disable "MSIE [1-6]\.";#gzip 压缩的最小文件,小于设置值的文件将不会压缩gzip_min_length 1k;# 启用压缩功能时,协议的最小版本,默认 HTTP/1.1gzip_http_version 1.0 | 1.1;# 指定 Nginx 服务需要向服务器申请的缓存空间的个数和大小 , 平台不同 , 默认 :32 4k 或者 16 8k;gzip_buffers number size;# 指明仅对哪些类型的资源执行压缩操作 ; 默认为 gzip_types text/html ,不用显示指定,否则出错gzip_types mime-type ...;# 如果启用压缩,是否在响应报文首部插入 “Vary: Accept-Encoding”, 一般建议打开gzip_vary on | off;# 预压缩,即直接从磁盘找到对应文件的 gz 后缀的式的压缩文件返回给用户,无需消耗服务器 CPU# 注意 : 来自于 ngx_http_gzip_static_module 模块gzip_static on | off;
# 重启 nginx 并进行访问测试压缩功能[root@nginx ~]# mkdir /webdata/nginx/timinglee.org/lee/data -p[root@nginx ~]# cp /usr/local/nginx/logs/access.log /webdata/nginx/timinglee.org/lee/data/data.txt
[root@nginx ~]# ll /webdata/nginx/timinglee.org/lee/data/data.txt
-rw-r--r-- 1 root root 2494866 Aug 20 23:18 /webdata/nginx/timinglee.org/lee/data/data.txt
[root@nginx ~]# echo test > /webdata/nginx/timinglee.org/lee/data/test.html #小于1k 的文件测试是否会压缩[root@nginx ~]# vim /usr/local/nginx/conf/nginx.conf
gzip on;
gzip_comp_level 5;
gzip_min_length 1k;gzip_types text/plain application/javascript application/x-javascript text/cssapplication/xml text/javascript application/x-httpd-php image/gif image/png;gzip_vary on;# 重启 Nginx 并访问测试:[root@nginx ~]# nginx -s reload
[root@nginx ~]#[root@nginx ~]# curl --head --compressed www.timinglee.org/data/test.html
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Tue, 20 Aug 2024 15:25:51 GMT
Content-Type: text/html
Content-Length: 5
Last-Modified: Tue, 20 Aug 2024 15:18:58 GMT
Connection: keep-alive
Keep-Alive: timeout=60
ETag: "66c4b3e2-5"
Accept-Ranges: bytes[root@nginx ~]# curl --head --compressed www.timinglee.org/data/data.txt
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Tue, 20 Aug 2024 15:26:08 GMT
Content-Type: text/plain
Last-Modified: Tue, 20 Aug 2024 15:18:05 GMT
Connection: keep-alive
Keep-Alive: timeout=60
Vary: Accept-Encoding
ETag: W/"66c4b3ad-261192"
Content-Encoding: gzip
1.3 Nginx的版本隐藏
[root@nginx ~]# cd nginx-1.24.0/
[root@nginx nginx-1.24.0]# vim src/core/nginx.h#define nginx_version 1024000
#define NGINX_VERSION "1.24.0"
#define NGINX_VER "nginx/" NGINX_VERSION
1.4 Nginx 变量使用
- nginx的变量可以在配置文件中引用,作为功能判断或者日志等场景使用
- 变量可以分为内置变量和自定义变量
- 内置变量是由nginx模块自带,通过变量可以获取到众多的与客户端访问相关的值。
1.4.1 内置变量
http://nginx.org/en/docs/varindex.html
$remote_addr;# 存放了客户端的地址,注意是客户端的公网 IP$args;# 变量中存放了 URL 中的所有参数# 例如 :https://search.jd.com/Search?keyword= 手机 &enc=utf-8# 返回结果为 : keyword= 手机 &enc=utf-8$is_args# 如果有参数为 ? 否则为空$document_root;# 保存了针对当前资源的请求的系统根目录 , 例如 :/webdata/nginx/timinglee.org/lee 。$document_uri;# 保存了当前请求中不包含参数的 URI ,注意是不包含请求的指令# 比如 :http://lee.timinglee.org/var?\id=11111 会被定义为 /var# 返回结果为 :/var$host;# 存放了请求的 host 名称limit_rate 10240;echo $limit_rate;# 如果 nginx 服务器使用 limit_rate 配置了显示网络速率,则会显示,如果没有设置, 则显示 0$remote_port;# 客户端请求 Nginx 服务器时随机打开的端口,这是每个客户端自己的端口$remote_user;# 已经经过 Auth Basic Module 验证的用户名$request_body_file;# 做反向代理时发给后端服务器的本地资源的名称$request_method;# 请求资源的方式, GET/PUT/DELETE 等$request_filename;# 当前请求的资源文件的磁盘路径,由 root 或 alias 指令与 URI 请求生成的文件绝对路径,# 如 :webdata/nginx/timinglee.org/lee/var/index.html$request_uri;# 包含请求参数的原始 URI ,不包含主机名,相当于 :$document_uri?$args,# 例如: /main/index.do?id=20190221&partner=search$scheme;# 请求的协议,例如 :http , https,ftp 等$server_protocol;# 保存了客户端请求资源使用的协议的版本,例如 :HTTP/1.0 , HTTP/1.1 , HTTP/2.0 等$server_addr;# 保存了服务器的 IP 地址$server_name;# 虚拟主机的主机名$server_port;# 虚拟主机的端口号$http_user_agent;# 客户端浏览器的详细信息$http_cookie;# 客户端的所有 cookie 信息$cookie_<name>#name 为任意请求报文首部字部 cookie 的 key 名$http_<name>#name 为任意请求报文首部字段 , 表示记录请求报文的首部字段, ame 的对应的首部字段名需要为小写,如果有横线需要替换为下划线# 示例 :echo $http_user_agent;echo $http_host;$sent_http_<name>#name 为响应报文的首部字段, name 的对应的首部字段名需要为小写,如果有横线需要替换为下划线 , 此变量有问题echo $sent_http_server;$arg_<name># 此变量存放了 URL 中的指定参数, name 为请求 url 中指定的参数echo $arg_id;
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {
listen 80;
server_name www.timinglee.org;
root /webdata/nginx/timinglee.org/lee;
location /var {
default_type text/html;
echo $remote_addr;
echo $args;
echo $document_root;
echo $document_uri;
echo $host;
echo $http_user_agent;
echo $request_filename;
echo $scheme;
echo $scheme://$host$document_uri?$args;
echo $http_cookie;
echo $cookie_key2;
echo $http_Accept;
}
}#测试[root@nginx ~]# nginx -s reload
[root@nginx ~]#
[root@nginx ~]# curl -b "title=lee;key1=lee,key2=timinglee" "www.timinglee.org/var?search=lee&&id=666666"192.168.10.140
search=lee&&id=666666/webdata/nginx/timinglee.org/lee/varlee.timinglee.orgcurl/7.29.0/webdata/nginx/timinglee.org/lee/varhttphttp://lee.timinglee.org/var?search=lee&&id=666666title=lee;key1=lee,key2=timingleetiminglee*/*
Syntax: set $variable value;Default: —Context: server, location, if
set $name timinglee;echo $name;set $my_port $server_port;echo $my_port;echo "$server_name:$server_port";[root@nginx ~]# mkdir -p /webdata/nginx/timinglee.org/lee/var[root@nginx ~]# echo xixi > /webdata/nginx/timinglee.org/lee/var/index.html[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {
listen 80;
server_name www.timinglee.org;
root /webdata/nginx/timinglee.org/lee;
location /var {
default_type text/html;
set $name timinglee;
echo $name;
set $web_port $server_port;
echo $web_port;
}
}
#测试输出[root@nginx ~]# nginx -s reload
[root@nginx ~]#
[root@nginx ~]# curl www.timinglee.org/vartiminglee80
二、 Nginx Rewrite 相关功能
- Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求
- 此功能依靠 PCRE(perl compatible regular expression),因此编译之前要安装PCRE库
- rewrite是nginx服务器的重要功能之一,用于实现URL的重写,URL的重写是非常有用的功能
- 比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的链接,就可以设置为访问
- 另外还可以在一定程度上提高网站的安全性。
2.1 ngx_http_rewrite_module 模块指令
官方文档 : https://nginx.org/en/docs/http/ngx_http_rewrite_module.html
2.1.1 if 指令
https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if
if (条件匹配) {action}
= #比较变量和字符串是否相等,相等时 if 指令认为该条件为 true ,反之为 false!= #比较变量和字符串是否不相等,不相等时 if 指令认为条件为 true ,反之为 false~ #区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假!~ #区分大小写字符 , 判断是否匹配,不满足匹配条件为真,满足匹配条件为假~* #不区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假!~* #不区分大小字符 , 判断是否匹配,满足匹配条件为假,不满足匹配条件为真-f 和 !-f #判断请求的文件是否存在和是否不存在-d 和 !-d #判断请求的目录是否存在和是否不存在-x 和 !-x #判断文件是否可执行和是否不可执行-e 和 !-e #判断请求的文件或目录是否存在和是否不存在 ( 包括文件,目录,软链接 )#注意:#如果$变量的值为空字符串或0,则if指令认为该条件为false,其他条件为true。#nginx 1.0.1之前$变量的值如果以0开头的任意字符串会返回false
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conflocation /test {
index index.html;
default_type text/html;
if ( $scheme = http ){
echo "if ---------> $scheme";
}
if ( $scheme = https ){
echo "if ---------> $scheme";
}
}
location /test2 {
if ( !-e $request_filename ){
echo "$request_filename is not exist";
return 409;
}
}#测试:[root@nginx ~]# curl www.timinglee.org/test/
if ---------> http[root@nginx ~]# curl www.timinglee.org/test2/test
/webdata/nginx/timinglee.org/lee/test2/test is not exist
2.1.2 set 指令
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conf
server {listen 80;server_name www.timinglee.org;root /webdata/nginx/timinglee.org/lee;location /test3{set $name lee;echo $name;}}#测试:[root@nginx ~]# curl www.timinglee.org/test3/
lee
2.1.3 break 指令
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name www.timinglee.org;root /webdata/nginx/timinglee.org/lee;location /break{default_type text/html;set $name lee;echo $name;break;set $port $server_port;echo $port;}}#测试:[root@nginx ~]# curl www.timinglee.org/break/ # 当未添加 break 时lee80[root@nginx ~]# curl www.timinglee.org/break/ # 添加 break 后lee
2.1.4 return 指令
return code; # 返回给客户端指定的 HTTP 状态码return code [text]; # 返回给客户端的状态码及响应报文的实体内容# 可以调用变量 , 其中 text 如果有空格 , 需要用单或双引号return code URL; # 返回给客户端的 URL 地址
server {listen 80;server_name www.timinglee.org;root /webdata/nginx/timinglee.org/lee;location /return {default_type text/html;if ( !-e $request_filename){return 301 http://www.baidu.com;#return 666 "$request_filename is not exist";}echo "$request_filename is exist";}}测试:[root@client ~]# curl www.timinglee.org/return/webdata/nginx/timinglee.org/lee/return is exist[root@client ~]# curl www.timinglee.org/return1/webdata/nginx/timinglee.org/lee/return1 is not exist# 测试 return 301 http://www.baidu.com;可在浏览器直接访问 lee.timinglee.org/return1
location /test {default_type application/json;return 200 '{"status:"success"}';}
2.2 rewrite 指令
https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite
rewrite regex replacement [flag];
- rewrite将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为表达式指定的新的URI
- 注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查,隐含有循环机制,但不超过10次;如果超过,提示500响应码,[flag]所表示的标志位用于控制此循环机制
- 如果替换后的URL是以http://或https://开头,则替换结果会直接以重定向返回给客户端, 即永久重定向301
. # 匹配除换行符以外的任意字符\w # 匹配字母或数字或下划线或汉字\s # 匹配任意的空白符\d # 匹配数字\b # 匹配单词的开始或结束^ # 匹配字付串的开始$ # 匹配字符串的结束* # 匹配重复零次或更多次+ # 匹配重复一次或更多次? # 匹配重复零次或一次(n) # 匹配重复 n 次{n,} # 匹配重复 n 次或更多次{n,m} # 匹配重复 n 到 m 次*? # 匹配重复任意次,但尽可能少重复+? # 匹配重复 1 次或更多次,但尽可能少重复?? # 匹配重复 0 次或 1 次,但尽可能少重复{n,m}? # 匹配重复 n 到 m 次,但尽可能少重复{n,}? # 匹配重复 n 次以上,但尽可能少重复\W # 匹配任意不是字母,数字,下划线,汉字的字符\S # 匹配任意不是空白符的字符\D # 匹配任意非数字的字符\B # 匹配不是单词开头或结束的位置[^x] # 匹配除了 x 以外的任意字符[^lee] # 匹配除了 magedu 这几个字母以外的任意字符
2.2.1 rewrite flag 使用介绍
- 跳转型指由客户端浏览器重新对新地址进行请求
- 代理型是在WEB服务器内部实现跳转
Syntax: rewrite regex replacement [flag]; # 通过正则表达式处理用户请求并返回替换后的数据包。Default: —Context: server, location, if
redirect;# 临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新 URL 给客户端# 由客户端重新发起请求 ; 使用相对路径 , 或者 http:// 或 https:// 开头,状态码: 302permanent;# 重写完成后以永久重定向方式直接返回重写后生成的新 URL 给客户端# 由客户端重新发起请求,状态码: 301break;# 重写完成后 , 停止对当前 URL 在当前 location 中后续的其它重写操作# 而后直接跳转至重写规则配置块之后的其它配置,结束循环,建议在 location 中使用# 适用于一个 URL 一次重写last;# 重写完成后 , 停止对当前 URI 在当前 location 中后续的其它重写操作,# 而后对新的 URL 启动新一轮重写检查,不建议在 location 中使用# 适用于一个 URL 多次重写,要注意避免出现超过十次以及 URL 重写后返回错误的给用户
2.2.2 rewrite案例: 域名永久与临时重定向
location / {root /data/nginx/html/pc;index index.html;rewrite / http://www.timinglee.com permanent;#rewrite / http://www.timinglee.com redirect;}# 重启 Nginx 并访问域名 http://www.timinglee.org 进行测试
2.2.2.1 永久重定向304
- 域名永久型调整,即域名永远跳转至另外一个新的域名,之前的域名再也不使用,跳转记录可以缓存到客户端浏览器
- 永久重定向会缓存DNS解析记录, 浏览器中有 from disk cache 信息,即使nginx服务器无法访问,浏览器也会利用缓存进行重定向
- 比如: 京东早期的域名 www.360buy.com 由于与360公司类似,于是后期永久重定向到了 www.jd.com
[root@nginx lee]# echo 123456 > /webdata/nginx/timinglee.org/lee/index.html[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {
listen 80;
server_name lee.timinglee.org;
root /webdata/nginx/timinglee.org/lee;
location / {
#rewrite / http://lee.timinglee.com redirect;
rewrite / http://lee.timinglee.com permanent;
}
}
server {
listen 80;
server_name lee.timinglee.com;
root /webdata/nginx/timinglee.com/lee;
}
#测试:[root@nginx ~]# nginx -s reload
[root@nginx ~]#
2.2.2.2 临时重定向301
[root@Nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name lee.timinglee.org;root /webdata/nginx/timinglee.org/lee;location / {rewrite / http://lee.timinglee.com redirect;#rewrite / http://lee.timinglee.com permanent;}}server {listen 80;server_name lee.timinglee.com;root /webdata/nginx/timinglee.com/lee;}
2.2.3 rewrite 案例: break 与 last
[root@nginx ~]# mkdir /webdata/nginx/timinglee.org/lee/{test1,test2,break}[root@nginx ~]# echo test1 > /webdata/nginx/timinglee.org/lee/test1/index.html[root@nginx ~]# echo test2 > /webdata/nginx/timinglee.org/lee/test2/index.html[root@nginx ~]# echo break > /webdata/nginx/timinglee.org/lee/break/index.html[root@nginx ~]#vim /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name lee.timinglee.org;root /webdata/nginx/timinglee.org/lee;location /break {root /webdata/nginx/timinglee.org/lee;rewrite ^/break/(.*) /test1/$1 last;rewrite ^/test1/(.*) /test2/$1 break;}location /last {root /webdata/nginx/timinglee.org/lee;rewrite ^/last/(.*) /test1/$1 last;rewrite ^/test1/(.*) /test2/$1 last;}location /test1 {default_type text/html;return 666 "new test1";}location /test2 {root /webdata/nginx/timinglee.org/lee;}}[root@nginx ~]# curl -L lee.timinglee.org/break/index.htmltest1[root@nginx ~]#curl -L lee.timinglee.org/last/index.htmlnew test1
2.2.4 rewrite案例: 自动跳转 https
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {listen 443 ssl;listen 80;ssl_certificate /apps/nginx/certs/www.timinglee.org.crt;ssl_certificate_key /apps/nginx/certs/www.timinglee.org.key;ssl_session_cache shared:sslcache:20m;ssl_session_timeout 10m;server_name www.timniglee.org;location / { #针对全站跳转root /data/nginx/html/pc;index index.html;if ($scheme = http ){ #如果没有加条件判断,会导致死循环rewrite / https://$host redirect;}}location /login { #针对特定的URL 进行跳转 httpsif ($scheme = http ){ #如果没有加条件判断,会导致死循环rewrite / https://$host/login redirect;}}}# 重启 Nginx 并访问测试[root@nginx ~]#curl -ikL www.timinglee.orgHTTP/1.1 302 Moved TemporarilyServer: nginx/1.18.0Date: Thu, 08 Oct 2020 15:23:48 GMTContent-Type: text/htmlContent-Length: 145Connection: keep-aliveLocation: https://www.magedu.orgHTTP/1.1 200 OKServer: nginx/1.18.0Date: Thu, 08 Oct 2020 15:23:48 GMTContent-Type: text/htmlContent-Length: 7Last-Modified: Sat, 26 Sep 2020 01:18:32 GMTConnection: keep-aliveETag: "5f6e96e8-7"Accept-Ranges: bytespc web
2.2.5 rewrite 案例: 判断文件是否存在
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conflocation / {root /data/nginx/html/pc;index index.html;if (!-e $request_filename) {rewrite .* http://www.timinglee.org/index.html; #实现客户端浏览器的302 跳转#rewrite .* /index.html; #web服务器内部跳转}}# 重启 Nginx 并访问测试
2.3 Nginx 防盗链
none : # 请求报文首部没有 referer 首部,# 比如用户直接在浏览器输入域名访问 web 网站,就没有 referer 信息。blocked : # 请求报文有 referer 首部,但无有效值,比如为空。server_names : #referer 首部中包含本主机名及即 nginx 监听的 server_name 。arbitrary_string : # 自定义指定字符串,但可使用 * 作通配符。示例 : *.timinglee.orgwww.timinglee.*regular expression : # 被指定的正则表达式模式匹配到的字符串 , 要使用 ~ 开头,例如:~.*\.timinglee\.com
172.25.254.1 - - [22/Jul/2024:09:27:36 +0800] "GET /favicon.ico HTTP/1.1" 404 149"http://lee.timinglee.org/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0)Gecko/20100101 Firefox/115.0"2024/07/22 09:27:36 [error] 34596#0: *205 open()"/webdata/nginx/timinglee.org/lee/favicon.ico" failed (2: No such file ordirectory), client: 172.25.254.1, server: lee.timinglee.org, request: "GET/favicon.ico HTTP/1.1", host: "lee.timinglee.org", referrer:"http://lee.timinglee.org/"
2.3.1 实现盗链
# 新建一个主机 192.168.10.141, 盗取另一台主机 lee.timinglee.org/images/logo.png 的图片[root@client ~]# yum install httpd -y[root@client html]# vim /var/www/html/index.html# 准备盗链 web 页面:<html><head><meta http-equiv=Content-Type content="text/html;charset=utf-8"><title> 盗链 </title></head><body><img src="http://lee.timinglee.org/images/logo.png" ><h1 style="color:red"> 欢迎大家 </h1><p><a href=http://lee.timinglee.org> 狂点老李 </a> 出门见喜 </p></body></html># 重启 apache 并访问 http://192.168.10.140 测试# 验证两个域名的日志,是否会在被盗连的 web 站点的日志中出现以下盗链日志信息:[root@Nginx ~]# cat /usr/local/nginx/logs/access.log192.168.10.1 - - [22/Jul/2024:09:50:01 +0800] "GET /images/logo.png HTTP/1.1" 3040 "http:/192.168.10.140/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36Edg/126.0.0.0"192.168.10.1 - - [22/Jul/2024:09:50:18 +0800] "GET / HTTP/1.1" 304 0"http://192.168.10.140/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36Edg/126.0.0.0"
2.3.2 实现防盗链
https://nginx.org/en/docs/http/ngx_http_referer_module.html
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name lee.timinglee.org;root /webdata/nginx/timinglee.org/lee;location /images {valid_referers none blocked server_names *.timinglee.org ~\.baidu\.;if ($invalid_referer){#return 403;rewrite ^/ http://lee.timinglee.org/daolian.png permanent;}}}# 重启 Nginx 并访问测试http://192.168.10.140
二、 Nginx 反向代理功能
- 反向代理:reverse proxy,指的是代理外网用户的请求到内部的指定的服务器,并将数据返回给用户的一种方式,这是用的比较多的一种方式。
- Nginx 除了可以在企业提供高性能的web服务之外,另外还可以将 nginx 本身不具备的请求通过某种预定义的协议转发至其它服务器处理,不同的协议就是Nginx服务器与其他服务器进行通信的一种规范,主要在不同的场景使用以下模块实现不同的功能
ngx_http_proxy_module: # 将客户端的请求以 http 协议转发至指定服务器进行处理ngx_http_upstream_module # 用于定义为 proxy_pass,fastcgi_pass,uwsgi_pass#等指令引用的后端服务器分组ngx_stream_proxy_module: # 将客户端的请求以 tcp 协议转发至指定服务器处理ngx_http_fastcgi_module: # 将客户端对 php 的请求以 fastcgi 协议转发至指定服务器助理ngx_http_uwsgi_module: # 将客户端对 Python 的请求以 uwsgi 协议转发至指定服务器处理
- 同构代理:用户不需要其他程序的参与,直接通过http协议或者tcp协议访问后端服务器
- 异构代理:用户访问的资源时需要经过处理后才能返回的,比如php,python,等等,这种访问资源需要经过处理才能被访问
2.1 实现 http 反向代理
2.1.1 http 协议反向代理
2.1.1.1 反向代理配置参数
# 官方文档: https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_passproxy_pass; #用来设置将客户端请求转发给的后端服务器的主机#可以是主机名(将转发至后端服务做为主机头首部)、 IP 地址:端口的方式#也可以代理到预先设置的主机群组,需要模ngx_http_upstream_module支持# 示例 :location /web {index index.html;proxy_pass http://172.25.254.30:8080; #8080后面无uri, 即无 / 符号 ,#需要将location后面url 附加到proxy_pass指定的url 后面#此行为类似于root#proxy_pass指定的uri不带斜线将访问的/web#等于访问后端服务器proxy_pass http://172.25.254.40:8080/; #8080后面有uri,即有 / 符号#相当于置换,即访问/web时实际返回proxy_pass后面uri 内容#此行为类似于alias#proxy_pass指定的uri带斜线#等于访问后端服务器的#http://172.25.254.40:8080/index.html#内容返回给客户端} # http://nginx/web/index.html ==>http://1:8080# 重启 Nginx 测试访问效果:#curl -L http://www.timinglee.org/web# 如果 location 定义其 uri 时使用了正则表达式模式 ( 包括 ~,~*, 但不包括 ^~) ,则 proxy_pass 之后必须不能使用uri# 即不能有 / , 用户请求时传递的 uri 将直接附加至后端服务器之后server {...server_name HOSTNAME;location ~|~* /uri/ {proxy_pass http://host:port; #proxy_pass后面的url 不能加/}...}http://HOSTNAME/uri/ --> http://host/uri/
proxy_hide_header field; #用于nginx作为反向代理的时候#在返回给客户端http响应时#隐藏后端服务器相应头部的信息#可以设置在http,server或location块# 示例 : 隐藏后端服务器 ETag 首部字段location /web {index index.html;proxy_pass http://10.0.0.18:8080/;proxy_hide_header ETag;}
proxy_pass_header field; # 透传# 默认 nginx 在响应报文中不传递后端服务器的首部字段 Date, Server, X-Pad, X-Accel 等参数# 如果要传递的话则要使用 proxy_pass_header field 声明将后端服务器返回的值传递给客户端#field 首部字段大小不敏感# 示例 : 透传后端服务器的 Server 和 Date 首部给客户端 , 同时不再响应报中显示前端服务器的 Server 字段proxy_pass_header Server;proxy_pass_header Date;
proxy_pass_request_body on | off;# 是否向后端服务器发送 HTTP 实体部分 , 可以设置在 http,server 或 location 块,默认即为开启
proxy_pass_request_headers on | off;# 是否将客户端的请求头部转发给后端服务器,可以设置在 http,server 或 location 块,默认即为开启
proxy_set_header;# 可更改或添加客户端的请求头部信息内容并转发至后端服务器,比如在后端服务器想要获取客户端的真实 IP 的时候,就要更改每一个报文的头部# 示例 :location ~ /web {proxy_pass http://172.25.254.20:80;proxy_hide_header ETag;proxy_pass_header Server;proxy_pass_request_body on;proxy_pass_request_headers on;proxy_set_header X-Forwarded-For $remote_addr;}[root@apache20 ~]# vim /etc/httpd/conf/httpd.confLogFormat "\"%{X-Forwarded-For}i\" %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined访问后看后端服务器日志
proxy_connect_timeout time;# 配置 nginx 服务器与后端服务器尝试建立连接的超时时间,默认为 60 秒用法如下: proxy_connect_timeout 6s;#60s 为自定义 nginx 与后端服务器建立连接的超时时间 , 超时会返回客户端 504 响应码
proxy_read_timeout time;# 配置 nginx 服务器向后端服务器或服务器组发起 read 请求后,等待的超时时间,默认 60s
proxy_send_timeout time;# 配置 nginx 项后端服务器或服务器组发起 write 请求后,等待的超时 时间,默认 60s
proxy_http_version 1.0;# 用于设置 nginx 提供代理服务的 HTTP 协议的版本,默认 http 1.0
proxy_ignore_client_abort off;# 当客户端网络中断请求时, nginx 服务器中断其对后端服务器的请求。即如果此项设置为 on 开启,则服务器、会忽略客户端中断并一直等着代理服务执行返回,如果设置为off ,则客户端中断后 Nginx 也会中断客户端请求并立即记录499 日志,默认为 off 。
2.1.1.2 实战案例: 反向代理单台 web 服务器
[root@nginx ~]# cat /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name www.timinglee.org;location / {proxy_pass http://192.168.10.141;}}# 重启 Nginx 并访问测试
2.1.1.3 实战案例: 指定 location 实现反向代理
2.1.1.3.1 针对指定的 location
server {listen 80;server_name www.timinglee.org;location / {proxy_pass http://192.168.10.141;}location ~ /static {proxy_pass http://192.168.10.140:8080;}}# 后端 web 服务器必须要有相对于的访问 URL[root@nginx ~]## mkdir /var/www/html/static[root@nginx ~]## echo static 192.168.10.140 > /var/www/html/static/index.html[root@nginx2 ~]#]# echo 192.168.10.141 > /var/www/html/index.html# 重启 Nginx 并访问测试:[root@nginx ~]# curl www.timinglee.org/static/static 192.168.10.140[root@nginx ~]# curl www.timinglee.org192.168.10.141
2.1.1.3.2 针对特定的资源实现代理
[root@Nginx ~]# vim /usr/local/nginx/conf.d/vhosts.confserver {listen 80;server_name www.timinglee.org;location / {proxy_pass http://192.168.10.141;}location ~ \.(png|jpg|gif) {proxy_pass http://192.168.10.140:8080;}}
2.1.1.4 反向代理示例: 缓存功能
proxy_cache zone_name | off; 默认 off# 指明调用的缓存,或关闭缓存机制 ;Context:http, server, location#zone_name 表示缓存的名称 . 需要由 proxy_cache_path 事先定义
proxy_cache_key string;# 缓存中用于 “ 键 ” 的内容,默认值: proxy_cache_key $scheme$proxy_host$request_uri;
proxy_cache_valid [code ...] time;# 定义对特定响应码的响应内容的缓存时长,定义在 http{...} 中示例 :proxy_cache_valid 200 302 10m;proxy_cache_valid 404 1m;
proxy_cache_path;# 定义可用于 proxy 功能的缓存 ;Context:httpproxy_cache_path path [levels=levels] [use_temp_path=on|off]keys_zone=zone_name:size [inactive=time] [max_size=size] [manager_files=number][manager_sleep=time] [manager_threshold=time] [loader_files=number][loader_sleep=time] [loader_threshold=time] [purger=on|off][purger_files=number] [purger_sleep=time] [purger_threshold=time];# 示例:在 http 配置定义缓存信息proxy_cache_path /var/cache/nginx/proxy_cache # 定义缓存保存路径, proxy_cache 会自动创建levels=1:2:2 # 定义缓存目录结构层次#1:2:2 可以生成2^4x2^8x2^8=2^20=1048576 个目录keys_zone=proxycache:20m # 指内存中缓存的大小,主要用于存放 key 和 metadata(如:使用次数)# 一般 1M 可存放 8000 个左右的 keyinactive=120s # 缓存有效时间max_size=10g; # 最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值
# 调用缓存功能,需要定义在相应的配置段,如 server{...}; 或者 location 等proxy_cache proxycache;proxy_cache_key $request_uri; # 对指定的数据进行 MD5 的运算做为缓存的 keyproxy_cache_valid 200 302 301 10m; # 指定的状态码返回的数据缓存多长时间proxy_cache_valid any 1m; # 除指定的状态码返回的数据以外的缓存多长时间 , 必须设置 ,否则不会缓存proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 |http_502 | http_503 | http_504 | http_403 | http_404 | off ; # 默认是 off# 在被代理的后端服务器出现哪种情况下,可直接使用过期的缓存响应客户端# 示例proxy_cache_use_stale error http_502 http_503;proxy_cache_methods GET | HEAD | POST ...;# 对哪些客户端请求方法对应的响应进行缓存, GET 和 HEAD 方法总是被缓存
2.1.1.4.1 非缓存场景压测
# 准备后端 httpd 服务器[root@apache20 static]# pwd/var/www/html/static[root@apache20 static]# cat /var/log/messages > ./log.html # 准备测试页面[root@apache30 ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.htmlConcurrency Level: 100Time taken for tests: 23.238 secondsComplete requests: 1000Failed requests: 0Total transferred: 2011251000 bytesHTML transferred: 2010991000 bytesRequests per second: 43.03 [#/sec] (mean)Time per request: 2323.789 [ms] (mean)Time per request: 23.238 [ms] (mean, across all concurrent requests)Transfer rate: 84521.97 [Kbytes/sec] received
2.1.1.4.2 准备缓存配置
[root@Nginx ~]# vim /usr/local/nginx/conf/nginx.conf#gzip on;proxy_cache_path /apps/nginx/proxy_cache levels=1:2:2 keys_zone=proxycache:20minactive=120s max_size=1g; # 配置在 nginx.conf http 配置段[root@Nginx ~]# vim /usr/local/nginx/conf.d/vhost.conflocation ~ /static { # 要缓存的 URL 或者放在 server 配置项对所有 URL 都进行缓存proxy_pass http://172.25.254.20:8080;proxy_cache proxycache;proxy_cache_key $request_uri;proxy_cache_valid 200 302 301 10m;proxy_cache_valid any 1m; # 必须指定哪些响应码的缓存}#/data/nginx/proxycache/ 目录会自动生成[root@Nginx ~]# ll /apps/nginx/proxy_cache/ -ddrwx------ 3 nginx root 4096 8 月 20 20:07 /apps/nginx/proxy_cache/[root@Nginx ~]# tree /apps/nginx/proxy_cache//data/nginx/proxycache/0 directories, 0 files
2.1.1.4.3 访问并验证缓存文件
# 访问 web 并验证缓存目录[root@nginx ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.html[root@nginx ~]# ab -n 2000 -c200 http://www.magedu.org/static/log.htmlConcurrency Level: 100Time taken for tests: 10.535 secondsComplete requests: 1000Failed requests: 0Total transferred: 2011251000 bytesHTML transferred: 2010991000 bytesRequests per second: 94.92 [#/sec] (mean)Time per request: 1053.507 [ms] (mean)Time per request: 10.535 [ms] (mean, across all concurrent requests)Transfer rate: 186435.60 [Kbytes/sec] received# 验证缓存目录结构及文件大小[root@Nginx ~]# tree /apps/nginx/proxy_cache//apps/nginx/proxy_cache/└── e└── 50└── 99└── 319432ef3663735a9d3cb4e0c1d9950e3 directories, 0 files
2.1.2 http 反向代理负载均衡
2.1.2.1 http upstream配置参数
# 自定义一组服务器,配置在 http 块内upstream name {server ...........}# 示例upstream backend {server backend1.example.com weight=5;server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;server unix:/tmp/backend3;server backup1.example.com backup;}server address [parameters];# 配置一个后端 web 服务器,配置在 upstream 内,至少要有一个 server 服务器配置。
#server 支持的 parameters 如下:weight=number # 设置权重,默认为 1, 实现类似于 LVS 中的 WRR,WLC 等max_conns=number # 给当前后端 server 设置最大活动链接数,默认为 0 表示没有限制max_fails=number # 后端服务器的下线条件 , 当客户端访问时 , 对本次调度选中的后端服务器连续进行检测多少次 , 如果都失败就标记为不可用 , 默认为 1 次 , 当客户端访问时 , 才会利用 TCP 触发对探测后端服务器健康性检查 , 而非周期性的探测fail_timeout=time # 后端服务器的上线条件 , 对已经检测到处于不可用的后端服务器 , 每隔此时间间隔再次进行检测是否恢复可用,如果发现可用 , 则将后端服务器参与调度 , 默认为 10 秒backup # 设置为备份服务器,当所有后端服务器不可用时 , 才会启用此备用服务器down # 标记为 down 状态 , 可以平滑下线后端服务器resolve # 当 server 定义的是主机名的时候,当 A 记录发生变化会自动应用新 IP 而不用重启Nginx
hash KEY [consistent];# 基于指定请求报文中首部字段或者 URI 等 key 做 hash 计算,使用 consistent 参数,将使用 ketama 一致性hash 算法,适用于后端是 Cache 服务器(如 varnish )时使用, consistent 定义使用一致性 hash 运算,一致性 hash 基于取模运算hash $request_uri consistent; # 基于用户请求的 uri 做 hash
hash $cookie_sessionid # 基于 cookie 中的 sessionid 这个 key 进行 hash 调度 , 实现会话绑定
ip_hash;# 源地址 hash 调度方法,基于的客户端的 remote_addr( 源地址 IPv4 的前 24 位或整个 IPv6 地址 ) 做 hash 计算,以实现会话保持
least_conn;# 最少连接调度算法,优先将客户端请求调度到当前连接最少的后端服务器 , 相当于 LVS 中的 WLC
2.1.2.2 反向代理示例: 后端多台 web服务器
192.168.10.10 #Nginx 代理服务器192.168.10.20 #后端 web A , Apache 部署192.168.10.30 #后端 web B , Apache 部署
[root@apache20 ~]# yum install httpd -y[root@apache20 ~]# echo "web1 192.168.10.20" > /var/www/html/index.html[root@apache20 ~]# systemctl enable --now httpd[root@apache30 ~]# yum install httpd -y[root@apache30 ~]# echo "web2 192.168.10.30" >> /var/www/html/index.html[root@apache30 ~]# systemctl enable --now httpd# 访问测试[root@centos8 ~]# curl http://192.168.10.20web1 192.168.10.20[root@centos8 ~]# curl http://192.168.10.30web2 192.168.10.30
[root@nginx ~]# cat /usr/local/nginx/conf/conf.d/pc.confupstream webserver {#ip_hash;#hash $request_uri consistent;#hash $cookie_lee#least_conn;server 192.168.10.20:8080 weight=1 fail_timeout=15s max_fails=3;server 192.168.10.30:80 weight=1 fail_timeout=15s max_fails=3;server 192.168.10.10:80 backup;}server {listen 80;server_name www.timinglee.org;location ~ / {proxy_pass http://webserver;}}# 重启 Nginx 并访问测试[root@nginx ~]# curl www.timinglee.org
web1 192.168.10.20[root@nginx ~]# curl www.timinglee.orgweb2 192.168.10.30#关闭192.168.10.20和192.168.10.30,测试 nginx backup 服务器可用性:[root@nginx ~]# while true; do curl http://www.timinglee.org; sleep 1; done
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.confhttp {upstream websrvs {hash $cookie_hello; #hello是cookie 的 key 的名称server 10.0.0.101:80 weight=2;server 10.0.0.102:80 weight-1;}}[root@nginx ~]# vim /usr/local/nginx/conf/conf.d/pc.confupstream webserver {#ip_hash;#hash $request_uri consistent;hash $cookie_lee;#least_conn;server 192.168.10.20:8080 weight=1 fail_timeout=15s max_fails=3;server 192.168.10.30:80 weight=1 fail_timeout=15s max_fails=3;#server 192.168.10.10:80 backup;}server {listen 80;server_name www.timinglee.org;location ~ / {proxy_pass http://webserver;}}# 测试[root@nginx ~]# curl -b lee=1 www.timinglee.org
2.2 实现 Nginx 四层负载均衡
2.2.1 tcp负载均衡配置参数
stream { # 定义 stream 相关的服务;Context:mainupstream backend { # 定义后端服务器hash $remote_addr consistent; # 定义调度算法server backend1.example.com:12345 weight=5; # 定义具体 serverserver 127.0.0.1:12345 max_fails=3 fail_timeout=30s;server unix:/tmp/backend3;}upstream dns { # 定义后端服务器server 10.0.0.1:53; # 定义具体 serverserver dns.example.com:53;}server { # 定义 serverlisten 12345; # 监听 IP:PORTproxy_connect_timeout 1s; # 连接超时时间proxy_timeout 3s; # 转发超时时间proxy_pass backend; # 转发到具体服务器组}server {listen 127.0.0.1:53 udp reuseport;proxy_timeout 20s;proxy_pass dns;}server {listen [::1]:12345;proxy_pass unix:/tmp/stream.socket;}}
2.2.2 负载均衡实例: MySQL
后端服务器安装 MySQL
# 在 apache20 中安装 mysql[root@apache20 ~]# yum install mariadb-server -y[root@apache20 ~]# vim /etc/my.cnf.d/mariadb-server.cnf[mysqld]server-id=20[root@apache20 ~]# systemctl start mariadb[root@apache20 ~]# mysql -e "grant all on *.* to lee@'%' identified by 'lee';"[root@apache30 ~]# mysql -ulee -plee -h192.168.10.20 -e "select @@server_id"+-------------+| @@server_id |+-------------+| 20 |+-------------+# 在 apache30 重复以上步骤并在 apache20 上测试
[root@Nginx ~]# vim /apps/nginx/conf/tcp/tcp.confstream {upstream mysql_server {server 192.168.10.20:3306 max_fails=3 fail_timeout=30s;server 192.168.10.30:3306 max_fails=3 fail_timeout=30s;}server {listen192.168.10.10:3306;proxy_pass mysql_server;proxy_connect_timeout 30s;proxy_timeout 300s;}}# 重启 nginx 并访问测试:[root@Nginx ~]# nginx -s reload# 测试通过 nginx 负载连接 MySQL :[root@apache30 ~]# mysql -ulee -plee -h192.168.10.10 -e "select @@server_id"+-------------+| @@server_id |+-------------+| 20 |+-------------+[root@apache30 ~]# mysql -ulee -plee -h192.168.10.10 -e "select @@server_id"+-------------+| @@server_id |+-------------+| 30 |+-------------+
# 在 10.0.0.28 停止 MySQL 服务[root@apache20 ~]# systemctl stop mariadb# 再次测试访问 , 只会看到 mysql-server1.timinglee.org 进行响应[root@apache30 ~]# mysql -ulee -plee -h192.168.10.10 -e "select @@server_id"+-------------+| @@server_id |+-------------+| 30 |+-------------+[root@apache30 ~]# mysql -ulee -plee -h192.168.10.10 -e "select @@server_id"+-------------+| @@server_id |+-------------+| 30 |+-------------+
2.2.3 udp 负载均衡实例: DNS
stream {upstream dns_server{server 192.168.10.20:53 max_fails=3 fail_timeout=30s;server 192.168.10.30:53 max_fails=3 fail_timeout=30s;}server {listen 192.168.10.10:53 udp;proxy_pass dns_server;proxy_timeout 1s;proxy_responses 1; # 使用 UDP 协议时,设置代理服务器响应客户端期望的数据报文数# 该值作为会话的终止条件error_log logs/dns.log;}}测试:[root@apache30 named]# dig www.timinglee.org @192.168.10.10; <<>> DiG 9.16.23 <<>> www.timinglee.org @192.168.10.10;; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33888;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 1232; COOKIE: 701447f1bdd8acea0100000066a27b465426b2b4bc7f1dc3 (good);; QUESTION SECTION:;www.timinglee.org. IN A;; ANSWER SECTION:
www.timinglee.org. 86400 IN A 192.168.10.20;; Query time: 2 msec;; SERVER: 192.168.10.10#53(192.168.10.10);; WHEN: Fri Jul 26 00:20:22 CST 2024;; MSG SIZE rcvd: 90[root@apache30 named]# dig www.timinglee.org @192.168.10.10; <<>> DiG 9.16.23 <<>> www.timinglee.org @192.168.10.10;; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8932;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 1232; COOKIE: 8ecb61bbfe2716df0100000066a27b47a3bb0c3d8e537858 (good);; QUESTION SECTION:;www.timinglee.org. IN A;; ANSWER SECTION:www.timinglee.org. 86400 IN A 172.25.254.30;; Query time: 1 msec;; SERVER: 172.25.254.10#53(172.25.254.10);; WHEN: Fri Jul 26 00:20:23 CST 2024;; MSG SIZE rcvd: 90
2.3 实现 FastCGI
CGI 的由来:最早的 Web 服务器只能简单地响应浏览器发来的 HTTP 请求,并将存储在服务器上的 HTML 文件返回给浏览器,也就是静态 html 文件,但是后期随着网站功能增多网站开发也越来越复杂,以至于出现动态技术,比如像 php(1995 年 ) 、 java(1995) 、 python(1991) 语言开发的网站,但是 nginx/apache 服务器并不能直接运行 php 、 java 这样的文件, apache 实现的方式是打补丁,但是 nginx 缺通过与第三方基于协议实现,即通过某种特定协议将客户端请求转发给第三方服务处理,第三方服务器会新建新的进程处理用户的请求,处理完成后返回数据给 Nginx 并回收进程,最后 nginx 在返回给客户端,那这个约定就是通用网关接口 (common gateway interface ,简称 CGI) , CGI (协议) 是 web 服务器和外部应用程序之间的接口标准,是 cgi 程序和 web 服务器之间传递信息的标准化接口。
为什么会有 FastCGI ?CGI 协议虽然解决了语言解析器和 Web Server 之间通讯的问题,但是它的效率很低,因为 Web Server每收到一个请求都会创建一个 CGI 进程, PHP 解析器都会解析 php.ini 文件,初始化环境,请求结束的时候再关闭进程,对于每一个创建的 CGI 进程都会执行这些操作,所以效率很低,而 FastCGI 是用来提高 CGI 性能的, FastCGI 每次处理完请求之后不会关闭掉进程,而是保留这个进程,使这个进程可以处理多个请求。这样的话每个请求都不用再重新创建一个进程了,大大提升了处理效率。什么是 PHP-FPM ?PHP-FPM(FastCGI Process Manager :
- FastCGI进程管理器)是一个实现了Fastcgi的程序,并且提供进程管理的功能。
- 进程包括master进程和worker进程。master进程只有一个,负责监听端口,接受来自web server的请求
- worker进程一般会有多个,每个进程中会嵌入一个PHP解析器,进行PHP代码的处理。
2.3.1 FastCGI配置指令
fastcgi_pass address:port;# 转发请求到后端服务器, address 为后端的 fastcgi server 的地址,可用位置: location, if inlocationfastcgi_index name;#fastcgi 默认的主页资源,示例: fastcgi_index index.php;fastcgi_param parameter value [if_not_empty];# 设置传递给 FastCGI 服务器的参数值,可以是文本,变量或组合,可用于将 Nginx 的内置变量赋值给自定义keyfastcgi_param REMOTE_ADDR $remote_addr; # 客户端源 IPfastcgi_param REMOTE_PORT $remote_port; # 客户端源端口fastcgi_param SERVER_ADDR $server_addr; # 请求的服务器 IP 地址fastcgi_param SERVER_PORT $server_port; # 请求的服务器端口fastcgi_param SERVER_NAME $server_name; # 请求的 server nameNginx 默认配置示例:location ~ \.php$ {root /scripts;fastcgi_pass 127.0.0.1:9000;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # 默认脚本路径#fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params; # 此文件默认系统已提供 , 存放的相对路径为prefix/conf}
2.3.2 FastCGI实战案例 : Nginx与php-fpm在同一服务器
# 利用 yum 解决 php 依赖[root@Nginx ~]# yum install -y bzip2 systemd-devel libxml2-devel sqlite-devellibpng-devel libcurl-devel oniguruma-devel# 解压源码并安装[root@Nginx ~]# ./configure \--prefix=/usr/local/php \ # 安装路径--with-config-file-path=/usr/local/php/etc \ # 指定配置路径--enable-fpm \ # 用 cgi 方式启动程序--with-fpm-user=nginx \ # 指定运行用户身份--with-fpm-group=nginx \--with-curl \ # 打开 curl 浏览器支持--with-iconv \ # 启用 iconv 函数,转换字符编码--with-mhash \ #mhash 加密方式扩展库--with-zlib \ # 支持 zlib 库,用于压缩 http 压缩传输--with-openssl \ # 支持 ssl 加密--enable-mysqlnd \ #mysql 数据库--with-mysqli \ --with-pdo-mysql \--disable-debug \ # 关闭 debug 功能--enable-sockets \ # 支持套接字访问--enable-soap \ # 支持 soap 扩展协议--enable-xml \ # 支持 xml--enable-ftp \ # 支持 ftp--enable-gd \ # 支持 gd 库--enable-exif \ # 支持图片元数据--enable-mbstring \ # 支持多字节字符串--enable-bcmath \ # 打开图片大小调整 , 用到 zabbix 监控的时候用到了这个模块--with-fpm-systemd # 支持 systemctl 管理 cgi
[root@Nginx etc]# cp php-fpm.conf.default php-fpm.conf[root@Nginx etc]# vim php-fpm.conf去掉注释pid = run/php-fpm.pid # 指定 pid 文件存放位置[root@Nginx etc]# cd php-fpm.d/[root@Nginx php-fpm.d]# cp www.conf.default www.conf# 生成主配置文件[root@Nginx php-fpm.d]# cd /root/php-8.3.9/[root@Nginx php-8.3.9]# cp php.ini-production /usr/local/php/etc/php.ini[root@Nginx ~]# vim /usr/local/php/etc/php.ini[Date]; Defines the default timezone used by the date functions; https://php.net/date.timezonedate.timezone = Asia/Shanghai # 修改时区# 生成启动文件[root@Nginx ~]# cd /root/php-8.3.9/[root@Nginx php-8.3.9]# cp sapi/fpm/php-fpm.service /lib/systemd/system/# Mounts the /usr, /boot, and /etc directories read-only for processes invoked bythis unit.#ProtectSystem=full # 注释该内容[root@Nginx php-8.3.9]# systemctl start php-fpm.service[root@Nginx php-8.3.9]# netstat -antlupe | grep phptcp 0 0 192.0.0.1:9000 0.0.0.0:* LISTEN 0820758 176202/php-fpm: mas
[root@Nginx ~]# mkdir /data/php -p[root@centos8 ~]# cat /data/php/index.php #php 测试页面<?phpphpinfo();?>
[root@Nginx ~]# vim /apps/nginx/conf.d/php.confserver {listen 80;server_name php.timinglee.org;root /data/php;location ~ \.php$ {fastcgi_pass 192.0.0.1:9000;fastcgi_index index.php;include fastcgi.conf;}}# 重启 Nginx 并访问 web 测试[root@Nginx ~]# nginx -s reload
[root@Nginx ~]# vim .bash_profile# .bash_profile# Get the aliases and functionsif [ -f ~/.bashrc ]; then. ~/.bashrcfi# User specific environment and startup programsPATH=$PATH:$HOME/bin:/apps/nginx/sbin:/usr/local/php/binexport PATH[root@Nginx ~]# source .bash_profile
2.3.3 php的动态扩展模块(php的缓存模块)
[root@Nginx ~]# tar zxf memcache-8.2.tgz[root@Nginx ~]# cd memcache-8.2/[root@Nginx memcache-8.2]# yum install autoconf[root@Nginx memcache-8.2]# phpize[root@Nginx memcache-8.2]# ./configure && make && make installInstalling shared extensions: /usr/local/php/lib/php/extensions/no-debug-nonzts-20230831/[root@Nginx memcache-8.2]# ls /usr/local/php/lib/php/extensions/no-debug-non-zts-20230831/memcache.so opcache.so
[root@Nginx ~]# cd memcache-8.2/[root@Nginx memcache-8.2]# lsautom4te.cache config.log configure.ac example.php Makefile.fragmentsREADMEbuild config.m4 config.w32 include Makefile.objects runtests.phpconfig9.m4 config.nice CREDITS libtool memcache.la srcconfig.h config.status docker LICENSE memcache.phptestsconfig.h.in configure Dockerfile Makefile modules[root@Nginx memcache-8.2]# cp example.php memcache.php /data/php/[root@Nginx ~]# vim /data/php/memcache.phpdefine('ADMIN_USERNAME','admin'); // Admin Usernamedefine('ADMIN_PASSWORD','lee'); // Admin Passworddefine('DATE_FORMAT','Y/m/d H:i:s');define('GRAPH_SIZE',200);define('MAX_ITEM_DUMP',50);$MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array#$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
[root@Nginx ~]# vim /usr/local/php/etc/php.ini;extension=zipextension=memcache;zend_extension=opcache[root@Nginx ~]# systemctl reload php-fpm[root@Nginx no-debug-non-zts-20230831]# php -m | grep memmemcache
[root@Nginx ~]# yum install memcached -y[root@Nginx ~]# systemctl enable --now memcached.service[root@Nginx ~]# netstat -antlupe | grep memcachetcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN976 1037243 186762/memcached[root@Nginx ~]# cat /etc/sysconfig/memcachedPORT="11211"USER="memcached"MAXCONN="1024"CACHESIZE="64"OPTIONS="-l 127.0.0.1,::1"
访问 http://php.timinglee.org/example.php 不断刷新访问 http://php.timinglee.org/memcache.php 查看命中效果
[root@apache20 ~]# ab -n500 -c10 http://php.timinglee.org/index.phpConcurrency Level: 10Time taken for tests: 0.514 secondsComplete requests: 500Failed requests: 44(Connect: 0, Receive: 0, Length: 44, Exceptions: 0)[root@apache20 ~]# ab -n500 -c10 http://php.timinglee.org/example.phpConcurrency Level: 10Time taken for tests: 0.452 secondsComplete requests: 500Failed requests: 0
2.3.4 php高速缓存
[root@Nginx ~]# rm -fr /apps/nginx/[root@Nginx ~]# tar zxf srcache-nginx-module-0.33.tar.gz[root@Nginx ~]# tar zxf memc-nginx-module-0.20.tar.gz[root@Nginx ~]# cd nginx-1.26.1/[root@Nginx nginx-1.26.1]# ./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --withhttp_realip_module --with-http_stub_status_module --with-http_gzip_static_module--with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --add-module=/root/memc-nginx-module-0.20 --add-module=/root/srcache-nginx-module-0.33[root@Nginx nginx-1.26.1]# make && make install[root@Nginx ~]# vim /apps/nginx/conf.d/php.confupstream memcache {server 127.0.0.1:11211;keepalive 512;}server {listen 80;server_name php.timinglee.org;root /data/php;location /memc {internal;memc_connect_timeout 100ms;memc_send_timeout 100ms;memc_read_timeout 100ms;set $memc_key $query_string; # 使用内置变量 $query_string 来作为 keyset $memc_exptime 300; # 缓存失效时间 300 秒memc_pass memcache;}location ~ \.php$ {set $key $uri$args; # 设定 key 的值srcache_fetch GET /memc $key; # 检测 mem 中是否有要访问的 phpsrcache_store PUT /memc $key; # 缓存为加载的 php 数据fastcgi_pass 192.0.0.1:9000;fastcgi_index index.php;include fastcgi.conf;}}[root@Nginx ~]# systemctl start nginx.service
[root@apache20 ~]# ab -n500 -c10 http://php.timinglee.org/index.phpConcurrency Level: 10Time taken for tests: 0.255 secondsComplete requests: 500Failed requests: 0
3.1 openresty
Nginx 是俄罗斯人发明的, Lua 是巴西几个教授发明的,中国人章亦春把 LuaJIT VM 嵌入到 Nginx 中,实现了 OpenResty 这个高性能服务端解决方案OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、 Web 服务和动态网关。OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx有效地变成一个强大的通用 Web 应用平台。这样, Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。OpenResty 由于有功能强大且方便的的 API, 可扩展性更强 , 如果需要实现定制功能 ,OpenResty 是个不错的选择官网 : http://openresty.org/cn/
[root@Nginx ~]#dnf -y install gcc pcre-devel openssl-devel perl[root@Nginx ~]#useradd -r -s /sbin/nologin nginx[root@Nginx ~]#cd /usr/local/src[root@Nginx src]#wget https://openresty.org/download/openresty-1.17.8.2.tar.gz[root@Nginx src]#tar xf openresty-1.17.8.2.tar.gz[root@Nginx src]#cd openresty-1.17.8.2/[root@Nginx openresty-1.17.8.2]#./configure \--prefix=/apps/openresty \--user=nginx --group=nginx \--with-http_ssl_module \--with-http_v2_module \--with_http_realip_module \--with-http_stub_status_module \--with-http_gzip_static_module--with-pcre --with-stream \--with-stream_ssl_module \--with-stream_realip_module[root@Nginx openresty-1.17.8.2]#make && make install[root@Nginx openresty-1.17.8.2]#ln -s /apps/openresty/bin/* /usr/bin/[root@Nginx openresty-1.17.8.2]#openresty -vnginx version: openresty/1.17.8.2[root@Nginx openresty-1.17.8.2]#openresty[root@Nginx openresty-1.17.8.2]#ps -ef |grep nginx[root@Nginx ~]#curl 10.0.0.18