作为代理服务器
前言
Nginx可以通过多种方式实现反向代理,包括但不限于以下方式:
- HTTP/HTTPS反向代理:Nginx可以将客户端请求转发到后端HTTP/HTTPS服务器,实现反向代理功能。在HTTP/HTTPS反向代理中,Nginx可以代理各种HTTP请求类型,如GET、POST等。
- TCP/UDP反向代理:Nginx还可以代理TCP/UDP连接,例如代理SMTP、DNS等服务。
- IMAP4/POP3反向代理:Nginx还支持代理IMAP4和POP3邮件服务。
- FastCGI/UWSGI/SCGI等协议反向代理动态请求:Nginx可以通过FastCGI、UWSGI、SCGI等协议代理动态请求,例如PHP、Python等脚本语言的动态请求。这种方式比较常用于Web应用程序的反向代理。
总之,Nginx作为一款高性能的Web服务器和反向代理服务器,支持多种协议和应用场景,可灵活应对各种需求。
反向代理 和 负载均衡
反向代理和负载均衡是两个不同的概念,虽然它们有时候在实践中会结合使用。
- 反向代理是一种代理服务器,它接收客户端请求并将请求转发给后端服务器,同时将后端服务器的响应返回给客户端,从而实现隐藏后端服务器的IP地址和提供更好的安全性等功能。
- 负载均衡是一种分布式系统中的策略,它通过将负载分散到多个计算机节点上,以提高系统的性能、可扩展性和可用性。负载均衡可以实现在多台服务器之间分发请求,以达到平衡服务器负载的目的。
在实践中,我们通常会将反向代理和负载均衡结合使用,将客户端请求转发到多个后端服务器上,从而实现高性能和高可用性的Web应用程序。
反向代理和负载均衡的区别
反向代理和负载均衡是两个不同的概念,虽然它们在实践中有时候会结合使用。它们的主要区别如下:
- 功能不同:反向代理是一种代理服务器,它接收客户端请求并将请求转发给后端服务器,同时将后端服务器的响应返回给客户端,从而实现隐藏后端服务器的IP地址和提供更好的安全性等功能。而负载均衡是一种分布式系统中的策略,它通过将负载分散到多个计算机节点上,以提高系统的性能、可扩展性和可用性。
- 目的不同:反向代理的主要目的是提高Web应用程序的安全性和性能,通过隐藏后端服务器的IP地址和实现静态资源的缓存等功能,减轻后端服务器的负载,从而提高应用程序的可用性和可靠性。而负载均衡的主要目的是实现服务器集群的负载平衡,将客户端请求分配到多个后端服务器上,从而平衡服务器的负载,提高整个系统的性能和可用性。
- 实现方式不同:反向代理是通过在代理服务器和后端服务器之间建立连接,并在代理服务器上配置反向代理规则,来实现客户端请求的转发和响应的返回。而负载均衡是通过在负载均衡器上配置负载均衡策略,并在负载均衡器和后端服务器之间建立连接,来实现客户端请求的分发和后端服务器的负载平衡。
虽然反向代理和负载均衡是两个不同的概念,但在实践中通常会结合使用,以实现高性能和高可用性的Web应用程序。
同构代理 和 异构代理
同构代理指的是代理服务器和后端服务器使用相同的协议通信
- 比如Nginx作为代理服务器,反向代理到Apache或Tomcat服务器,它们都使用HTTP协议进行通信。
异构代理则指代理服务器和后端服务器使用不同的协议通信
- 比如Nginx作为代理服务器,反向代理到MySQL或Redis服务器,MySQL或Redis使用TCP协议进行通信,而Nginx使用HTTP协议进行通信。
- 需要注意的是,虽然代理服务器和后端服务器的协议不同,但代理服务器可以使用一些转换协议的技术来进行通信,比如使用FastCGI、uWSGI、SCGI等协议将请求转发到后端服务器。这样可以在代理服务器和后端服务器之间进行协议转换,从而实现异构代理的功能。
Nginx 代理相关模块说明
ngx_http_upstream_module
- 用于定义为proxy_pass、fastcgi_pass、uwsgi_pass等指令引用的后端服务器分组
- 这个模块允许 Nginx 与上游服务器进行通信,以便在 Nginx 与客户端之间进行负载均衡和高可用性方案。该模块可以配置多种负载均衡算法,如轮询、IP哈希、最少连接数等。
- https://nginx.org/en/docs/http/ngx_http_upstream_module.html
ngx_http_proxy_module
- 将客户端的请求以http协议转发至指定服务器进行处理,缓存实现
- 该模块提供了将请求代理转发到另一个服务器的功能,通常用于反向代理和缓存。可以配置请求头、响应头和缓存设置等。
- https://nginx.org/en/docs/http/ngx_http_proxy_module.html
ngx_stream_proxy_module
- 将客户端的请求以tcp协议转发至指定服务器处理
- 该模块提供了将TCP或UDP数据流代理转发到另一个服务器的功能,常用于代理转发数据库、消息队列等应用。
- https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html
ngx_http_fastcgi_module
- 将客户端对php的请求以fastcgi协议转发至指定服务器处理
- 这个模块允许 Nginx 与 FastCGI 应用程序进行通信,通常用于运行 PHP 等脚本语言。可以配置请求参数、响应参数等。
- https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html
ngx_http_uwsgi_module
- 将客户端对python的请求以uwsgi协议转发至指定服务器处理
- 该模块允许 Nginx 与 uWSGI 应用程序进行通信,通常用于 Python 等脚本语言。可以配置请求参数、响应参数等。
- https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html
ngx_http_headers_module
- 添加头部报文信息,实现对后端服务器响应给客户端的报文中添加指定的响应首部字段
- 该模块提供了添加、修改、删除请求和响应头的功能。可以用于设置缓存控制、安全标头等。
- https://nginx.org/en/docs/http/ngx_http_headers_module.html
http_proxy 模块常用参数说明
- https://nginx.org/en/docs/http/ngx_http_proxy_module.html
- nginx默认会隐藏后端服务器的server、Date、X-Pad、X-Accel-… 等字段传递给客户端
proxy_pass
- 设置将客户端请求转发给的后端服务器主机
Syntax: proxy_pass URL;
Default: —
Context: location, if in location, limit_except- URL 可以是:
- 主机名(将转发至后端服务器作为主机头首部)
- IP地址:端口
- 预先设置的转发主机群组,需要 ngx_stream_proxy_module 模块支持
范例
有 / 符号 和 无 / 符号的区别
- 无 / ,类似root
server {
...
server_name HOSTNAME;
location /web {
proxy_pass http://10.0.0.18:8080; #8080后无 /
# 表示location后面的url附加到8080后
# 最终访问的是后端服务器的:http://10.0.0.18:8080/web/index.html
# 后端服务器需要有web目录才能访问,否则将将报404未找到
}
...
}
#访问结果简述:
# http://HOSTNAME/web/ --> http://10.0.0.18:8080/web/index.html - 有 / ,类似alias
server {
...
server_name HOSTNAME;
...
location /web {
proxy_pass http://10.0.0.18:8080/; #8080后有 /
# 表示访问代理服务器的 /web 相当于直接访问: http://10.0.0.18:8080/index.html
}
...
}
# 访问结果简述:
# http://HOSTNAME/web/ --> http://10.0.0.18:8080其他注意事项
- 如果 location 定义其 uri 时使用了正则表达式模式(包括~、
*,但不包括^),则 proxy_pass 之后必须不能使用 uri,用户请求时传递的 uri 将直接附加至后端服务器之后
server {
...
server_name HOSTNAME;
...
location ~|~* /uri {
index index.html;
proxy_pass http://host:port; # proxy_pass后面的url不能加/,否则会报语法错误
...
}
# 访问结果简述:
# http://HOSTNAME/uri/ --> http://host:port/uri是的,如果在 location 块中使用正则表达式模式(包括 ~、~*),那么在使用 proxy_pass 指令时,应该避免在 URI 中指定具体的值,否则 Nginx 将会把请求传递到一个错误的后端服务器或者返回 404 错误。
在这种情况下,proxy_pass 后面应该指定一个代理服务器的基本地址,并且不指定 URI,例如:
location ~* ^/api/(.*)$ {
proxy_pass http://backend-server;
}在上面的示例中,Nginx 不会使用具体的 URI,而是将请求传递到 http://backend-server 代理服务器上,并将 URI 附加到代理请求中。
需要注意的是,如果在使用正则表达式模式时,URI 中包含了变量,那么 Nginx 会自动将 URI 附加到代理请求中,例如:
location ~* ^/api/(.*)$ {
set $api_endpoint http://backend-server/$1;
proxy_pass $api_endpoint;
}在上面的示例中,URI 包含了变量 $1,那么 Nginx 会自动将 URI 附加到代理请求中,因此 proxy_pass 指令不需要指定具体的 URI。
proxy_hide_header
- 返回给客户端http响应时,指定隐藏后端服务器的响应头部信息;
- 默认情况下,nginx 不会将代理服务器的响应中的标头字段 Date、Server、X-Pad、X-Accel-… 等传递给客户端
Syntax: proxy_hide_header field;
Default: —
Context: http, server, location范例
# 设置前
# curl -I www.xiangzheng.com/web/
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 16 Jan 2022 20:39:16 GMT
Content-Type: text/html
Content-Length: 20
Connection: keep-alive
Last-Modified: Sun, 16 Jan 2022 20:23:14 GMT
ETag: "61e47eb2-14"
Accept-Ranges: bytes
# 设置
# vim /apps/nginx/conf.d/pc.conf
server {
listen 80;
server_name www.xiangzheng.com;
location /web {
index index.html;
proxy_pass http://10.0.0.18:80/;
proxy_hide_header ETag; # 添加此行,隐藏ETag
#proxy_hide_header X-Powered-By; #隐藏php的首部字段
}
}
# 测试,ETag被隐藏了
# curl -I www.xiangzheng.com/web/
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 16 Jan 2022 20:45:36 GMT
Content-Type: text/html
Content-Length: 20
Connection: keep-alive
Last-Modified: Sun, 16 Jan 2022 20:23:14 GMT
Accept-Ranges: bytesproxy_pass_header
- 返回给客户端http响应时,指定显示后端服务器的响应头部信息
- 默认情况下,nginx 不会将代理服务器的响应中的标头字段 Date、Server、X-Pad、X-Accel-… 等传递给客户端
Syntax: proxy_pass_header field;
Default: —
Context: http, server, location范例
# 配置前
#curl -I www.xiangzheng.com/web/
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 16 Jan 2022 21:11:24 GMT
Content-Type: text/html
Content-Length: 20
Connection: keep-alive
Last-Modified: Sun, 16 Jan 2022 20:23:14 GMT
ETag: "61e47eb2-14"
Accept-Ranges: bytes
# 配置
# vim /apps/nginx/conf.d/pc.conf
server {
listen 80;
server_name www.xiangzheng.com;
location /web {
index index.html;
proxy_pass http://10.0.0.18:80/;
proxy_pass_header server; # 显示后端服务器的server头部信息,隐藏的字段不区分大小写
}
}
# 测试
#curl -I www.xiangzheng.com/web/
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Connection: keep-alive
Server: nginx/1.14.1 # 只显示后端服务器的server版本了,不会再显示前端服务器的server字段
Date: Sun, 16 Jan 2022 21:14:04 GMT
Last-Modified: Sun, 16 Jan 2022 20:23:14 GMT
ETag: "61e47eb2-14"
Accept-Ranges: bytesproxy_pass_request_body
-
是否向后端服务器发送HTTP实体部分,默认开启
-
不开启则后端服务器将无法收到HTTP实体,进而无法进行响应
Syntax: proxy_pass_request_body on | off;
Default: proxy_pass_request_body on;
Context: http, server, location范例
proxy_pass_request_body 指令用于控制是否将客户端请求体(request body)一起转发给代理服务器。当该指令设置为 on 时,Nginx 会将客户端请求体一并转发给代理服务器,当设置为 off 时,Nginx 不会转发客户端请求体。
下面是一个示例,展示了如何使用 proxy_pass_request_body 指令:
location /api {
proxy_pass http://backend-server;
proxy_pass_request_body on;
}在上面的示例中,proxy_pass_request_body 指令被设置为 on,因此 Nginx 会将客户端请求体一并转发给代理服务器。这通常用于 POST 请求中传递的表单数据、JSON 数据等情况。
如果你不想将客户端请求体转发给代理服务器,可以将 proxy_pass_request_body 指令设置为 off,例如:
location /api {
proxy_pass http://backend-server;
proxy_pass_request_body off;
}在上面的示例中,proxy_pass_request_body 指令被设置为 off,因此 Nginx 不会将客户端请求体转发给代理服务器。这通常用于仅仅需要将请求路由到代理服务器,而不需要传递具体的请求体的情况。
proxy_pass_request_headers
-
是否将客户端的请求头部转发给后端服务器,默认开启
-
不开启则后端服务器无法识别请求头部信息,进而无法进行响应
Syntax: proxy_pass_request_headers on | off;
Default: proxy_pass_request_headers on;
Context: http, server, location范例
proxy_pass_request_headers 指令用于控制是否将客户端请求头(request headers)一并转发给代理服务器。当该指令设置为 on 时,Nginx 会将客户端请求头一并转发给代理服务器,当设置为 off 时,Nginx 不会转发客户端请求头。
下面是一个示例,展示了如何使用 proxy_pass_request_headers 指令:
location /api {
proxy_pass http://backend-server;
proxy_pass_request_headers on;
}在上面的示例中,proxy_pass_request_headers 指令被设置为 on,因此 Nginx 会将客户端请求头一并转发给代理服务器。这通常用于需要将客户端请求头传递给代理服务器的情况。
如果你不想将客户端请求头转发给代理服务器,可以将 proxy_pass_request_headers 指令设置为 off,例如:
location /api {
proxy_pass http://backend-server;
proxy_pass_request_headers off;
}在上面的示例中,proxy_pass_request_headers 指令被设置为 off,因此 Nginx 不会将客户端请求头转发给代理服务器。这通常用于仅仅需要将请求路由到代理服务器,而不需要传递具体的请求头的情况。
proxy_set_header
-
更改或添加客户端的请求头部信息内容并转发至后端服务器
-
例如:后端服务器想获得客户端的真实IP,则需要更改每一个报文的头部
Syntax: proxy_set_header field value;
Default: proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context: http, server, location相关嵌入式变量:
$proxy_host: 当 Nginx 作为代理服务器时,代理请求的目标主机地址。$proxy_port: 当 Nginx 作为代理服务器时,代理请求的目标主机端口号。$proxy_add_x_forwarded_for: 当 Nginx 作为代理服务器时,向 HTTP 请求头中添加X-Forwarded-For头部,以标识该请求是从哪个客户端发起的。- “X-Forwarded-For”客户机请求标头字段,其后面附加了$remote_addr变量,用逗号分隔。如果客户端请求标头中不存在“X-Forwarded-For”字段,则$proxy_add_X_Forwarded_For变量等于$remote_addr变量。
这三个变量是在 Nginx 中与代理(proxy)相关的变量:
这三个变量在配置代理服务器时非常有用,特别是在需要记录客户端 IP 地址时。由于 Nginx 作为代理服务器时,实际处理请求的是代理服务器而非客户端,因此默认情况下无法记录客户端 IP 地址。使用 $proxy_add_x_forwarded_for 变量可以向请求头中添加 X-Forwarded-For 头部,该头部会包含客户端的 IP 地址信息。
下面是一个示例,展示了如何使用 $proxy_add_x_forwarded_for 变量:
location /api {
proxy_pass http://backend-server;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}在上面的示例中,$proxy_add_x_forwarded_for 变量被用于在 X-Forwarded-For 头部中添加客户端的 IP 地址信息。同时,proxy_set_header 指令也被使用,将客户端的真实 IP 地址存储在 X-Real-IP 头部中。这样做可以方便后续记录日志等操作。
proxy_set_header 是 Nginx 的一个指令,用于设置代理服务器发送到后端服务器的 HTTP 请求头部信息。
当 Nginx 作为代理服务器转发请求时,默认情况下会将客户端请求的 HTTP 头部信息原封不动地转发给后端服务器。但是有些情况下,我们可能需要在转发请求时,修改或添加一些 HTTP 请求头部信息,例如添加 X-Forwarded-For 头部、修改 Host 头部等。这时,就可以使用 proxy_set_header 指令来实现。
具体来说,proxy_set_header 指令的语法如下:
proxy_set_header header_name value;其中,header_name 是需要设置的 HTTP 请求头部的名称,value 则是该头部的值。例如,如果需要设置 X-Forwarded-For 头部,可以使用以下指令:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;这个指令会将 X-Forwarded-For 头部的值设置为 $proxy_add_x_forwarded_for,其中 $proxy_add_x_forwarded_for 是 Nginx 内置的变量,表示当前请求的客户端 IP 地址和之前代理服务器 IP 地址的列表。
总之,proxy_set_header 指令的作用就是在代理服务器转发请求时,修改或添加 HTTP 请求头部信息。
范例:实现客户端地址透传
环境说明
| IP | Role |
|---|---|
| 10.0.0.7 | client |
| 10.0.0.8 | nginx-proxy |
| 10.0.0.100 | nginx-web1 |
配置前测试
- 修改前测试,发现后端服务器只能显示代理服务器的IP,而不能显示客户端的真实IP
- 可以看到 “x_forwarded”: “-” 没有任何内容
# tail -f /var/log/nginx/access.log
...
{"@timestamp": "27/Apr/2022:19:48:32 +0800","remote_addr": "10.0.0.8","referer": "-","request": "GET / HTTP/1.0","status": 200,"bytes": 20,"agent": "curl/7.29.0","x_forwarded": "-","up_addr": "-","up_host": "-","up_resp_time": "-","request_time": "0.000" }proxy server 配置
#主配置文件
# /apps/nginx/conf/nginx.conf
...
http {
...
upstream test-proxy { # backend server groups define
server 10.0.0.100:8080 weight=1 fail_timeout=15s max_fails=3;
}
include /apps/nginx/conf/conf.d/*.conf;
...
# 子配置文件
# /apps/nginx/conf/conf.d/pc.conf
...
server {
...
location / {
proxy_pass http://test-proxy; #引用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 添加HOST到报文头部,如果客户端为NAT上网 那么其值为客户端的公用的公网IP地址,常用于在日志中记录客户端的真实IP地址
}
...
}backend server 配置
- 如果 backend server 是 nginx
# vim /etc/nginx/nginx.conf
http {
...
#主要是添加 "x_forwarded": "$http_x_forwarded_for" 这个字段
log_format log_json '{"@timestamp": "$time_local",'
'"remote_addr": "$remote_addr",'
'"referer": "$http_referer",'
'"request": "$request",'
'"status": $status,'
'"bytes": $body_bytes_sent,'
'"agent": "$http_user_agent",'
'"x_forwarded": "$http_x_forwarded_for",'
'"up_addr": "$upstream_addr",'
'"up_host": "$upstream_http_host",'
'"up_resp_time": "$upstream_response_time",'
'"request_time": "$request_time"'
' }';
access_log logs/access.log log_json; # 引用日志路径和格式名称
...
} - 如果 backend server 是 apache
# vim /etc/httpd/conf/httpd.conf
...
#添加%{X-Forwarded-For}i
LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
...
CustomLog "logs/access_log" combined配置后测试
- 测试访问后查看日志
- “x_forwarded”: “10.0.0.7”,这里可以看到真实的客户端地址
[root@nginx-web1 ~]# tail -f /var/log/nginx/access.log
...
{"@timestamp": "27/Apr/2022:19:47:26 +0800","remote_addr": "10.0.0.8","referer": "-","request": "GET / HTTP/1.0","status": 200,"bytes": 20,"agent": "curl/7.29.0","x_forwarded": "10.0.0.7","up_addr": "-","up_host": "-","up_resp_time": "-","request_time": "0.000" }
"补充说明
- 当Nginx设置X-Forwarded-For于$proxy_add_x_forwarded_for后会有两种情况发生:
- 如果从CDN过来的请求没有设置X-Forwarded-For头(通常这种事情不会发生),而到了我们这里Nginx设置将其设置为$proxy_add_x_forwarded_for的话,X-Forwarded-For的信息应该为CDN的IP,因为相对于Nginx负载均衡来说客户端即为CDN,这样的话,后端的web程序时死活也获得不了真实用户的IP的。
- CDN设置了X-Forwarded-For,我们这里又设置了一次,且值为$proxy_add_x_forwarded_for的话,那么X-Forwarded-For的内容变成 ”客户端IP,Nginx负载均衡服务器IP“如果是这种情况的话,那后端的程序通过X-Forwarded-For获得客户端IP,则取逗号分隔的第一项即可。
- 如上两点所说,如果我们知道了CDN设置了X-Forwarded-For信息,且只有客户端真实的IP的话,那么我们的Nginx负载均衡服务器可以不必理会该头,让它默认即可。
- 其实Nginx中还有一个$http_x_forwarded_for变量,这个变量中保存的内容就是请求中的X-Forwarded-For信息。如果后端获得X-Forwarded-For信息的程序兼容性不好的话(没有考虑到X-Forwarded-For含有多个IP的情况),最好就不要将X-Forwarded-For设置为 $proxy_add_x_forwarded_for。应该设置为$http_x_forwarded_for或者干脆不设置!
您所描述的两种情况都是可能发生的,具体取决于 CDN 和 Nginx 配置中的具体设置。
对于第一种情况,如果 CDN 没有设置 X-Forwarded-For 头部,那么在 Nginx 中设置 $proxy_add_x_forwarded_for 后,X-Forwarded-For 头部的值就会变成 CDN 的 IP 地址。这种情况下,后端的 web 程序将无法获得真实用户的 IP 地址。
对于第二种情况,如果 CDN 已经设置了 X-Forwarded-For 头部,那么在 Nginx 中设置 $proxy_add_x_forwarded_for 后,X-Forwarded-For 头部的值就会变成 “客户端 IP, CDN 的 IP, Nginx 的 IP”。此时,后端的程序可以通过取逗号分隔的第一项来获取客户端的真实 IP 地址。
综上所述,为了保证能够正确获取客户端的真实 IP 地址,需要根据具体情况进行配置,同时需要保证 CDN 和 Nginx 的设置是一致的。通常情况下,如果 CDN 已经设置了 X-Forwarded-For 头部,那么就不需要在 Nginx 中再次设置 $proxy_add_x_forwarded_for,否则可能会导致 X-Forwarded-For 头部的值不正确。
如果 X-Forwarded-For 头部的内容是 “客户端 IP, CDN 的 IP, Nginx 的 IP”,那么要从中获取客户端的真实 IP 地址,可以通过以下方法来提取:
在 Nginx 配置文件中,可以使用 $proxy_add_x_forwarded_for 变量来设置 X-Forwarded-For 头部的值。如果要在后端的程序中获取客户端的真实 IP 地址,可以使用相应的方法来解析 X-Forwarded-For 头部的值。
例如,在 PHP 中,可以使用以下代码来获取客户端的真实 IP 地址:
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$client_ip = trim($ips[0]);
} else {
$client_ip = $_SERVER['REMOTE_ADDR'];
}在这段代码中,首先判断是否存在 X-Forwarded-For 头部,如果存在,则使用 explode() 函数将其拆分为多个 IP 地址。然后从中获取第一个 IP 地址,并使用 trim() 函数去除空格。如果不存在 X-Forwarded-For 头部,则直接使用 $_SERVER['REMOTE_ADDR'] 变量来获取客户端的 IP 地址。
用shell呢
在 shell 脚本中,可以使用 awk 命令来提取 X-Forwarded-For 头部的值。
例如,如果 X-Forwarded-For 头部的值是 “客户端 IP, CDN 的 IP, Nginx 的 IP”,可以使用以下命令来提取客户端的 IP 地址:
#!/bin/bash
X_FORWARDED_FOR="客户端IP,CDN的IP,Nginx的IP"
CLIENT_IP=$(echo "$X_FORWARDED_FOR" | awk -F',' '{print $1}' | awk '{print $1}')
echo "Client IP: $CLIENT_IP"在这个例子中,我们首先将 X-Forwarded-For 的值存储在变量 $X_FORWARDED_FOR 中。然后,使用 echo 命令将其输出到管道中。接下来,我们使用 awk 命令来提取逗号分隔的第一个 IP 地址,使用 -F',' 选项指定逗号为分隔符,使用 {print $1} 选项来打印第一个字段。最后,我们再次使用 awk 命令来去除空格,并将结果存储在变量 $CLIENT_IP 中。
在实际的使用中,可以将这个脚本中的变量替换为实际的 X-Forwarded-For 头部的值,并将输出结果存储在一个变量中,以供后续处理使用。
范例:转发到后端时添加主机头
- apache 为:在<VirtualHost/>标签中的最后添加 ProxyPreserveHost on
- nginx为:在location {…}中添加 proxy_set_header Host $host;
proxy_connect_timeout
- 定义与后端服务器尝试建立连接的超时时间,默认60秒,超时会返回客户端504响应码
Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http, server, locationproxy_read_timeout
- 向后端服务器或服务器组发起read请求后,等待的超时时长,默认60s
Syntax: proxy_read_timeout time;
Default: proxy_read_timeout 60s;
Context: http, server, locationproxy_send_timeout
- 向后端服务器或服务器组发起wirte请求后,等待的超时时长,默认60s
Syntax: proxy_send_timeout time;
Default: proxy_send_timeout 60s;
Context: http, server, locationproxy_http_version
- nginx提供代理服务的http协议版本,默认1.0
Syntax: proxy_http_version 1.0 | 1.1;
Default: proxy_http_version 1.0;
Context: http, server, location
proxy_ignore_client_abort
- 当客户端关闭连接时是否也关闭与后端服务器的连接。
Syntax: proxy_ignore_client_abort on | off;
Default: proxy_ignore_client_abort off;
Context: http, server, location
# on:则服务器会忽略客户端中断并一直等着后端服务执行返回
# off:客户端中断后nginx也会中断与后端服务器的连接 并立即记录499日志proxy_headers_hash_bucket_size
- 当配置了 proxy_hide_header 和 proxy_set_header 的时候,用于设置nginx保存HTTP报文头的hash表的上限
Syntax: proxy_headers_hash_bucket_size size;
Default: proxy_headers_hash_bucket_size 64;
Context: http, server, locationproxy_headers_hash_max_size size
设置 proxy_headers_hash_max_size 的最大可用空间
Syntax: proxy_headers_hash_max_size size;
Default: proxy_headers_hash_max_size 512;
Context: http, server, locationhttp_core 模块相关参数说明
- hash 说明:https://nginx.org/en/docs/hash.html
server_names_hash_bucket_size
- server_name hash 表申请空间大小
Syntax: server_names_hash_bucket_size size;
Default: server_names_hash_bucket_size 32|64|128;
Context: httpserver_names_hash_bucket_size 是 Nginx 配置文件中用于设置服务器名哈希表桶的大小的指令。
在 Nginx 中,当客户端发起请求时,Nginx 会根据请求中的域名(即 HTTP 请求头部中的 Host 字段)来确定应该将请求转发给哪个虚拟主机。这个过程需要使用哈希表来实现。因为哈希表的查询速度很快,所以这种方式非常高效。
server_names_hash_bucket_size 指令用于设置这个哈希表的桶的数量。默认情况下,这个值是 32 或 64,取决于 Nginx 版本和编译参数。如果你有大量的虚拟主机(例如数百个或数千个),并且经常会动态添加或删除虚拟主机,那么你可能需要增加这个值,以避免哈希冲突,提高 Nginx 的性能。
通常来说,可以将 server_names_hash_bucket_size 设置为虚拟主机数量的两倍,例如:
http {
server_names_hash_bucket_size 128;
...
}这个指令需要放在 http 块中,并且必须在任何 server 块之前设置。
server_names_hash_max_size
- 设置服务器名称hash表的上限大小
Syntax: server_names_hash_max_size size;
Default: server_names_hash_max_size 512;
Context: httpserver_names_hash_max_size 是 Nginx 配置文件中用于设置服务器名哈希表的最大大小的指令。
在 Nginx 中,当客户端发起请求时,Nginx 会根据请求中的域名(即 HTTP 请求头部中的 Host 字段)来确定应该将请求转发给哪个虚拟主机。这个过程需要使用哈希表来实现。哈希表的大小由 server_names_hash_max_size 指令设置。
当 Nginx 遇到一个新的虚拟主机时,它会将主机名添加到哈希表中。如果哈希表已经满了,Nginx 会使用server_names_hash_bucket_size 指令定义的桶的数量来确定应该删除哪些条目。如果哈希表中的所有条目都无法删除,Nginx 会自动调整哈希表的大小,并生成一个新的哈希表来保存所有的主机名。
如果哈希表的最大大小被设置得太小,那么 Nginx 可能会不得不频繁地创建新的哈希表,这会影响性能。因此,如果你的服务器有大量的虚拟主机(例如数百个或数千个),那么你可能需要增加这个值。
通常情况下,可以将 server_names_hash_max_size 设置为虚拟主机数量的两倍或三倍,例如:
http {
server_names_hash_max_size 4096;
server_names_hash_bucket_size 128;
...
}这个指令需要放在 http 块中,并且必须在任何 server 块之前设置。
http_upstream 模块常用参数说明
- 针对 http 协议定义转发的后端服务器组,就是实现七层负载均衡
- 提供服务器分组转发、权重分配、状态检测、调度算法等高级功能
- Module ngx_http_upstream_module (nginx.org)
upstream name {
[调度算法] server address [parameters]; ...
}
# address 可以为IP、域名、unix,可以指定端口 不指定则默认为80端口调度算法说明
- 不加任何调度算法默认为轮询
hash key
- 基于指定请求报文中首部字段或 RU I等 key 做 hash 计算后调度
- 另外,需要注意的是在实际生产环境中,选择合适的 key 并不是一件简单的事情。如果选择的 key 不够随机或者分布不均,可能会导致后端服务器的负载不均衡,进而影响系统性能。因此,需要根据具体业务场景和需求进行选择。
- https://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash
Syntax: hash key [consistent];
Default: —
Context: upstream
# 使用 consistent 参数,将使用ketama一致性hash算法,适用于后端是cache服务器(如varnish)时使用,consistent定义使用一致性hash运算,一致性hash基于取模运算(即使增减后端服务器数量,也能尽量保持相同的 key 对应的后端服务器不变,避免出现大量缓存失效等问题。)范例
- 基于用户请求的uri做hash调度,可以防止session堆积,实现会话保持
upstream backend {
hash $request_uri consistent;
}- 基于客户端请求报文中 cookie 首部字段中的 JSESSIONID 这个 key 进行 hash 调度
upstream backend {
hash $cookie_JSESSIONID;
}- 基于cookie中的sessionID这个key进行hash调度,实现会话绑定
upstream backend {
hash $cookie_sessionid;
}$cookie_JSESSIONID 和 $cookie_sessionid 这两个变量是从哪来的呀,我看nginx官方文档也没有啊
是的,您说的这两个变量 $cookie_JSESSIONID 和 $cookie_sessionid 并不是 Nginx 官方预定义的变量,它们是用户根据实际业务需要自定义的变量。
在 Nginx 中,我们可以使用 $cookie_name 变量来获取请求报文中指定的 cookie 值,其中的 name 部分可以根据实际情况来自定义,例如 $cookie_JSESSIONID 就是获取请求报文中名为 JSESSIONID 的 cookie 值。同理,$cookie_sessionid 也是根据实际情况自定义的变量名,用来获取请求报文中名为 sessionid 的 cookie 值。
需要注意的是,由于 HTTP 协议规定 cookie 名称大小写敏感,因此在使用 $cookie_name 变量时,需要保证变量名大小写与 cookie 名称完全一致。
ip_hash
-
ip_hash 算法是基于客户端的 remote_addr 做 hash 计算,以实现会话保持。只要是同一个IP地址则都会转发到同一个后端服务器。
-
但需要注意,在家庭或学校等场合可能会存在多台主机使用同一个公网IP地址的情况,因此这种情况下不适用ip_hash算法。
- 在家庭或学校等场合可能会存在多台主机使用同一个公网IP地址的情况,所以不常用
-
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash
Syntax: ip_hash;
Default: —
Context: upstream范例
upstream backend {
ip_hash;
}least_conn
- 最少连接调度算法,基于转发服务器组中连接数最少的主机进行转发,相当于LVS中的WLC
- 不会会话保持,不常用
Syntax: least_conn;
Default: —
Context: upstream范例
upstream backend {
least_conn;
}parameters
- 定义相关参数
- 这些参数都可以在upstream的server块中使用来配置负载均衡器和后端服务器的行为,实现更灵活的负载均衡策略和高可用性。
- https://nginx.org/en/docs/http/ngx_http_upstream_hc_module.html
weight=number
- 指定服务器的权重,假设有两台服务器,权重分别为3和1,即代表一个服务器被调用3次另一个服务器才会被调用1次
- 默认为1,类似于LVS中的WRR、WLC等
范例
- 将第一台服务器的权重设置为2,第二台服务器的权重设置为1,即第一台服务器被调用2次,第二台服务器被调用1次。
upstream backend {
server backend1.example.com weight=2;
server backend2.example.com weight=1;
}max_conns=number
- 给当前server设置最大活动连接数
- 默认为0,表示没有限制
范例
- 将第一台服务器的最大连接数设置为100,第二台服务器的最大连接数设置为50。
upstream backend {
server backend1.example.com max_conns=100;
server backend2.example.com max_conns=50;
}max_fails=number
- 超时后与后端服务器的重新尝试次数,和fail_timeout配合使用
- 假设fail_timeout=15s max_fails=3,即代表连续三次请求超时超过15秒就标记为不可用
- 默认为1,当客户端访问时,才会利用TCP触发对探测后端服务器健康性检查,而非周期性的探测
范例
- 将第一台服务器的最大失败次数设置为3,最大超时时间为15秒,第二台服务器的最大失败次数设置为2,最大超时时间为10秒。
upstream backend {
server backend1.example.com max_fails=3 fail_timeout=15s;
server backend2.example.com max_fails=2 fail_timeout=10s;
}fail_timeout=time
- 对后端服务器的单次检测超时时间
- 默认10s,和max_fails配合使用
范例
- 将第一台服务器的最大失败次数设置为3,单次检测超时时间为20秒,第二台服务器的最大失败次数设置为2,单次检测超时时间为30秒。
upstream backend {
server backend1.example.com fail_timeout=20s max_fails=3;
server backend2.example.com fail_timeout=30s max_fails=2;
}backup
- 将服务器标记为备份服务器。当主服务器不可用时,它将被传递请求
- 需要注意的是,backup参数不能和hash、ip_hash、random负载平衡方法一起使用,因为这些方法的调度算法和backup参数相互矛盾,使用时需要慎重。
范例
- 将第一台服务器设置为主服务器,第二台服务器设置为备份服务器。当第一台服务器不可用时,才会将请求传递给第二台服务器。
upstream backend {
server backend1.example.com;
server backend2.example.com backup;
}down
- 标记为down状态
范例
upstream backend {
server 192.168.1.1 down;
}resolve
- 当server定义的是主机名的时候,如果A记录发生变化会自动应用新IP而不用重启nginx
范例
upstream backend {
server backend.example.com resolve;
}综合范例一
此配置文件定义了一个名为"backend"的upstream,其中使用了ip_hash负载均衡策略。该upstream包含4个server,分别为:
- backend1.example.com,权重为5,即代表其被调用5次后才会调用下一个server;
- 127.0.0.1:8080,设置了最大失败次数为3次,如果在30秒内连续3次请求失败,则该server标记为不可用,直到fail_timeout时间过后重新被调度;
- unix:/tmp/backend3,这是一个Unix域套接字(Unix domain socket);
- backup1.example.com,被标记为备份服务器,在其他server都不可用的情况下,才会将请求转发到该server。
当客户端请求到达时,会根据ip_hash负载均衡算法将请求分配到相应的server中。如果前3个请求都失败了,则最后一个备份server的信息会被返回给客户端。
upstream backend {
ip_hash;
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,每个server有不同的权重,如果有一个server宕机,则将请求分配到其他两个server
同时对宕机的server进行健康性检查,每30秒检查一次,最多尝试3次,单次检查超时时间为10秒
upstream backend {
server 192.168.0.101 weight=2;
server 192.168.0.102;
server 192.168.0.103 weight=3;
# 对宕机的server进行健康性检查
# 每30秒检查一次,最多尝试3次,单次检查超时时间为10秒
check interval=30s rise=3 fall=3 timeout=10s type=http;
}综合范例三
此配置表示:以ip_hash方式分配请求到两个server,server1被标记为down状态,只有server2可用,如果server2也宕机,则将请求分配到备份server
nginxCopy codeupstream backend {
ip_hash;
server 192.168.0.101 down;
server 192.168.0.102;
server backup1.example.com backup;
}stream_upstream 模块常用参数说明
- 实现TCP/UDP/UNIX负载均衡 定义转发的后端服务器组,就是实现四层负载均衡
- 由 ngx_stream_upstream_module 模块
- 如果编译安装,则需要指定 –with-stream 选项才能支持
- Module ngx_stream_upstream_module (nginx.org)
Syntax: upstream name { ... }
Default: —
Context: stream范例
- 注意:stream语句块是一个单独的语句块,要写在所有语句块之外
这个配置定义了一个用于stream流量的upstream块,使用了consistent哈希算法基于客户端IP进行负载均衡。具体说明如下:
- upstream块定义了一组后端服务器,包括两个主服务器backend1.example.com:12345和backend2.example.com:12345,以及一个Unix域套接字/tmp/backend3对应的服务器。这些服务器的调度权重使用默认值1。此外,还有两个备份服务器backup1.example.com:12345和backup2.example.com:12345,备份服务器不参与负载均衡调度。
- 负载均衡的调度方式是使用了哈希算法,将客户端的IP地址作为哈希函数的输入,将请求分配给指定的后端服务器。使用consistent选项表示会在后端服务器列表发生变化时保持连接到同一台服务器。
- 在stream服务器中使用proxy_pass指令将所有流量代理到名为backend的upstream块上,实现了基于哈希的负载均衡。
同时,需要注意到这是一个用于stream流量的配置段,并不是用于HTTP请求的。
upstream backend { # stream 服务器相关配置段
hash $remote_addr consistent; # 调度算法与 http upstream 一致
server backend1.example.com:12345 weight=5;
server backend2.example.com:12345;
server unix:/tmp/backend3;
server backup1.example.com:12345 backup;
server backup2.example.com:12345 backup;
}
server { # 在server配置段
listen 12346;
proxy_pass backend; # 调用
}实现 http协议反向代理 (单主机)
- 针对后端单主机反向代理
server {
...
location / {
...
proxy_pass http://10.0.0.18:8080; #注意一般末尾不加/
...
}
...
}实现 http协议反向代理 (多主机)
定义服务器组
- 自定义一组服务器,只能配置在http段中
[root@nginx-proxy ~]# vim /apps/nginx/conf/nginx.conf
...
http {
upstream group-name { # 老版本组名不能使用_(但新版本支持_,但不建议使用)
#hash $request_uri consistent; # 调度算法,基于用户请求的uri做hash调度,可以防止session堆积,实现会话保持
server 10.0.0.100:8080 weight=1 fail_timeout=15s max_fails=3;
server 10.0.0.101:8080 weight=1 fail_timeout=15s max_fails=3;
}
include /apps/nginx/conf/conf.d/*.conf;
...
}
}
...调用
[root@nginx-proxy ~]# vim /apps/nginx/conf/conf.d/pc.conf
server {
listen 80;
server_name www.xiangzheng.com;
location /web {
index index.html;
proxy_pass http://group-name; #直接调用upstream名称即可
}
}测试访问
- 因为将基于uri调度注释了,所以默认将以轮询的方式进行调度
- 如果根据uri进行调度则同一主机只要访问的是相同的uri则都会调度到同一台backend server上
[root@client ~]#curl www.azheng.com
backend server page 10.0.0.100
[root@client ~]#curl www.azheng.com
backend server page 10.0.0.101实现 tcp协议反向代理
实现 redis 的反向代理
- 下面是和MySQL的tcp负载均衡范例:
环境说明
10.0.0.8 #nginx-proxy
10.0.0.100 #redis-node1
10.0.0.101 #redis-node2nginx-proxy 配置
- 注意要放在http语句块外
[root@nginx-proxy ~]# vim /apps/nginx/conf/nginx.conf
...
stream {
upstream redis-server { #定义
#hash $remote_addr consistent;
server 10.0.0.100:6379;
server 10.0.0.101:6379;
}
server {
listen 0.0.0.0:6379;
proxy_pass redis-server; #调用
}
upstream mysql-server {
#hash $remote_addr consistent;
server 10.0.0.100:3306;
server 10.0.0.101:3306;
}
server {
listen 0.0.0.0:3306;
proxy_pass mysql-server;
}
...
# 重启nginx查看监听在proxy的端口是否开启
[root@nginx-proxy ~]# ss -ntl|grep 6379
LISTEN 0 511 10.0.0.8:6379 0.0.0.0:* backend server 配置
- 在所有 redis-node 执行
# yum -y install redis
# vim /etc/redis.conf
...
bind 0.0.0.0
...
# systemctl enable --now redis
# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:6379 0.0.0.0:* 测试
- 因为将远程地址hash的调度算法注释了,所以默认会以轮询的方式进行调度转发,否则只要客户端源地址相同 就转发到同一台backend server上
[root@clicent ~]#telnet 10.0.0.8 6379
Trying 10.0.0.8...
Connected to 10.0.0.8.
Escape character is '^]'.
info
$3256
# Server
redis_version:5.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:9529b692c0384fb7
...
[root@clicent ~]#telnet 10.0.0.8 6379
Trying 10.0.0.8...
Connected to 10.0.0.8.
Escape character is '^]'.
set james azheng
+OK
get james
$6
azheng实现 MySQL 的反向代理
环境说明
10.0.0.8 #nginx-proxy
10.0.0.100 #mysql-node1
10.0.0.101 #mysql-node2nginx-proxy 配置
- 注意要放在http语句块外
[root@nginx-proxy ~]# vim /apps/nginx/conf/nginx.conf
...
stream {
upstream redis-server {
#hash $remote_addr consistent;
server 10.0.0.100:6379;
server 10.0.0.101:6379;
}
server {
listen 0.0.0.0:6379;
proxy_pass redis-server;
}
upstream mysql-server { #定义
#hash $remote_addr consistent;
server 10.0.0.100:3306;
server 10.0.0.101:3306;
}
server {
listen 0.0.0.0:3306;
proxy_pass mysql-server; #调用
}
...
[root@nginx-proxy ~]# ss -ntl|grep 3306
LISTEN 0 511 10.0.0.8:3306 0.0.0.0:* backend server 配置
- 在所有 mysql-node 执行
# yum -y install mariadb-server
# systemctl enable --now mariadb
# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:3306 0.0.0.0:*
#创建一个测试账号
# mysql
MariaDB [(none)]> CREATE USER 'james'@'%' IDENTIFIED BY 'azheng';
MariaDB [(none)]> GRANT ALL ON *.* TO james@'%';测试
- 因为将远程地址hash的调度算法注释了,所以默认会以轮询的方式进行调度转发,否则只要客户端源地址相同 就转发到同一台backend server上
root@client:~# mysql -h 10.0.0.8 -ujames -pazheng
...
mysql> select @@hostname;
+------------+
| @@hostname |
+------------+
| mysql-node1|
+------------+
root@client:~# mysql -h 10.0.0.8 -ujames -pazheng
...
mysql> select @@hostname;
+------------+
| @@hostname |
+------------+
| mysql-node2|
+------------+实现 fastcgi协议反向代理
将客户端对php的请求以fastcgi协议转发至指定服务器处理
官方文档:https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html
- 参阅LNMP笔记
nginx proxy 优化总结
开启客户端真实地址透传
- 利用 ngx_http_proxy_module模块 中的 proxy_set_header参数 来实现客户端真实地址透传,以便分析客户端的访问情况
- 否则默认 backend server 只能捕获 proxy 的访问IP
开启缓存
- 在每个nginx-proxy开启缓存后,可以将一些频繁往后端服务器读取的数据缓存在nginx-proxy上,从而提升效率,缓存功能默认处于关闭状态
- 注意动态页面慎用缓存 以及 升级前要关闭缓存
添加响应报文头部信息
-
实现对后端服务器响应给客户端的报文中添加指定的响应首部字段
-
#生产中通常开启,可以间接性的告诉攻击者攻击的是CDN等信息 add_header X-Cache $upstream_cache_status; #是否缓存命令,常用 add_header X-Accel $server_name #客户访问的FQDN,常用
定义基于TCP、UDP、UNIX等协议的后端代理配置
tream
- 定义基于TCP、UDP、UNIX等协议的后端代理配置
stream backend { # stream 服务器相关配置段
hash $remote_addr consistent;
server backend1.example.com:12345 weight=5;
server backend2.example.com:12345;
server unix:/tmp/backend3;
server backup1.example.com:12345 backup;
server backup2.example.com:12345 backup;
}
server { # 在server配置段
listen 12346;
proxy_pass backend; # 调用
}Nginx + php
Nginx + PHP 实现动静分离,可以有效地提升网站的访问速度和性能。在这种架构下,静态资源由 Nginx 处理,而动态请求则交由 PHP 处理。下面是实现动静分离的详细步骤以及需要注意的事项:
- 安装 Nginx 和 PHP
首先,需要安装 Nginx 和 PHP。具体安装方法可以参考各自的官方文档。
- 配置 Nginx
配置 Nginx 的主要任务是将静态资源的请求转发到 Nginx,而将动态请求转发到 PHP。
在 Nginx 的配置文件中,需要添加如下代码段:
phpCopy codelocation / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
这里的 location / 表示对根路径的请求进行处理,try_files 指令表示先尝试访问 URI 对应的文件,若不存在则尝试访问以 URI 为前缀的目录,若还不存在则将请求转发到 index.php。location ~ \.php$ 表示对以 .php 结尾的请求进行处理,将其转发到 PHP 的 FastCGI 进程中处理。
- 配置 PHP
在 PHP 的配置中,需要设置 PHP 的 FastCGI 进程监听一个 Unix Socket,这里我们假设它监听在 /var/run/php/php7.4-fpm.sock。
- 配置静态资源
将静态资源(如图片、CSS、JavaScript 等)放在一个单独的目录下,并在 Nginx 配置文件中添加如下代码段:
arduinoCopy codelocation /static/ {
root /path/to/static/files;
expires max;
add_header Cache-Control public;
}
这里的 location /static/ 表示对以 /static/ 开头的请求进行处理,root 指令表示静态资源的根目录,expires 指令表示浏览器缓存时间,add_header 指令表示添加响应头。
注意事项
- 需要保证静态资源和动态请求的路径不重复,否则可能会出现访问冲突。
- 静态资源可以使用 CDN 进行加速,提高访问速度。
- 静态资源的缓存时间需要合理设置,可以根据实际情况调整。过长的缓存时间可能会导致客户端无法及时获取到更新后的资源。
- 需要定期清理静态资源的缓存,避免过期资源占用过多的存储空间。
FastCGI
PHP 的请求是通过 FastCGI 协议与 Web 服务器进行通信的。具体实现方式是,Web 服务器(如 Nginx)会作为 FastCGI 客户端,将 PHP 脚本的请求转发给 FastCGI 服务器(如 PHP-FPM),FastCGI 服务器接收到请求后,会解析请求参数,执行 PHP 脚本,将结果返回给 Web 服务器,最终由 Web 服务器将结果返回给客户端。这种方式实现了 Web 服务器和 PHP 解释器的解耦,可以提高 Web 服务器的性能和稳定性。
Nginx + tomcat
Nginx + Tomcat 实现动静分离,同样可以提升网站的访问速度和性能。在这种架构下,静态资源由 Nginx 处理,而动态请求则交由 Tomcat 处理。下面是实现动静分离的详细步骤以及需要注意的事项:
- 安装 Nginx 和 Tomcat
首先,需要安装 Nginx 和 Tomcat。具体安装方法可以参考各自的官方文档。
- 配置 Nginx
配置 Nginx 的主要任务是将静态资源的请求转发到 Nginx,而将动态请求转发到 Tomcat。
在 Nginx 的配置文件中,需要添加如下代码段:
location / {
try_files $uri $uri/ @tomcat;
}
location @tomcat {
proxy_pass http://localhost: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;
}这里的 location / 表示对根路径的请求进行处理,try_files 指令表示先尝试访问 URI 对应的文件,若不存在则尝试访问以 URI 为前缀的目录,若还不存在则将请求转发到 @tomcat 标记的 location。location @tomcat 表示将请求转发到 Tomcat 的 HTTP 端口上。
- 配置 Tomcat
在 Tomcat 的配置中,需要将 HTTP 端口设置为一个非标准的端口(如 8080),以避免和 Nginx 的 HTTP 端口(80)冲突。同时,可以使用 AJP 协议将 Tomcat 和 Nginx 进行连接,提高性能和安全性。
在 Tomcat 的 server.xml 配置文件中,可以添加如下代码段:
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443" />
这里的 port 表示 AJP 协议监听的端口号,protocol 表示使用的协议,redirectPort 表示重定向端口号。
- 配置静态资源
将静态资源(如图片、CSS、JavaScript 等)放在一个单独的目录下,并在 Nginx 配置文件中添加如下代码段:
arduinoCopy codelocation /static/ {
root /path/to/static/files;
expires max;
add_header Cache-Control public;
}
这里的 location /static/ 表示对以 /static/ 开头的请求进行处理,root 指令表示静态资源的根目录,expires 指令表示浏览器缓存时间,add_header 指令表示添加响应头。
注意事项
- 需要保证静态资源和动态请求的路径不重复,否则可能会出现访问冲突。
- 静态资源的缓存策略需要考虑到不同类型的文件,如图片、CSS、JavaScript 等可能需要设置不同的缓存时间。
- 在高并发的情况下,需要配置 Nginx 和 Tomcat 的连接池,以提高处理能力。
- 如果使用 HTTPS 协议,则需要在 Nginx 和 Tomcat 中配置 SSL 证书。
- 需要考虑安全问题,如防止跨站脚本攻击(XSS)、SQL 注入等。
- 在配置过程中需要注意语法错误和路径设置错误等问题,以避免出现无法预料的错误。
AJP
Tomcat 同样也可以使用类似于 FastCGI 的协议来与 Web 服务器进行通信,具体的协议名称是 AJP(Apache JServ Protocol)。AJP 是一种二进制协议,可以在 Web 服务器和 Tomcat 之间快速传输 HTTP 请求和响应。
与 FastCGI 类似,AJP 协议也可以提高 Web 服务器和应用服务器(如 Tomcat)之间的性能,同时还可以支持动态负载均衡和会话复制等功能。通常情况下,Nginx 作为反向代理服务器与 Tomcat 进行通信时,可以使用 AJP 协议实现动态网页的请求转发。