我目前正在尝试获取 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 { }
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
当我尝试运行 Python 客户端时,出现以下错误:
回溯(最近一次调用最后):文件“gRPCExampleClient.py”,第10行,在stub.Insert(example_pb2.InsertRequest(Value = 155))文件“C:\ Python38 \ lib \ site-packages \ grpc_channel.py”中,第923行,在 调用 中返回_end_unary_response_blocking(state,call,False,None)文件“C:\ Python38 \ lib \ site-packages \ grpc_channel.py”,第826行,在_end_unary_response_blocking中引发_InactiveRpcError(state)grpc._channel._InactiveRpcError: <_InactiveRpcError 的 RPC 终止于:status = StatusCode.UNAVAILABLE 详细信息 = "无法连接到所有地址" debug_error_string = "{"created":"@1612810257.299000000","description":"无法选择子通道","file" :"src/core/ext/filters/client_channel/client_channel.cc","file_line":5391,"referenced_errors":[{"created":"@1612810257.299000000","description":"无法连接到所有地址" ,“文件”:“src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc”,“file_line”:398,“grpc_status”:14}]}"
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);
}
}
Run Code Online (Sandbox Code Playgroud)
with grpc.insecure_channel('https://localhost:5001') as channel:)。执行此操作时,我收到以下错误消息:回溯(最近一次调用最后):文件“gRPCExampleClient.py”,第10行,在stub.Insert(example_pb2.InsertRequest(Value = 155))文件“C:\ Python38 \ lib \ site-packages \ grpc_channel.py”中,第923行,在 调用 中返回_end_unary_response_blocking(state,call,False,None)文件“C:\ Python38 \ lib \ site-packages \ grpc_channel.py”,第826行,在_end_unary_response_blocking中引发_InactiveRpcError(state)grpc._channel._InactiveRpcError: <_InactiveRpcError 的 RPC 终止于:status = StatusCode.UNAVAILABLE 详细信息 = “服务的 DNS 解析失败:https://localhost:5001” debug_error_string = “{”created”:“@1612809227.889000000”,“description”:“解析器瞬态失败","文件":"src/core/ext/filters/client_channel/client_channel.cc","file_line":2141,"referenced_errors":[{"创建":"@1612809227.889000000","描述":"DNS服务解析失败:https://localhost:5001","file":"src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc","file_line":201,"grpc_status": 14,"referenced_errors":[{"已创建":"@1612809227.889000000","描述":"操作系统错误","文件":"src/core/lib/iomgr/resolve_address_windows.cc","file_line":95, "os_error":"没有这样的主机已知。\r\n","syscall":"getaddrinfo","wsa_error":11001}]}]}"
GRPC_DNS_RESOLVER为native. 遗憾的是错误保持不变from os import environ
environ["GRPC_DNS_RESOLVER"] = "native"
Run Code Online (Sandbox Code Playgroud)
127.0.0.1,0.0.0.0有或没有https://方案而不是localhostdns://作为默认值时。我能够通过启用environ["GRPC_TRACE"] = "api,client_channel_routing,cares_resolver"和确认这一点 environ["GRPC_VERBOSITY"] = "debug"
https是我应该重点解决的错误,因为它dns://可能是可以解析的,但一开始就没有任何意义。我希望我只是错过了一些明显的东西,感谢您的阅读和考虑提供帮助。
我终于弄明白了。根本问题是我的服务器配置不匹配。由于 C# 客户端会自动检测 dotnet 设置的 ASP.NET 核心开发证书,因此它将能够安全地连接到服务器。所有其他脚本语言(例如 python 和 go)将无法执行此操作并拒绝连接,因为无法验证证书。(这是完全正确的,它仍然只是一个用于调试的自签名证书)
简而言之:您无法不安全地连接到 gRPC 服务器,除非明确将其设置为通过 HTTP/2 提供不安全的 http 请求。
所以我们现在有两个选择:
为了导出我使用的 ASP.NET Core 开发证书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))
Run Code Online (Sandbox Code Playgroud)
我也在 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)
}
}
Run Code Online (Sandbox Code Playgroud)
这是我找到的第一个解决方案,但我不建议使用它,因为它只是一种解决方法。通过配置我的服务器应用程序,我能够通过配置 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>();
});
Run Code Online (Sandbox Code Playgroud)