1.uwsgi、fastcgi、scgi、http指令的差异
七层反向代理
CGI
全称通用网关接口 Commmon Gateway Interface。CGI描述了服务器和请求处理程序之间传输数据的一种标准。
是HTTP服务器和动态脚本语言间通信的接口或者工具
fastcgi : php
Fast Common Gateway Interface,早期 CGI的增强版本
差异:
- CGI 对 Web 服务器的每个请求均建立一个进程进行请求处理
- FastCGI 服务进程接收 Web 服务器的请求后,由自己的进程自行创建线程完成请求处理。
php的实现fastcgi是 PHP-FPM
wsgi usgi
多用于 python web开发 , django或flask
scgi
Simple Common Gateway Interface 简单通用网关接口 fastCGI类型 但更容易实现,性能比 FastCGI 要弱
2.memcached反向代理的用法
ngx_http_memcached_module 安装
例子
server {
server_name localhost;
listen 8319;
#root html/;
default_type text/plain;
location /get { # 通过get转发
set $memcached_key "$arg_key";
#memcached_gzip_flag 2;
memcached_pass localhost:11211; # 代理的memcahed 服务器地址
}
}
# 写入变量
curl 127.0.0.1:8319/get?key=hello
telnet 127.0.0.1 11211
# set hello 0 0 5
# get hello
# VALUE hello 0 5
curl 127.0.0.1:8319/get?key=hello -I
#显示不压缩
# 通过加入 gzipkey 告诉浏览器开启gzip压缩
curl 127.0.0.1:8319/get?key=gzipkey -I
#Content-Encoding: gzip
3.websocket反向代理
安装 ngx_http_proxy_module
# 需要升级协议才能使用
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
- 数据分片 :有序
- 不支持多路复用 A Multiplexing Extension for WebSockets
- 不支持压缩 Compression Extensions for WebSocket
扩展头部
- Sec-WebSocket-Version: 客户端发送,表示它想使用的WebSocket 协议版本 (13表示RFC 6455)。如果服务器不支持这个版本,必须回应自己支持的版本。
- Sec-WebSocket-Key: 客户端发送,自动生成的一个键,作为一个对服务器的“挑战”,以验证服务器支持请求的协议版本;
- Sec-WebSocket-Accept: 服务器响应,包含Sec-WebSocket-Key 的签名值,证明它支持请求的协议版本
- Sec-WebSocket-Protocol: 用于协商应用子协议: 客户端发送支持的协议列表,服务器必须只回应一个协议名- - Sec-WebSocket-Extensions: 用于协商本次连接要使用的WebSocket扩展: 客户端发送支持的扩展,服务器i同的首部确认自己支持一或多个扩展。
例子
server {
server_name localhost;
#root html/;
default_type text/plain;
listen 8320;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass ws://121.40.165.18:8800/;
}
}
## 在线调试
http://www.websocket-test.com/
在网址里输入 本地的的 ws://xx.xx.xx.xx:8320 服务,点击连接 然后发送消息
# 本地监听转发的地址
tcpdump -i eth0 port 8800 and host 121.40.165.18
# 可以监听各种输出
4.用分片提升缓存效率
slice模块
Nginx的slice模块可以将一个请求分解成多个子请求,每个子请求返回响应内容的一个片段,让大文件的缓存更有效率。同时配合http的range 提供客户端下载的速度。
- 通过range协议将大文件分解为多个小文件,更好的用缓存为客户端的range协议服务模块.
- http_slice_module,通过--with-http_slice_module启用功能
例子
# 上游服务器
server {
server_name localhost;
listen 8012;
default_type text/plain;
root html;
location /{
return 200 ;
}
}
# 下游服务器
proxy_cache_path /data/nginx/tmpcache3 levels=2:2 keys_zone=three:10m loader_threshold=300
loader_files=200 max_size=200m inactive=1m;
server {
server_name localhost;
listen 8321;
error_log logs/cacherr.log debug;
location ~ /purge(/.*) {
proxy_cache_purge three $1$is_args$args$slice_range;
}
location /{
proxy_cache three;
# 通过制定切割大小实现 缓存优化,slice proxy_cache_key proxy_set_header 需要同时打开
#slice 1m;
#proxy_cache_key $uri$is_args$args$slice_range;
#proxy_set_header Range $slice_range;
proxy_cache_valid 200 206 1m;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://localhost:8012;
}
}
# 在没打开 slice ; 时候
# 按指定切片访问nginx 的大文件,虽然只访问了一小部分,但nginx考虑到后面其他链接也会使用,所以会发起完整的请求。
curl -r 5000010-5000019 127.0.0.1:8321/test.mp4 -I
# nginx会切割成多个 小请求,通过range方式请求上游服务器,通过日志可以看到
tail -f../logs/access.log
# 日志显示 请求了完整的文件/test.mp4
#127.0.0.1 23/Dec/2023:14:30:19+08001/video .mp4"HEAD/video.mp4 HTTP/10" 206 curl/7.29.0"
# 当打开了 slice ; 时候
curl -r 5000010-5000019 127.0.0.1:8321/test.mp4 -I
tail -f../logs/access.log
# 只请求的切片的大小
#127.0.0.1 23/Dec/2023:14:30:19+08001/video .mp4"HEAD/video.mp4 HTTP/10" 206 57381757 curl/7.29.0"
5.open_file_cache提升系统性能
直接把文件缓存到内存里,检查问价open的开销
open_file_cache_errors on | off;
例子
server {
listen 8092;
root html;
location / {
open_file_cache max=10 inactive=60s;
open_file_cache_min_uses 1;
open_file_cache_valid 60s;
open_file_cache_errors on;
}
}
# 安装strace strace常用来跟踪进程执行时的系统调用和所接收的信号, 是一个强大的工具
yum install strace -y
# 获取当前nginx的 worker id
ps -ef | grep nginx
strace -p 4669 # 监听指定id
# 可以看到 nginx是否
6. http2协议介绍
HTTP2主要特性
- 传输数据量的大幅减少 : 以二进制方式传输,标头压缩
- 多路复用及相关功能: 消息优先级
- 服务器消息推: 并行推送
HTTP2.0核心概念
- 连接Connection:1个TCP连接,包含一个或者多个Stream
- 数据流Stream:一个双向通讯数据流,包含多条Message
- 消息Message:对应HTTP1中的请求或者响应包含一条或者多条Frame
- 数据帧Frame:最小单位,以二进制压缩格式存放HTTP1中的内容
传输中无序,接收时组装
数据流优先级
- 每个数据流有优先级(1-256)
- 数据流间可以有依赖关系
标头压缩
Frame格式
TYPE类型:
- HEADERS:顿仅包含HTTP 标头信息
- DATA:顿包含消息的所有或部分有效负载
- PRIORITY:指定分配给流的重要性。
- RST STREAM: 错误通知:一个推送承诺遭到拒绝。终止流
- SETTINGS:指定连接配置。
- PUSH PROMISE: 通知一个将资源推送到客户端的意图
- PING: 检测信号和往返时间。
- GOAWAY:停止为当前连接生成流的停止通知
- WINDOW UPDATE: 用于管理流的流控制。
- CONTINUATION: 用于延续某个标头碎片序列。
服务器推送PUSH
7.搭建http2服务并推送资源
- ngx_http_v2_module,通过--with-http_v2_module编译nginx加入http2协议的支持
- 功能:对客户端使用http2协议提供基本功能
- 前提: 开启TLS/SSL协议
- 使用方法: listen 443 ssl http2;
nginx推送资源
- http2_push_preload on | off;
- http2 push uri | off; 如: http2 push/image.png
测试nginx http2协议的客户端工具 github.com/nghttp2/ngh…
centos下使用yum安装:yum install nghttp2
例子
server {
server_name localhost;
listen 4430 ssl http2; # managed by Certbot
ssl_certificate ./cert/a.crt;
ssl_certificate_key ./cert/a.key;
root html;
location / { # 当请求 根目录,则会推送 mirror.txt和video.mp4回去
http2_push /mirror.txt;
http2_push /video.mp4;
}
location /test {
add_header Link "</style.css>; as=style; rel=preload";
http2_push_preload on;
}
}
nghttp -ns https://localhost:4430/
# 返回 返回html同时,也会推送 mirror.txt和video.mp4回去
nghttp -ns https://localhost:4430/test
# 同时返回 css
## 最大并行推送数
http2_max_concurrent_pushes 10;
## 超时控制
http2_recv_timeout 30s;
http2_idle_timeout 3m;
## 并发请求控制
http2_max_concurrent_pushes 10;
http2_max_concurrent_streams 128;
http2_max_field_size 4k;
## 连接最大处理请求数
http2_max_requests 1000;
http2_chunk_size 8k;
## 设置响应包体的分片大小
http2_chunk_size 8k;
## 缓冲区盅椽大小设置
http2_recv_buffer_size 256k;
http2_max_header_size 16k;
http2_body_preread_size 64k
8.grpc反向代理
- gRPC是一种进程间通信技术。在 gRPC 中,客户端可以直接调用不同机器上的服务端的方法,就像调用本地函数一样。
- RPC是远程过程调用(Remote Procedure Call)的缩写形式
- grpc协议:grpc.io/
- ngx_http_grpc_module,通过--without-http_grpc_module禁用依赖ngx_http_v2_module模块
例子
server {
server_name localhost;
root html;
location / {
grpc_pass localhost:50051; # 50051是本地的python服务
}
listen 4431 http2;
ssl_certificate ./cert/a.crt;
ssl_certificate_key ./cert/a.key;
}
with grpc.insecure channel( localhost:50051') as channel:
python greeter client.py # 这里的python 是grpc example提供
9.stream四层反向代理(TCP)
9.1基础
stream模块7个阶段
- POST_ACCEPT realip
- PREACCESS limt_conn
- ACCESS access
- SSL ssl
- PREREAD ssl_preread
- CONTENT return, stream_proxy
- LOG access_log
传输层相关的变量
- binary_remote_addr 客户端地址的整型格式,对于IPv4是4字节,对于 IPV6是16字节
- connection 递增的连接序号
- remote_addr 客户端地址
- remote_port 客户端端口
- proxy_protocol_addr 若使用了proxy_protocol协议则返回协议中的地址,否则返回空
- proxy_protocol_port 若使用了proxy_protocol协议则返回协议中的端口,否则返回空
- protocol 传输层协议,值为TCP或者UDP
- server_addr 服务器端地址
- server_port 服务器端端口
- bytes_received 从客户端接收到的字节数
- bytes_sent 已经发送到客户端的字节数
传输层相关的变量 status
- 200: session成功结束
- 400:客户端数据无法解析,例如proxy protocol协议的格式不正确
- 403:访问权限不足被拒绝,例如access模块限制了客户端IP地址
- 500:服务器内部代码错误
- 502:无法找到或者连接上游服务
- 503:上游服务不可用
Nginx 系统变量
- time_local 以本地时间标准输出的当前时间,例如14/Nov/2018:15:55:37 +0800
- time_iso8601 使用ISO 8601标准输出的当前时间,例如2018-11-14T15:55:37+08:00
- nginx_version Nginx 版本号
- pid 所属worker 进程的进程id
- pipe 使用了管道则返回 p,否则返回
- hostname 所在服务器的主机名,与hostname 命令输出一致
- msec 1970年1月1日到现在的时间,单位为秒,小数点后精确到毫秒
content阶段: return模块
returm value;
例子
stream {
log_format basic '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
error_log logs/stream_error.log debug;
access_log logs/stream_access.log basic;
server {
listen 10004;
set_real_ip_from 127.0.0.1;
allow 202.112.144.236;
deny all;
return '10004 vars:
bytes_received: $bytes_received
bytes_sent: $bytes_sent
proxy_protocol_addr: $proxy_protocol_addr
proxy_protocol_port: $proxy_protocol_port
remote_addr: $remote_addr
remote_port: $remote_port
realip_remote_addr: $realip_remote_addr
realip_remote_port: $realip_remote_port
server_addr: $server_addr
server_port: $server_port
session_time: $session_time
status: $status
protocol: $protocol
';
}
}
# 测试
telnet localhost 10004
9.2.proxy protocol协议与realip模块
proxy_protocol协议
- v1协议
- v2协议
stream处理proxy_protocol流程
- 连接建立成功,是否携带listen proxy protocol? 是
- 加入读定时器proxy_protocol timeout(默认30秒)
- 读取107字节(proxy protocol最大长度)
- 判断前12字节是否匹配V2协议的头部? 否
- 读取v1协议头部的真实IP地址
- 读取v2协议头部的真实IP地址
- 进入7个阶段的stream模块处理
读取proxy_protocol协议的超时控制
proxy_protocol_timeout 30s;
post_accept阶段: realip模块
通过proxy_protocol协议取出客户端真实地址,并写入remote_addr及remote_port变量。同时使用realip remote_addr和realip_remote_port保留TCP连接中获得的原始地址。
模块:ngx_stream_realip_module ,通过--with-stream_realip_module启用功能
set_real_ip_from address | CIDR | unix:;
例子
stream {
log_format basic '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
error_log logs/stream_error.log debug;
access_log logs/stream_access.log basic;
server {
listen 10002 proxy_protocol;
return '10002 server get ip: $remote_addr!\n';
}
server {
listen 10003 proxy_protocol;
return '10003 server get ip: $remote_addr!\n';
}
server {
listen 10004 proxy_protocol;
set_real_ip_from 127.0.0.1;
allow 202.112.144.236;
deny all;
return '10004 vars:
bytes_received: $bytes_received
bytes_sent: $bytes_sent
proxy_protocol_addr: $proxy_protocol_addr
proxy_protocol_port: $proxy_protocol_port
remote_addr: $remote_addr
remote_port: $remote_port
realip_remote_addr: $realip_remote_addr
realip_remote_port: $realip_remote_port
server_addr: $server_addr
server_port: $server_port
session_time: $session_time
status: $status
protocol: $protocol
';
}
}
# 测试
telnet localhost 10004
9.3.限并发连接、限IP、记日志
PREACCESS阶段的limit_conn模块
功能: 限制客户端的并发连接数。使用变量自定义限制依据,基于共享内存所有worker进程同时生效。 模块: ngx_stream_limit_conn_module,通过--without-stream_limit_conn_module禁用模块 limit_conn_zone key zone=name:size;
ACCESS阶段的access模块
功能:根据客户端地址 (realip模块可以修改地址)决定连接的访问权限
模块:ngx_stream_access_module,通过--without-stream_access_module禁用模块
allow address | CIDR | unix: | all deny address | CIDR | unix: | all;
log阶段: stream log模块
access_log path format [buffer=size] [gzip[=level]] [flush=time] [if=condition];
log_format name [escape=default|json|none] string ...;
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
9.4.stream四层反向代理 SSL下游流量
stream模块TLS/SSL应用场景
- 客户端 <-TLS/SSL-> nginx <-TLS/SSL-> 上游服务
- 客户端 <-TLS/SSL-> nginx <-TCP-> 上游服务
- 客户端 <-TCP-> nginx <-TLS/SSL-> 上游服务
stream中的ssl
功能: 使stream反向代理对下游支持TLS/SSL协议 模块: ngx_stream_ssl_module,默认不编译进nginx,通过--with-stream_ssl_module
stream ssl模块提供的变量 (1)
#安全套件
ssl_cipher #本次通讯选用的安全套件,例如ECDHE-RSA-AES128-GCM-SHA256
ssl_ciphers #客户端支持的所有安全套件
ssl_protocol #本次通讯选用的TLS版本,例如TLSv1.2
ssl_curves #客户端支持的圆曲线,例如secp384rl:secp521rl
#证书
ssl_clicnt_raw_cert #原始客户端证书内容
ssl_client_escaped_cert #返回客户端证书做urlencode编码后的内容
ssl_client_cert #对客户端证书每一行内容前加tab制表符空白,增强可读性
ssl_clicnt_fingerprint #客户端证书的SHA1指纹
STREAM SSL模块实战
客户端 <-HTTPS-> nginx <-HTTP-> 上游服务
注意:nginx使用stream四层反向代理的stream ssl module模块解析TLS协议
例子
server {
listen 4433;
resolver 114.114.114.114;
resolver timeout 60s;
proxy_pass $ssl_preread_server_name:443;
ssl_preread on;
}
server {
server_name localhost;
listen 4434 ssl;
# 设置自己的服务器证书 a 证书
ssl_certificate ./cert/a.crt;
ssl_certificate_key ./cert/a.key;
proxy_pass localhost:80;
}
server {
server_name localhost;
#root html/;
default_type text/plain;
listen 8320;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass ws://121.40.165.18:8800/;
}
}
# 通过浏览器访问 http://xx.xx.xx.xx:8320:
# 测试
tcpdump -i lo port 8320 -A -s 0 # 通过tcpdump 抓包查看日志
9.5.stream_preread 模块取出SSL关键信息
SSL PREREAD模块
模块 : stream_ssl_preread_module,使用--with-stream_ssl_preread_module启用模块 功能 :解析下游TLS证书中信息,以变量方式赋能其他模块
提供变量
- $ssl_preread_protocol :客户端支持的TLS版本中最高的版本,例如TLSv1.3
- $ssl_preread_server name : 从SNI中获取到的服务器域名
- $ssl_preread_alpn_protocols : 通过ALPN中获取到的客户端建议使用的协议,例如h2,http/1.1
流程
- POST_ACCEPT realip
- PREACCESS limt_conn
- ACCESS access
- SSL ssl
- PREREAD ssl_preread
- CONTENT return, stream_proxy
- LOG access_log
preread阶段: ssl preread模块
preread_buffer_size 16k; preread_timeout 30s; ssl_preread off;
STREAM SSL PREREAD模块实战
STREAM SSL模块实战
客户端 <-HTTPS-> nginx <-HTTPS-> 上游服务
基于stream_ssl_preread_module模块中取得的证书中域名,选择上游的服务
9.6.stream proxy四层反向代理的用法
反向代理stream_proxy模块
模块 ngx_stream_proxy_module,默认在Nginx中
功能
- 提供TCP/UDP协议的反向代理
- 支持与上游的连接使用TLS/SSL协议
- 支持与上游的连接使用proxy protocol协议
proxy模块对上下游的限速指令
proxy_download_rate 0; # 限制读取上游服务数据的速度( 客户端 -> nginx )
proxy_upload_rate 0; # 限制读取客户端数据的速度 ( nginx <- 上游 )
stream proxy模块实战
客户端 <-HTTP (proxy protocol)-> nginx <-HTTP (proxy protocol)-> nginx上游服务
客户端 <-HTTP-> nginx(加入proxy_protocol_on;配置) <-HTTP proxy protocol(各种slb)-> nginx上游服务
注意:nginx上游服务 使用 http模块listen_proxy_protocol
例子
stream {
server {
listen 9001 proxy_protocal;
location / {
return 200 '
proxy_protocol_addr: $proxy_protocol_addr
proxy_protocol_port: $proxy_protocol_port
';
}
}
}
http{
server {
listen 4435;
proxy_pass localhost:9001;
proxy_protocol on;
}
}
# 测试
telnet localhost:4435
curl localhost:4435
tcpdump -i lo port 9001 -A -s 0 # 通过tcpdump 抓包查看日志
10.UDP反向代理
UDP反向代理的理论依据
src 源, dst 目标
- 客户端 发送端口A
- nginx 监听端口B 发送端口C
- 上游服务 监听端口D
所有端口都是session
指定一次会话session中最多从客户端接收到多少报文就结束session。 (1.15.7非稳定版本)
- 仅会话结束时才会记录access日志.
- 同一个会话中,nginx使用同一端口连接上游服务
- 设置为0表示不限制,每次请求都会记录access日志
proxy_requests number;
指定对应一个请求报文,上游应返回多少个响应报文.与proxy timeout结合使用,控制上游服务是否不可用
proxy_responses number;
例子
stream {
server {
listen 4436 udp;
proxy_pass localhost:9999;
proxy_requests 1; # 1-3
proxy_reponses 2;
proxy_timeout 2s;
access_log logs/udp_access.log udplog;
}
}
# 测试
python client.py 4436
# 查看日志
tail -f udp_access.log
tcpdump -i lo port 9001 -A -s 0 # 通过tcpdump 抓包查看日志
11.透传IP地址的3个方案
透传IP地址
proxy_protocol协议
修改IP报文:步骤:1.修改IP报文中的源地址 2.修改路由规则
方案
- IP地址透传:经由nginx转发上游返回的报文给客户端(TCP/UDP)
- DSR: 上游直接发送报文给客户端 (仅UDP)
- 客户端: IP地址是A
- nginx :IP地址是B
- 上游: IP地址是C
IP地址透传
proxy_bind $remote_addr transparent;
调节上游服务所在主机上的网关为nginx所在主机:
# route del default gw 10.0.2.2 (原网关IP)
# route add default gw 172.16.0.1 (Nginx所在主机的IP)
调节nginx所在主机上的路由规则,使它把接收自上游的、目标I是客户端IP的报文转发nginx
# ip rule add fwmark 1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100
# iptables -t mangle -A PREROUTING -p tcp -s 172.16.0.0/28 --sport 80 -i MARK-set-xmark 0x1/0xffffffff
DSR方案
DSR上游服务直接向客户端回包
proxy_responses 0 # # 简化版ip透传,由上游服务直接将报文发送给客户端
proxy_bind $remote_addr:$remote_port transparent; #若上游服务在内网无公网ip,则可由nginx所在主机转发。 在上游服务所在主机上添加路由
route add default gw nginx-ip-address #允许操作系统转发ip报文
sysctl -w net.ipy4.ip_forward=10 # 转发时修改源地址为nginx所在主机的地址
问题
nginx检测不到上游服务是否回包,负载均衡策略受限