因为Grpc采用HTTP/2作为通信协议,默认采用LTS/SSL加密方式传输,比如使用.net core启动一个服务端(被调用方)时:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { options.ListenAnyIP(5000, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps("xxxxx.pfx", "password"); }); }); webBuilder.UseStartup<Startup>(); });
其中使用UseHttps方法添加证书和秘钥。
但是,有时候,比如开发阶段,我们可能没有证书,或者是一个自己制作的临时测试证书,那么在客户端(调用方)调用是可能就会出现下面的异常:
Call failed with gRPC error status. Status code: 'Internal', Message: 'Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: The remote certificate is invalid according to the validation procedure.'. fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: The remote certificate is invalid according to the validation procedure.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure. at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception) at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest) --- End of stack trace from previous location where exception was thrown --- at System.Net.Security.SslStream.ThrowIfExceptional() at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult) at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result) at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult) ..........
然而我们可能没有办法得到有效的证书,这时,我们有两个办法:
1、使用http协议
想想,我们为什么要使用Grpc?因为高性能,高效率,简单易用吧,但是https相比http就是多个加密的过程,这可能会有一定的性能损失(一般可忽略)。
而一般的,我们在微服务架构中使用Grpc比较多,而微服务一般部署在我们自己的一个子网下,这也就没必要使用https了吧?
首先我们知道,Grpc是基于HTTP/2作为通信协议的,而HTTP/2默认是基于LTS/SSL加密技术的,或者说默认需要https协议支持(https=http+lts/ssl),而HTTP/2又支持明文传输,即对http也是支持,但是一般需要我们自己去设置。
当我们使用Grpc时,又不去改变这个默认行为,那可能就会导致上面的报错。
在.net core开发中,Grpc要支持http,我们需要显示的指定不需要TLS支持,官方给出的做法是添加如下配置(比如客户端(调用方)在ConfigureServices添加):
public void ConfigureServices(IServiceCollection services) { //显式的指定HTTP/2不需要TLS支持 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true); services.AddGrpcClient<Greeter.GreeterClient>(nameof(Greeter.GreeterClient), options => { options.Address = new Uri("http://localhost:5000"); }); ... }
2、调用时不对证书进行验证
如果是控制台程序,我们可以这么做:
public static void Main(string[] args) { var channel = GrpcChannel.ForAddress("https://localhost:5000", new GrpcChannelOptions() { HttpClient = null, HttpHandler = new HttpClientHandler { //方法一 ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator //方法二 //ServerCertificateCustomValidationCallback = (a, b, c, d) => true } }); var client = new Greeter.GreeterClient(channel); var result = client.SayHello(new HelloRequest() { Name = "Grpc" }); }
其中 HttpClientHandler 的 ServerCertificateCustomValidationCallback 是对证书的自定义验证,上面给出了两种方式验证。
如果是.net core的webmvc或者webapi程序,因为.net core 3.x开始已经支持了Grpc的引入,所以我只需要在ConfigureServices中注入Grpc的客户端是进行设置:
public void ConfigureServices(IServiceCollection services) { services.AddGrpcClient<Greeter.GreeterClient>(nameof(Greeter.GreeterClient), options => { options.Address = new Uri("https://localhost:5000"); }).ConfigurePrimaryHttpMessageHandler(() => { return new HttpClientHandler { //方法一 ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator //方法二 //ServerCertificateCustomValidationCallback = (a, b, c, d) => true }; }); ... }
因为.net core3.x中Grpc的使用是基于它的HttpClient机制,比如 AddGrpcClient 方法返回的就是一个 IHttpClientBuilder 接口对象,上面的配置我们还可以这么写:
public void ConfigureServices(IServiceCollection services) { services.AddGrpcClient<Greeter.GreeterClient>(nameof(Greeter.GreeterClient)); services.AddHttpClient(nameof(Greeter.GreeterClient), httpClient => { httpClient.BaseAddress = new Uri("https://localhost:5000"); }).ConfigurePrimaryHttpMessageHandler(() => { return new HttpClientHandler { //方法一 ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator //方法二 //ServerCertificateCustomValidationCallback = (a, b, c, d) => true }; }); ... }
总之,不管怎么调用,机制都是一样的,最终都是像上面的客户端调用一样去创建Client,只要能理解就好了。
- certificate validation according procedure invalidcertificate validation according procedure certificate expired docker valid certificate validate because contain according intellectual according the evidences categorize according leetcode criteria procedure procedures lmysqlcppconn procedure mysql call determine procedure parameter metadata