使用 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/6vnxiNax

Nginx 配置

我们确实尝试了很多配置——它们似乎并没有改变 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 通信卡住的主要内容,如果未能解决你的问题,请参考以下文章

gRPC 异步服务死锁/永远卡住

使用 nginx 终止 grpc 流量

gRPC 在.Net core中使用gRPC

GRPC+Nginx

nginx 1.13.10开始支持gRPC

你可以使用 gRPC/gRPC-Web 在 Azure 上托管的微服务之间进行通信吗?