ASP.NET Core: Khắc phục sự cố gRPC trên .NET
Trong bài viết này
- Không khớp giữa cấu hình SSL/TLS của máy khách và dịch vụ
- Gọi dịch vụ gRPC bằng chứng chỉ không đáng tin cậy/không hợp lệ
- Gọi các dịch vụ gRPC không an toàn bằng máy khách .NET Core
- Không thể khởi động ứng dụng gRPC ASP.NET Core trên macOS
- Nội dung gRPC C# không phải là mã được tạo từ các file .proto
- Các dự án WPF không thể tạo nội dung gRPC C# từ các file .proto
- Gọi các dịch vụ gRPC được lưu trữ trong thư mục con
- Định cấu hình máy khách gRPC để sử dụng HTTP/3
- Xây dựng gRPC trên Alpine Linux
Ghi chú
Đây không phải là phiên bản mới nhất của bài viết này. Để biết bản phát hành hiện tại, hãy xem phiên bản .NET 8 của bài viết này.
Tài liệu này thảo luận về các vấn đề thường gặp khi phát triển ứng dụng gRPC trên .NET.
Không khớp giữa cấu hình SSL/TLS của máy khách và dịch vụ
Các sample và mẫu gRPC sử dụng Bảo mật lớp truyền tải (TLS) để bảo mật các dịch vụ gRPC theo mặc định. Máy khách gRPC cần sử dụng kết nối an toàn để gọi thành công các dịch vụ gRPC được bảo mật.
Bạn có thể xác minh dịch vụ gRPC ASP.NET Core đang sử dụng TLS trong nhật ký được ghi khi khởi động ứng dụng. Dịch vụ sẽ lắng nghe trên điểm cuối HTTPS:
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
Máy khách .NET Core phải sử dụng https
địa chỉ máy chủ để thực hiện cuộc gọi với kết nối an toàn:
static async Task Main(string[] args)
{
// The port number(5001) must match the port of the gRPC server.
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greet.GreeterClient(channel);
}
Tất cả việc triển khai ứng dụng khách gRPC đều hỗ trợ TLS. Máy khách gRPC từ các ngôn ngữ khác thường yêu cầu kênh được định cấu hình bằng SslCredentials
. SslCredentials
chỉ định chứng chỉ mà máy khách sẽ sử dụng và nó phải được sử dụng thay vì thông tin xác thực không an toàn. Để biết ví dụ về cách định cấu hình triển khai máy khách gRPC khác nhau để sử dụng TLS, hãy xem Xác thực gRPC.
Gọi dịch vụ gRPC bằng chứng chỉ không đáng tin cậy/không hợp lệ
Máy khách .NET gRPC yêu cầu dịch vụ phải có chứng chỉ tin cậy. Thông báo lỗi sau được trả về khi gọi dịch vụ gRPC mà không có chứng chỉ tin cậy:
Unhandled exception. 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.
Bạn có thể gặp lỗi này nếu bạn đang kiểm tra ứng dụng của mình cục bộ và chứng chỉ phát triển ASP.NET Core HTTPS không đáng tin cậy. Để biết hướng dẫn khắc phục sự cố này, hãy xem Tin cậy chứng chỉ phát triển ASP.NET Core HTTPS trên Windows và macOS.
Nếu bạn đang gọi dịch vụ gRPC trên một máy khác và không thể tin cậy vào chứng chỉ thì máy khách gRPC có thể được định cấu hình để bỏ qua chứng chỉ không hợp lệ. Đoạn mã sau sử dụng HttpClientHandler.ServerCertificateCustomValidationCallback để cho phép các lời gọi không có chứng chỉ tin cậy:
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var channel = GrpcChannel.ForAddress("https://localhost:5001",
new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);
Factory máy khách gRPC cho phép các lời gọi mà không cần chứng chỉ đáng tin cậy. Sử dụng phương thức tiện ích mở rộng configurePrimaryHttpMessageHandler để định cấu hình trình xử lý trên máy khách:
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
return handler;
});
Cảnh báo
Chỉ nên sử dụng chứng chỉ không đáng tin cậy trong quá trình phát triển ứng dụng. Ứng dụng production phải luôn sử dụng chứng chỉ hợp lệ.
Gọi các dịch vụ gRPC không an toàn bằng máy khách .NET Core
Máy khách .NET gRPC có thể gọi các dịch vụ gRPC không an toàn bằng cách chỉ định http
trong địa chỉ máy chủ. Ví dụ, GrpcChannel.ForAddress("http://localhost:5000")
.
Có một số yêu cầu bổ sung để gọi các dịch vụ gRPC không an toàn tùy thuộc vào phiên bản .NET mà ứng dụng đang sử dụng:
-
.NET 5 trở lên yêu cầu Grpc.Net.Client phiên bản 2.32.0 trở lên.
-
.NET Core 3.x yêu cầu cấu hình bổ sung. Ứng dụng phải đặt
System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport
chuyển thànhtrue
:// This switch must be set before creating the GrpcChannel/HttpClient. AppContext.SetSwitch( "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); // The port number(5000) must match the port of the gRPC server. var channel = GrpcChannel.ForAddress("http://localhost:5000"); var client = new Greet.GreeterClient(channel);
Việc System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport
chuyển đổi chỉ được yêu cầu đối với .NET Core 3.x. Nó không làm gì trong .NET 5 và không bắt buộc.
Quan trọng
Các dịch vụ gRPC không an toàn phải được lưu trữ trên cổng chỉ HTTP/2. Để biết thêm thông tin, hãy xem Đàm phán giao thức ASP.NET Core.
Không thể khởi động ứng dụng gRPC ASP.NET Core trên macOS
Kestrel không hỗ trợ HTTP/2 với TLS trên macOS trước .NET 8. Các sample và mẫu ASP.NET Core gRPC sử dụng TLS theo mặc định. Bạn sẽ thấy thông báo lỗi sau khi cố gắng khởi động máy chủ gRPC:
Không thể liên kết với https://localhost:5001 trên giao diện loopback IPv4: 'HTTP/2 qua TLS không được hỗ trợ trên macOS do thiếu hỗ trợ ALPN.'.
Để khắc phục sự cố này trong .NET 7 trở về trước, hãy đặt cấu hình Kestrel và máy khách gRPC để sử dụng HTTP/2 mà không cần TLS. Bạn chỉ nên làm điều này trong quá trình phát triển. Không sử dụng TLS sẽ dẫn đến việc gửi tin nhắn gRPC mà không được mã hóa.
Kestrel phải định cấu hình điểm cuối HTTP/2 không có TLS trong Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
// Setup a HTTP/2 endpoint without TLS.
options.ListenLocalhost(<5287>, o => o.Protocols =
HttpProtocols.Http2);
});
- Trong đoạn code trên, thay thế số cổng localhost
5287
bằng số cổngHTTP
(không phảiHTTPS
) được chỉ định trongProperties/launchSettings.json
với dự án dịch vụ gRPC.
Khi điểm cuối HTTP/2 được định cấu hình không có TLS, thì ListenOptions.Protocols của điểm cuối phải được đặt thành HttpProtocols.Http2
. HttpProtocols.Http1AndHttp2
sẽ không thể sử dụng được vì TLS được yêu cầu để đàm phán HTTP/2. Nếu không có TLS, tất cả các kết nối đến điểm cuối mặc định là HTTP/1.1 và lệnh gọi gRPC đều không thành công.
Máy khách gRPC cũng phải được cấu hình để không sử dụng TLS. Để biết thêm thông tin, hãy xem phần Gọi các dịch vụ gRPC không an toàn bằng ứng dụng khách .NET Core.
Cảnh báo
HTTP/2 không có TLS chỉ nên được sử dụng trong quá trình phát triển ứng dụng. Các ứng dụng production phải luôn sử dụng bảo mật truyền tải. Để biết thêm thông tin, hãy xem Những cân nhắc về bảo mật trong gRPC dành cho ASP.NET Core.
Nội dung gRPC C# không phải là mã được tạo từ các file .proto
Việc tạo mã gRPC của các máy khách cụ thể và các lớp cơ sở dịch vụ yêu cầu các tệp và công cụ protobuf phải được tham chiếu từ một dự án. Bạn phải bao gồm:
- Các file
.proto
bạn muốn sử dụng trong nhóm item<Protobuf>
. Các file .proto đã import phải được tham chiếu bởi dự án. - Tham chiếu gói đến gói công cụ gRPC Grpc.Tools.
Để biết thêm thông tin về cách tạo nội dung gRPC C#, hãy xem các dịch vụ gRPC với C#.
Một ứng dụng web ASP.NET Core lưu trữ các dịch vụ gRPC chỉ cần tạo lớp cơ sở dịch vụ:
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
Máy khách gRPC thực hiện lệnh gọi gRPC chỉ cần tạo ứng dụng khách cụ thể:
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
Các dự án WPF không thể tạo nội dung gRPC C# từ các file .proto
Các dự án WPF có một sự cố đã biết khiến việc tạo mã gRPC không hoạt động bình thường. Bất kỳ loại gRPC nào được tạo trong dự án WPF bằng cách tham chiếu các file Grpc.Tools
và .proto
sẽ tạo ra lỗi biên dịch khi sử dụng:
error CS0246: The type or namespace name 'MyGrpcServices' could not be found (are you missing a using directive or an assembly reference?)
Bạn có thể giải quyết vấn đề này bằng cách:
- Tạo một dự án thư viện lớp .NET Core mới.
- Trong dự án mới, hãy thêm các tham chiếu để cho phép tạo mã C# từ các file .proto:
- Thêm các tài liệu tham khảo gói sau:
- Thêm file .proto vào nhóm mục <Protobuf>.
- Trong ứng dụng WPF, thêm tham chiếu đến dự án mới.
Ứng dụng WPF có thể sử dụng các loại gRPC được tạo từ dự án thư viện lớp mới.
Gọi các dịch vụ gRPC được lưu trữ trong thư mục con
Cảnh báo
Nhiều công cụ gRPC của bên thứ ba không hỗ trợ các dịch vụ được lưu trữ trong thư mục con. Hãy cân nhắc việc tìm cách lưu trữ gRPC làm thư mục gốc.
Thành phần đường dẫn của địa chỉ kênh gRPC bị bỏ qua khi thực hiện lệnh gọi gRPC. Ví dụ: GrpcChannel.ForAddress("https://localhost:5001/ignored_path")
sẽ không sử dụng ignored_path
khi định tuyến các lời gọi gRPC cho dịch vụ.
Đường dẫn địa chỉ bị bỏ qua vì gRPC có cấu trúc địa chỉ quy định, được chuẩn hóa. Địa chỉ gRPC kết hợp tên gói, dịch vụ và phương thức: https://localhost:5001/PackageName.ServiceName/MethodName
.
Có một số trường hợp khi ứng dụng cần bao gồm đường dẫn có lệnh gọi gRPC. Ví dụ: khi ứng dụng ASP.NET Core gRPC được lưu trữ trong thư mục IIS và thư mục đó cần được đưa vào yêu cầu. Khi cần có một đường dẫn, nó có thể được thêm vào lệnh gọi gRPC bằng cách sử dụng tùy chỉnh SubdirectoryHandler
được chỉ định như bên dưới:
/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
private readonly string _subdirectory;
public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
: base(innerHandler)
{
_subdirectory = subdirectory;
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var old = request.RequestUri;
var url = $"{old.Scheme}://{old.Host}:{old.Port}";
url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
request.RequestUri = new Uri(url, UriKind.Absolute);
return base.SendAsync(request, cancellationToken);
}
}
SubdirectoryHandler
được sử dụng khi kênh gRPC được tạo.
var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
Đoạn code trên:
- Tạo một
SubdirectoryHandler
với đường dẫn/MyApp
. - Định cấu hình kênh để sử dụng
SubdirectoryHandler
. - Gọi dịch vụ gRPC bằng
SayHelloAsync
. Lời gọi gRPC được gửi tớihttps://localhost:5001/MyApp/greet.Greeter/SayHello
.
Ngoài ra, bạn có thể định cấu hình factory máy khách SubdirectoryHandler
bằng cách sử dụng AddHttpMessageHandler.
Định cấu hình máy khách gRPC để sử dụng HTTP/3
Máy khách .NET gRPC hỗ trợ HTTP/3 với .NET 6 trở lên. Nếu máy chủ gửi tiêu đề phản hồi alt-svc
tới máy khách cho biết máy chủ hỗ trợ HTTP/3 thì máy khách sẽ tự động nâng cấp kết nối của nó lên HTTP/3. Để biết thông tin về cách bật HTTP/3 trên máy chủ, hãy xem Sử dụng HTTP/3 với máy chủ web ASP.NET Core Kestrel.
Hỗ trợ HTTP/3 trong .NET 8 được bật theo mặc định. Hỗ trợ HTTP/3 trong .NET 6 và .NET 7 cần được bật thông qua cờ cấu hình trong tệp dự án:
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>
System.Net.SocketsHttpHandler.Http3Support
cũng có thể được đặt bằng AppContext.SetSwitch.
DelegatingHandler có thể được sử dụng để buộc máy khách gRPC sử dụng HTTP/3. Việc buộc HTTP/3 sẽ tránh được chi phí nâng cấp yêu cầu. Buộc HTTP/3 với mã tương tự như sau:
/// <summary>
/// A delegating handler that changes the request HTTP version to HTTP/3.
/// </summary>
public class Http3Handler : DelegatingHandler
{
public Http3Handler() { }
public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Version = HttpVersion.Version30;
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
return base.SendAsync(request, cancellationToken);
}
}
Http3Handler
được sử dụng khi kênh gRPC được tạo. Đoạn mã sau tạo một kênh được định cấu hình để sử dụng Http3Handler
.
var handler = new Http3Handler(new HttpClientHandler());
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });
Ngoài ra, bạn có thể định cấu hình factory máy khách Http3Handler
bằng cách sử dụng AddHttpMessageHandler.
Xây dựng gRPC trên Alpine Linux
Gói Grpc.Tools
tạo ra các loại .NET từ các file .proto
bằng cách sử dụng tệp nhị phân gốc đi kèm có tên là protoc
. Cần có các bước bổ sung để xây dựng ứng dụng gRPC trên nền tảng không được các tệp nhị phân gốc trong Grpc.Tools
, chẳng hạn như Alpine Linux hỗ trợ.
Tạo mã trước thời hạn
Một giải pháp là tạo mã trước thời hạn.
- Di chuyển các file
.proto
và tham chiếu góiGrpc.Tools
tới một dự án mới. - Xuất bản dự án dưới dạng gói NuGet và tải nó lên nguồn cấp dữ liệu NuGet.
- Cập nhật ứng dụng để tham khảo gói NuGet.
Với các bước ở trên, ứng dụng không còn cần phải yêu cầu Grpc.Tools
để build nữa vì mã được tạo trước thời hạn.
Tùy chỉnh các file nhị phân gốc Grpc.Tools
Grpc.Tools
hỗ trợ sử dụng các file nhị phân gốc tùy chỉnh. Tính năng này cho phép công cụ gRPC chạy trong môi trường mà các file nhị phân gốc đi kèm của nó không hỗ trợ.
Xây dựng hoặc thu thập các file nhị phân gốc protoc
và grpc_csharp_plugin
và định cấu hình Grpc.Tools
để sử dụng chúng. Định cấu hình các file nhị phân gốc bằng cách đặt các biến môi trường sau:
PROTOBUF_PROTOC
- Đường dẫn đầy đủ đến trình biên dịch bộ đệm giao thứcGRPC_PROTOC_PLUGIN
- Đường dẫn đầy đủ tới grpc_csharp_plugin
Đối với Alpine Linux, có các gói do cộng đồng cung cấp cho trình biên dịch bộ đệm giao thức và các plugin gRPC tại https://pkgs.alpinelinux.org/.
# Build or install the binaries for your architecture.
# e.g. for Alpine Linux the grpc-plugins package can be used
# See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins # Alpine Linux specific package installer
# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin
# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build
Để biết thêm thông tin về cách sử dụng Grpc.Tools
với kiến trúc không được hỗ trợ, hãy xem tài liệu tích hợp bản dựng gRPC.