使用 Nginx 的 gRPC 通信卡住
Posted
技术标签:
【中文标题】使用 Nginx 的 gRPC 通信卡住【英文标题】:gRPC communication stucks using Nginx 【发布时间】:2021-08-09 07:41:32 【问题描述】:基本场景
我们有一个简单的客户端服务器通信,就像在 https://grpc.io/docs/languages/java/quickstart/ 上提供的快速入门示例中一样 - HelloRequest 有一个不同之处:我们重复“字符串名称 = 1”以创建更大的请求。
// The greeting service definition.
service Greeter
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply)
// The request message containing the user's name.
message HelloRequest
repeated string name = 1;
// The response message containing the greetings
message HelloReply
string message = 1;
我们用 C# 和 Java 构建了一个客户端和一个服务器实现。服务器和客户端可以相互通信,从C#到C#,从C#到Java,从Java到C#等等。不错:)
无需 Nginx 的通信
数据流是这样的:Client =Request=> Server =Reply=> Client
案例 1.1:客户端向服务器发送带有 500 个字符串的 HelloRequest,服务器回复,客户端打印结果。 案例 1.2:客户端向服务器发送带有 1000 个字符串的 HelloRequest,……(部分同上)1.1 和 1.2 都在工作。不错。
与 Nginx 的通信
nginx 用作客户端和服务器之间的反向代理(https://www.nginx.com/resources/glossary/reverse-proxy-server/,https://en.wikipedia.org/wiki/Proxy_server#Reverse_proxies)
所以Dataflow是这样扩展的:Client =Request=> Nginx =Request=> Server =Reply=> Nginx =Reply=> Client
再次,上面的案例
案例 2.1:客户端发送 .... 到服务器,客户端打印结果。 案例 2.2:客户端发送 .... 到服务器并 FAILES 并带有 Grpc 超时。2.1 正在运行。还是不错的。案例 2.2 卡住了。
错误和调试消息
在 Nginx-Error.log 中
2021/08/06 09:02:41 [error] 29880#39568: *10 upstream timed out (10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond) while sending request to upstream, client: 127.0.0.1, server: , request: "POST /greet.Greeter/SayHello HTTP/2.0", upstream: "grpcs://127.0.0.1:5001", host: "localhost:5002"
已启用调试消息的 Nginx
还有一个警告:
2021/08/06 09:02:41 [debug] 29880#39568: *10 event timer del: 684: 440756309
2021/08/06 09:02:41 [debug] 29880#39568: *10 http upstream request: "/greet.Greeter/SayHello?"
2021/08/06 09:02:41 [debug] 29880#39568: *10 http upstream send request handler
2021/08/06 09:02:41 [debug] 29880#39568: *10 http next upstream, 4
2021/08/06 09:02:41 [debug] 29880#39568: *10 free rr peer 2 4
2021/08/06 09:02:41 [warn] 29880#39568: *10 upstream server temporarily disabled while sending request to upstream, client: 127.0.0.1, server: , request: "POST /greet.Greeter/SayHello HTTP/2.0", upstream: "grpcs://127.0.0.1:5001", host: "localhost:5002"
2021/08/06 09:02:41 [debug] 29880#39568: *10 free rr peer failed: 02811C78 0
2021/08/06 09:02:41 [error] 29880#39568: *10 upstream timed out…. See above
客户端中的 C# 错误消息
Grpc.Core.RpcException: 'Status(StatusCode="Unavailable", Detail="Bad gRPC response. HTTP status code: 504")'
您可以在此处找到 Nginx 调试日志:
用于有 500 个条目的工作请求https://pastebin.com/q54T55vR 对于不工作的请求 1000 个条目https://pastebin.com/6vnxiNaxNginx 配置
我们确实尝试了很多配置——它们似乎并没有改变 Nginx 的行为。我们已经在多种不同的安排中对它们进行了测试,并在不同区域(服务器、http、位置)有选择地测试了每种配置。
最基本的是这样的:
worker_processes 1;
events
worker_connections 1024;
http
include mime.types;
ssl_certificate cert-self.crt;
ssl_certificate_key cert-self.key;
error_log logs/customError.log debug;
server
listen 5002 ssl http2;
location /Shared.Contract.TestService
grpc_send_timeout 90s;
client_max_body_size 0;
grpc_pass grpcs://localhost:5001;
location /greet.Greeter
grpc_send_timeout 90s;
client_max_body_size 0;
grpc_pass grpcs://localhost:5001;
环境
我们使用的是 Windows 10 机器,在大多数情况下是专用硬件,在某些情况下是来自 azure 的 VM。没有容器化。客户端、服务器和 Nginx 运行在同一台主机上。 我们使用的是通过 OpenSSL 创建的自签名证书。
资源
您可以在https://github.com/xTeare/GrpcAndNGINX 找到 C#(用于 Visual Studio)和 Java(用于 Netbeans / Maven)解决方案@ 在 VS 解决方案中,您应该主要使用 Projects ProtoClient 和 ProtoServer。 Client 和 Server 项目使用的是 C# 的 code 1st 方法,它们与 Java 项目不兼容。
似乎 Github 丢弃了证书或私钥。这就是为什么您可以找到 C# 和 Java 的 CertsAndKeys.zip。或者查看https://github.com/xTeare/GrpcAndNGINX#certificate 来创建新的。
其他信息/历史记录
我们最初使用 C# (https://docs.microsoft.com/en-us/aspnet/core/grpc/code-first?view=aspnetcore-5.0) 中的代码优先 gRPC 方法遇到了这个问题。 在我们的开发环境中,几乎所有 grpc 请求都能正常工作。但是,如果请求变得太大,它们就会停止工作。我们不知道确切的大小,但它必须小于 180kb。
C# 示例合同
那里的通信合约是基于 C# 接口的(可能不像使用协议缓冲区那样可互操作,但非常方便)(也包含在 Github repo 中)
[ServiceContract]
public interface IGreetServiceCode1st
[OperationContract]
ValueTask<Model.HelloReplyCode1st> SaveResultsAsync(Model.HelloRequestCode1st requestCode);
[DataContract]
public class HelloRequestCode1st
[DataMember(Order = 1)]
public List<string> Names get; set;
[DataContract]
public class HelloReplyCode1st
[DataMember(Order = 1)]
public string Message get; set;
[DataMember(Order = 2)]
public bool Success get; set;
使用客户端流式传输也会出现问题。
【问题讨论】:
【参考方案1】:Nginx 中有一个错误:- 见 https://trac.nginx.org/nginx/ticket/2229
该错误已在 1.21.2 版本中解决: “如果使用 select、poll 或 /dev/poll 方法,与 gRPC 后端的 SSL 连接可能会挂起。” (http://nginx.org/en/CHANGES)
【讨论】:
请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。 我添加了一些信息。还有什么应该包括的吗?以上是关于使用 Nginx 的 gRPC 通信卡住的主要内容,如果未能解决你的问题,请参考以下文章