【问题标题】:gRPC failed to connect to all addresses or DNS resolution failed for servicegRPC 无法连接到所有地址或服务的 DNS 解析失败
【发布时间】:2021-05-12 10:23:49
【问题描述】:

我目前正在尝试获取 gRPC 工作的示例。我使用 C# Asp.NET Core WebApi 作为服务器,并尝试通过 Python 客户端连接到它。

资源

我的原型文件:

syntax = "proto3";

service Example {
  rpc Insert (InsertRequest) returns (Empty);
  rpc Update (UpdateRequest) returns (Empty);
  rpc Delete (DeleteRequest) returns (Empty);
}

message InsertRequest {
    int32 Value = 1;
}

message UpdateRequest {
  string Id = 1;
  int32 Value = 2;
}

message DeleteRequest {
  string Id = 1;
}

message Empty { }

Python 客户端:

import grpc

import example_pb2
import example_pb2_grpc

with grpc.insecure_channel('localhost:5001') as channel:
    stub = example_pb2_grpc.ExampleStub(channel)
    stub.Insert(example_pb2.InsertRequest(Value = 155))

问题

当我尝试运行我的 Python 客户端时,我收到以下错误:

Traceback(最近一次调用最后一次):文件“gRPCExampleClient.py”,行 10、在 stub.Insert(example_pb2.InsertRequest(Value = 155)) 文件“C:\Python38\lib\site-packages\grpc_channel.py”,第 923 行,在 致电 return _end_unary_response_blocking(state, call, False, None) 文件“C:\Python38\lib\site-packages\grpc_channel.py”,第 826 行,在 _end_unary_response_blocking raise _InactiveRpcError(state) grpc._channel._InactiveRpcError: <_inactiverpcerror of rpc statuscode.unavailable details="无法连接到所有地址" debug_error_string="{" created subchannel>

到目前为止我尝试了什么

  • 为我的 Kestrel 服务器显式启用 HTTP/2
  • 已验证服务器正在使用 C# gRPC 客户端
    • C# 客户端使用与 Python 客户端相同的 proto 文件(C# 客户端按预期工作,可以与服务器通信)
    class Program
    {
        static void Main(string[] args)
        {
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new Example.ExampleClient(channel);

            client.Insert(new InsertRequest() { Value = 155 });

            Console.ReadKey(true);
        }
    }
  • 尝试在我的 Python 客户端 (with grpc.insecure_channel('https://localhost:5001') as channel:) 中使用“https://”方案。执行此操作时,我收到以下错误消息:

Traceback(最近一次调用最后一次):文件 “gRPCExampleClient.py”,第 10 行,在 stub.Insert(example_pb2.InsertRequest(Value = 155)) 文件“C:\Python38\lib\site-packages\grpc_channel.py”,第 923 行,在 致电 return _end_unary_response_blocking(state, call, False, None) 文件“C:\Python38\lib\site-packages\grpc_channel.py”,第 826 行,在 _end_unary_response_blocking raise _InactiveRpcError(state) grpc._channel._InactiveRpcError: <_inactiverpcerror of rpc statuscode.unavailable details="服务的 DNS 解析失败:https://localhost:5001" debug_error_string="{" created https: :>

  • 在此之后,我尝试将环境变量GRPC_DNS_RESOLVER 设置为native。遗憾的是,错误保持不变
from os import environ

environ["GRPC_DNS_RESOLVER"] = "native"
  • 尝试使用127.0.0.10.0.0.0https:// 方案而不是localhost

其他信息

我希望我只是遗漏了一些明显的东西,感谢您阅读并考虑提供帮助。

【问题讨论】:

    标签: python c# grpc


    【解决方案1】:

    我终于明白了。根本问题是我的服务器配置不匹配。由于 C# 客户端会自动检测 dotnet 设置的 ASP.NET 核心开发证书,因此它将能够安全地连接到服务器。由于无法验证证书,所有其他脚本语言(如 python 和 go)将无法执行此操作并拒绝连接。 (这是完全正确的,它仍然只是一个用于调试的自签名证书)

    简而言之:除非明确设置为通过 HTTP/2 服务不安全的 http 请求,否则您不能不安全地连接到 gRPC 服务器。

    所以我们现在有两个选择:

    1. 明确向我们的脚本客户端提供证书公钥,以便他们知道他们可以信任我们的服务器
    2. 使用 HTTP/2 以非 SSL 模式启动我们的服务器

    1

    为了导出我的 asp.net 核心开发证书,我使用了certmgr。 使用此工具,我们可以将开发证书的公钥部分导出为 .cer 文件。 在此之后,我们只需要告诉我们的客户端建立一个安全连接并根据我们刚刚导出的公钥进行验证:

    with open('../dev-cert.cer', 'rb') as f: #manually import public key of localhost ASP.NET Core dev certioficate (exported with certmgr)
            credentials = grpc.ssl_channel_credentials(f.read())
    with grpc.secure_channel('localhost:5001', credentials) as channel:
        stub = example_pb2_grpc.ExampleStub(channel)
        stub.Insert(example_pb2.InsertRequest(Value = 155))
    

    我也在 go 中测试了所有这些(以验证它不仅仅是一个 python 库问题)

    func main() {
        creds, err := credentials.NewClientTLSFromFile("../dev-cert.cer", "")
        if err != nil {
            log.Fatalf("could not process the credentials: %v", err)
        }
    
        conn, err := grpc.Dial("localhost:5001", grpc.WithTransportCredentials(creds))
    
        if err != nil {
            log.Fatalf("did not connect: %s", err)
        }
        defer conn.Close()
    
        request := example_grpc_client.InsertRequest{
            Value: 123,
        }
    
        client := example_grpc_client.NewExampleClient(conn)
    
        _, err = client.Insert(context.Background(), &request)
        if err != nil {
            log.Fatalf("error sending message: %s", err)
        }
    }
    

    2

    这是我找到的第一个解决方案,但我不建议使用它,因为它只是一种解决方法。 通过配置我的服务器应用程序,我能够通过配置 kestrel 在 HTTP/2 中启动不安全的连接来获得不安全的连接:

            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.ConfigureKestrel(options => 
                        {
                            options.ListenLocalhost(5001, o => o.Protocols = HttpProtocols.Http2);
                        });
    
                        webBuilder.UseStartup<Startup>();
                    });
    

    【讨论】:

      【解决方案2】:

      请看https://github.com/grpc/grpc/blob/master/doc/naming.md

      “https”的使用不正确

      【讨论】:

      • 感谢分享。但我确实发现了问题,你会发现 https 方案并不是全部。
      猜你喜欢
      • 2021-12-06
      • 2016-04-08
      • 2020-09-18
      • 2020-05-21
      • 2020-05-06
      • 2019-12-27
      • 1970-01-01
      • 1970-01-01
      • 2019-09-15
      相关资源
      最近更新 更多