第二章【NGINX 开源功能】—— 七层反向代理(下)

本节目录

gRPC

grpc 是由 Google 开发的一种高性能、开源的远程过程调用(rpc)框架,它定义了一套 rpc 语义与编码规范,并使用 HTTP/2 作为传输协议,其基于 HTTP/2 作为传输层协议,支持多路复用、头部压缩、双向流等特性。所以严格来说,grpc 并不是一个底层网络协议,而是一个构建在 HTTP/2 之上的应用层 rpc 协议。

grpc 协议的用途现今十分广泛,微服务、移动端 app、IoT设备、多编程语言集成项目等,其中都有 gpc 的影子。

nginx 从 nginx-1.13.10 版本起,正式官方支持 grpc 反向代理:

由于 grpc 基于 HTTP/2 的,所以需要在编译时安装 --with-http_ssl_module--with-http_v2_module 模块。(基于 HTTP/3(quic)在 grpc 生态中还处于实验或非主流阶段)

nginx 复制代码
location /service {
    grpc_pass grpc://127.0.0.1:50051;			# 指定 grpc 后端地址,可以是 ip、域名、套接字、负载均衡组
#   grpc_pass grpcs://127.0.0.1:50051;			# 如果后端是 grpc 加密的,使用此项(location 中 grpc_pass 只能存在一个)

	grpc_bind 192.168.1.100;					# 设置连接后端时使用的本地 ip 地址(多网卡场景)

	grpc_connect_timeout 5s;					# 与后端建立连接的超时时间(默认 60s)
	grpc_send_timeout 10s;						# 向后端发送请求的超时(默认 60s)
	grpc_read_timeout 1h; 						# 从后端读取响应的超时(默认 60s)
	
	grpc_next_upstream error timeout http_500;	# 定义在错误、超时、状态码 500 的情况下将请求重发给下一个后端
	grpc_next_upstream_tries 3;					# 最多尝试三次
	grpc_next_upstream_timeout 15s;				# 三次尝试总时间不超过 15s

	grpc_set_header Authorization "Bearer $grpc_auth_token";		# 传输自定义 header,可用于传输 grpc 元数据
	grpc_set_header X-Request-ID $request_id;

	grpc_hide_header Server;					# 拒绝某些后端响应的 herder,提升安全
	grpc_hide_header X-Powered-By;

	grpc_pass_header X-Custom-Backend-Status;	# 允许 nginx 传递部分非标准 header 到后端

	grpc_ignore_headers  X-Accel-Redirect		# 忽略后端返回的某些 herder

	grpc_ssl_certificate     /etc/nginx/client.pem;		# nginx 作为 grpc 客户端去连接后端,需进行 mtls 认证
	grpc_ssl_certificate_key /etc/nginx/client.key;
	grpc_ssl_verify          on;						# on 需要验证后端服务器证书(默认 off)
	grpc_ssl_name grpc.ex.com;							# 验证后端服务器证书的主机名,后端存在多域多证书时必须写上
	grpc_ssl_trusted_certificate /etc/nginx/ca.pem;
	grpc_ssl_protocols TLSv1.2 TLSv1.3;					# 指定 tls 版本
	grpc_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;		# 指定加密算法
	grpc_ssl_session_reuse on;					# 开启 ssl 会话重用,可显著降低多次 tcp 握手的开销
	
	grpc_buffering on;							# 启用响应缓冲
	grpc_buffer_size 4k;						# 读取后端响应第一部分的缓冲区大小

	grpc_socket_keepalive on;					# 向后端 grpc 开启长连接,防止没有数据传输导致 tcp 被中断
	
	grpc_intercept_errors on;					# 拦截后端返回的错误码,交由 nginx 处理
	error_page 502 /grpc_error.html;
}

注意:

  • grpc 基于 HTTP/2 工作,请务必确保此 server 的 listen 443 http2 中包含 http2 字样;
  • 路径必须与 .proto 中定义的 service 和 method 完全匹配,否则将返回 404 或 UNIMPLEMENTED;
  • 对于流式应用,grpc_buffering 必须设置为 off,否则用户端会存在明显的卡顿感;
  • 如果同时配置了 grpc_read_timeoutgrpc_socket_keepalive on,请确保 grpc_read_timeout 的时间大于 grpc 自身的 keepalive 时间,否则 nginx 会切断看似闲置的长连接;
  • 若 grpc 元数据很大(如包含了很大的 token),则需要调整 http 级别的 large_client_header_buffers
  • 如果后端 grpc 使用了 SNI(多域名托管在同一 ip),那么 grpc_ssl_name 必须强制配置,如果后端的 SNI 和配置的域名不同,则 nginx 会拒绝;
  • 若开启了 grpc_intercept_errors on,那请确保你的 error_page 返回的也是符合 grpc 规范的响应,一般对于业务逻辑错误的,都建议关闭此项,让后端 grpc 的直接将错误透传给客户端,由客户端自行处理。

FastCGI

fastcgi 是一种用于 web 服务器与外部程序(如 php、python、go 等)通信的二进制协议。它的出现是为了解决传统 cgi 性能低下的问题(传统 cgi 会频繁的创建和销毁进程,进而消耗了巨大的 CPU 和内存资源)。

针对上述问题,fastcgi 引入了常驻进程的概念,当 web 服务启动时,fastcgi 进程管理器(如 php-fpm)就会预先启动一批进程,等待请求到来时,通过 socket 将数据传给已有的进程,但在处理完后进程并不退出,而是继续等待下一个请求。

fastcgi 现在几乎是 php 的专属产品了,另外 perl、超老旧的 python 等产品可能还在用(忽略不记),其他语言(如 java、go、c# 等)都自带高性能 web 服务器的,完全不需要中间再夹一个 fastcgi,所以其应用场景现在很窄了。[ 如果不是 php 还需要它,fastcgi 也早就入土了,和下面的 scgi 一样 ]

nginx 对 fastcgi 的支持几乎是与生俱来的;

nginx 复制代码
http {
	...
	fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=phpCache:100m inactive=60m;	# 设置 fastcgi 缓存
	...
}

location ~ \.php$ {
	root /var/www/html;
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    include fastcgi_params;
    fastcgi_index index.php;						# 当 uri 以 / 结尾时,等于是访问首页
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;		# 向fastcgi 进程传递变量
    
    fastcgi_connect_timeout 10s;					# 连接 fastcgi 服务器超过 10s ,则超时(默认 60s)
    fastcgi_send_timeout 30s;						# 向 fastcgi 发送请求超过 30s,则超时(默认 60s)
    fastcgi_read_timeout 1m;  						# fastcgi 响应间隔超过 1 分钟则超时(默认 60s)
    
    fastcgi_buffering on;							# 开启响应式缓冲,流式输出适合 off
    fastcgi_buffer_size 16k;						# 设置用于读取 herder 的缓冲区大小
	fastcgi_buffers 4 16k;							# 为每个连接分配缓冲区和大小

	fastcgi_request_buffering off;					# 允许大文件可以边上传边处理 

	fastcgi_next_upstream error timeout http_500;	# 功能和上面的 grpc 的此配置项功能一样
    fastcgi_next_upstream_tries 3;

	fastcgi_hide_header X-Powered-By;				# 隐藏后端返回的敏感信息头
	fastcgi_hide_header Server;

	fastcgi_ignore_client_abort on;					# 客户端断开连接后,继续处理异步任务(默认 off)
	
	fastcgi_cache_key "$scheme$request_method$host$request_uri";				# 设置每条缓存存储的唯一标识
	fastcgi_cache phpCache;							# 启用缓存
	fastcgi_cache_valid 200 302 10m;				# 指定不同状态码的缓存时间
	fastcgi_cache_valid 404 1m;	
	map $request_method $skip_cache {
    	default 0;
    	POST 1; 
	}
	fastcgi_cache_bypass $skip_cache;				# 对于某些请求需要跳过缓存

	fastcgi_intercept_errors on;					# 将 后端返回的 404 错误交给 nginx 来处理

	fastcgi_keep_conn on;							# 开启长连接减少握手
}

注意事项:(针对 php-fpm 的)

  • fastcgi_read_timeout 不要设置太长,除非你的后端是处理超大任务,最后确保 read_timeout 应该略大于 php-fpm 中 max_execution_time 的值;
  • fastcgi_keep_conn 如果开启,需要注意 fastcgi 后端可能需要没有任何限制短连接的配置,配合负载均衡模块中的 keepalive 指令效果会更好,而且需要确保 php-fpm 的配置文件(www.conf)中也支持长连接,否则此项无效;
  • fastcgi_request_buffering 如果不是处理超大文件或实现上传进度条之类的,请保持 on;
  • 如果请求 herder 很大(如超大 cookie),fastcgi_buffer_sizefastcgi_buffers 需要响应调大,而且要确保php-fpm 配置文件中的 max_input_timepost_max_size 与之相匹配;
  • fastcgi_ignore_client_abort 请谨慎开启,若无脑开启,当发生恶意攻击(大量请求并立即断开)时,后端进程可能会被占满且不会因为客户端断开而释放,导致服务器宕机。如果需要,必须配合限流(limit_req / limit_conn)使用。

uWSGI

在 2010--2020 年间,uwsgi 是 python web 部署的黄金标准,但截至目前,uwsgi 的使用量正在明显减少,尤其在新项目中已不再是主流选择,但它仍在大量遗留系统、传统企业应用和部分 django 项目中稳定运行(新项目中已逐渐被 gunicorn + uvicorn 取代)。现在的 uwsgi 已进入了维护模式了,它完成了自己的历史使命,不再是未来推荐的 sgi 标准了。

uwsgi 既是一个服务器,又定义了 uwsgi 协议,其运行速度还可以,但是配置起来很容易出错,文档也很杂乱;

nginx 复制代码
location / {
    uwsgi_pass unix:/tmp/uwsgi.sock;
  	include uwsgi_params; 								# 包含标准 uWSGI 参数映射文件(由 nginx 提供,写上就行)
 	uwsgi_param UWSGI_CHDIR /var/www/myapp;  		  	# 设置工作目录(uwsgi_param 可以自定义参数)
	uwsgi_param UWSGI_SCRIPT app:application; 			# 指定入口(旧式)
	uwsgi_param DJANGO_SETTINGS_MODULE myapp.settings;
	uwsgi_param Host $host;
    uwsgi_param X-Real-IP $remote_addr;
    uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
    uwsgi_param X-Forwarded-Proto $scheme;

	uwsgi_connect_timeout 10s;
    uwsgi_send_timeout 60s;
    uwsgi_read_timeout 300s;
    ...
}

uwsgi 的整体配置和上述的 fastcgi 等差不多,只是换个前缀即可。
对于新项目,建议转向 gunicorn + proxy_pass 或 uvicorn(asgi) 架构!!

SCGI

截至 2025 年,scgi 在实际生产环境中几乎已经没人使用了,它是一个理论简洁、实践上被时代淘汰的协议。虽然 nginx 中仍然保留了 scgi 模块,但却极少在示例或最佳实践中提及。

nginx 复制代码
server {
    listen 80;
    server_name localhost;

    location / {
    	scgi_pass 127.0.0.1:8080;
    	include scgi_params;
    	scgi_param REQUEST_METHOD $request_method;			# 自定义变量
    	scgi_param QUERY_STRING $query_string;
	}
}

注意:

  • scgi 在生产环境中已基本淘汰,请不要在新项目中再次使用它,即使项目很简单也不推荐(除非你是学习 [bushi, 这鬼东西现在谁还学它啊, 不理解但尊重 ])
  • 如果你在维护一个老系统且它还用了 scgi,虽然可继续运行,但建议迁移到现代架构上来;
  • 如果你在学习 web 协议,那么 了解 其原理即可,重点掌握 HTTP、fastcgi、asgi 等。

nginx 所有支持的协议总览(除开 HTTP):

协议 传输层 语言绑定 现状
grpc HTTP/2 java、go、c++、python 等主流语言都支持 必选
fastcgi TCP、Unix php 差点入土
uwsgi TCP、Unix python 土埋半截
scgi TCP 几乎没有 已经入土
相关推荐
prettyxian2 小时前
【linux】进程概念(2)Linux进程的生命密码:从fork到完全独立
linux·运维·服务器
Trouvaille ~2 小时前
【Linux】库制作与原理(一):静态库与动态库的制作使用
linux·运维·服务器·c语言·汇编·动静态库·编译链接
热爱专研AI的学妹2 小时前
Coze-AI 智能体平台:工作流如何成为智能体的 “自动化引擎”?解锁零代码落地新范式
运维·数据结构·人工智能·自动化
梦想的旅途22 小时前
从 0 到 1:构建外部群自动化的全链路监控大屏
运维·自动化
HIT_Weston2 小时前
73、【Ubuntu】【Hugo】搭建私人博客:Hugo&PaperMod 兼容问题
linux·运维·ubuntu
清平乐的技术专栏2 小时前
新电脑验机工具介绍及避坑指南
运维·电脑
xixiyuguang2 小时前
nginx tar离线安装 ubuntu22.04
运维·nginx
丁丁丁梦涛2 小时前
nginx解决域名代理到IP+端口的平台静态资源和接口地址问题
运维·tcp/ip·nginx
春日见2 小时前
如何提升手眼标定精度?
linux·运维·开发语言·数码相机·matlab