ASP.NET Core: Cân bằng tải phía máy khách gRPC
Trong bài viết này
- Định cấu hình cân bằng tải phía máy khách gRPC
- Định cấu hình trình phân giải
- Cấu hình bộ cân bằng tải
- Định cấu hình thông tin xác thực kênh
- Sử dụng cân bằng tải với factory máy khách gRPC
- Viết trình phân giải tùy chỉnh và bộ cân bằng tải
- Định cấu hình trình phân giải tùy chỉnh và bộ cân bằng tải
- Tại sao cân bằng tải lại quan trọng
- Tài nguyên bổ sung
Cân bằng tải (load blancing) phía máy khách là một tính năng cho phép máy khách gRPC phân phối tải một cách tối ưu trên các máy chủ có sẵn. Bài viết này thảo luận về cách định cấu hình cân bằng tải phía máy khách để tạo các ứng dụng gRPC hiệu suất cao, có thể mở rộng trong .NET.
Cân bằng tải phía máy khách yêu cầu:
- .NET 5 trở lên.
- Grpc.Net.Clientphiên bản 2.45.0 trở lên.
Định cấu hình cân bằng tải phía máy khách gRPC
Cân bằng tải phía máy khách được định cấu hình khi kênh được tạo. Hai thành phần cần cân nhắc khi sử dụng cân bằng tải:
- Trình phân giải, giải quyết các địa chỉ cho kênh. Trình giải quyết hỗ trợ nhận địa chỉ từ nguồn bên ngoài. Điều này còn được gọi là khám phá dịch vụ.
- Bộ cân bằng tải tạo kết nối và chọn địa chỉ mà lệnh gọi gRPC sẽ sử dụng.
Việc triển khai tích hợp các bộ phân giải và bộ cân bằng tải được bao gồm trong Grpc.Net.Client. Cân bằng tải cũng có thể được mở rộng bằng cách viết các trình phân giải và cân bằng tải tùy chỉnh .
Địa chỉ, kết nối và trạng thái cân bằng tải khác được lưu trữ trong một thể hiện của GrpcChannel
. Một kênh phải được sử dụng lại khi thực hiện lệnh gọi gRPC để cân bằng tải hoạt động chính xác.
Ghi chú
Một số cấu hình cân bằng tải sử dụng tính năng chèn phụ thuộc (DI). Các ứng dụng không sử dụng DI có thể tạo phiên bản ServiceCollection.
Nếu một ứng dụng đã có thiết lập DI, chẳng hạn như trang web ASP.NET Core, thì các kiểu phải được đăng ký với phiên bản DI hiện có. GrpcChannelOptions.ServiceProvider
được định cấu hình bằng cách lấy IServiceProvider từ DI.
Định cấu hình trình phân giải
Trình phân giải được định cấu hình bằng địa chỉ mà kênh được tạo. Lược đồ URI của địa chỉ chỉ định trình phân giải.
Cơ chế | Kiểu | Mô tả |
---|---|---|
dns |
DnsResolverFactory |
Phân giải địa chỉ bằng cách truy vấn tên máy chủ để tìm bản ghi địa chỉ DNS . |
static |
StaticResolverFactory |
Giải quyết các địa chỉ mà ứng dụng đã chỉ định. Được khuyến nghị nếu ứng dụng đã biết địa chỉ mà nó gọi. |
Kênh không gọi trực tiếp URI khớp với trình phân giải. Thay vào đó, một trình phân giải phù hợp sẽ được tạo và sử dụng để phân giải các địa chỉ.
Ví dụ: sử dụng GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure })
:
- Lược đồ
dns
ánh xạ tớiDnsResolverFactory
. Một phiên bản mới của trình phân giải DNS được tạo cho kênh. - Trình phân giải thực hiện truy vấn DNS
my-example-host
và nhận được hai kết quả:127.0.0.100
và127.0.0.101
. - Bộ cân bằng tải sử dụng
127.0.0.100:80
và127.0.0.101:80
để tạo kết nối và thực hiện lệnh gọi gRPC.
DnsResolverFactory
Việc DnsResolverFactory
tạo một trình phân giải được thiết kế để lấy địa chỉ từ nguồn bên ngoài. Độ phân giải DNS thường được sử dụng để cân bằng tải trên các phiên bản nhóm có dịch vụ Kubernetes headless.
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Đoạn code trên:
- Định cấu hình kênh đã tạo bằng địa chỉ
dns:///my-example-host
.- Lược đồ
dns
ánh xạ tớiDnsResolverFactory
. my-example-host
là tên máy chủ cần giải quyết.- Không có cổng nào được chỉ định trong địa chỉ, vì vậy các lệnh gọi gRPC sẽ được gửi đến cổng 80. Đây là cổng mặc định cho các kênh không bảo mật. Một cổng có thể được chỉ định tùy ý sau tên máy chủ. Ví dụ:
dns:///my-example-host:8080
định cấu hình các lời gọi gRPC để được gửi đến cổng 8080.
- Lược đồ
- Không chỉ định bộ cân bằng tải. Kênh mặc định là bộ cân bằng tải chọn đầu tiên.
- Bắt đầu lời gọi gRPC
SayHello
:- Trình phân giải DNS lấy địa chỉ cho tên máy chủ
my-example-host
. - Chọn lần thử cân bằng tải đầu tiên để kết nối với một trong các địa chỉ đã được giải quyết.
- Lời gọi được gửi đến địa chỉ đầu tiên mà kênh kết nối thành công.
- Trình phân giải DNS lấy địa chỉ cho tên máy chủ
Bộ nhớ đệm địa chỉ DNS
Hiệu suất rất quan trọng khi cân bằng tải. Độ trễ của việc phân giải địa chỉ được loại bỏ khỏi lệnh gọi gRPC bằng cách lưu địa chỉ vào bộ nhớ đệm. Trình phân giải sẽ được gọi khi thực hiện lệnh gọi gRPC đầu tiên và các lệnh gọi tiếp theo sẽ sử dụng bộ đệm.
Địa chỉ sẽ tự động được làm mới nếu kết nối bị gián đoạn. Việc làm mới rất quan trọng trong các trường hợp địa chỉ thay đổi trong thời gian chạy. Ví dụ: trong Kubernetes, nhóm được khởi động lại sẽ kích hoạt trình phân giải DNS làm mới và nhận địa chỉ mới của nhóm.
Theo mặc định, trình phân giải DNS được làm mới nếu kết nối bị gián đoạn. Trình phân giải DNS cũng có thể tùy chọn tự làm mới theo định kỳ. Điều này có thể hữu ích để nhanh chóng phát hiện các phiên bản nhóm mới.
services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
Đoạn code trên tạo một DnsResolverFactory
với khoảng thời gian làm mới và đăng ký nó với tính năng chèn phụ thuộc. Để biết thêm thông tin về cách sử dụng trình phân giải được định cấu hình tùy chỉnh, hãy xem Định cấu hình trình phân giải tùy chỉnh và cân bằng tải.
Factory phân giải tĩnh
Trình phân giải tĩnh được cung cấp bởi StaticResolverFactory
. Trình giải quyết này:
- Không gọi nguồn bên ngoài. Thay vào đó, ứng dụng khách sẽ định cấu hình địa chỉ.
- Được thiết kế cho các tình huống mà ứng dụng đã biết địa chỉ mà nó gọi.
var factory = new StaticResolverFactory(addr => new[]
{
new BalancerAddress("localhost", 80),
new BalancerAddress("localhost", 81)
});
var services = new ServiceCollection();
services.AddSingleton<ResolverFactory>(factory);
var channel = GrpcChannel.ForAddress(
"static:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
Đoạn code trên:
- Tạo một file
StaticResolverFactory
. Factory này biết về hai địa chỉ:localhost:80
vàlocalhost:81
. - Đăng ký factory với tính năng chèn phụ thuộc (DI).
- Định cấu hình kênh đã tạo bằng:
- Địa chỉ
static:///my-example-host
. Lược đồstatic
ánh xạ tới một trình phân giải tĩnh. - Đặt
GrpcChannelOptions.ServiceProvider
với nhà cung cấp dịch vụ DI.
- Địa chỉ
Ví dụ này tạo một ServiceCollection mới cho DI. Giả sử một ứng dụng đã có thiết lập DI, chẳng hạn như trang web ASP.NET Core. Trong trường hợp đó, các kiểu phải được đăng ký với phiên bản DI hiện có. GrpcChannelOptions.ServiceProvider
được định cấu hình bằng cách lấy IServiceProvider từ DI.
Cấu hình bộ cân bằng tải
Bộ cân bằng tải được chỉ định trong tập tin service config sử dụng ServiceConfig.LoadBalancingConfigs
. Hai bộ cân bằng tải được tích hợp sẵn và ánh xạ tới tên cấu hình của bộ cân bằng tải:
Tên | Kiểu | Mô tả |
---|---|---|
pick_first |
PickFirstLoadBalancerFactory |
Cố gắng kết nối với các địa chỉ cho đến khi kết nối được thực hiện thành công. Tất cả các cuộc gọi gRPC đều được thực hiện cho kết nối thành công đầu tiên. |
round_robin |
RoundRobinLoadBalancerFactory |
Cố gắng kết nối với tất cả các địa chỉ. Các cuộc gọi gRPC được phân phối trên tất cả các kết nối thành công bằng cách sử dụng logic quay vòng . |
service config
là tên viết tắt của cấu hình dịch vụ và được thể hiện bằng kiểu ServiceConfig
. Có một số cách mà kênh có thể nhận được service config
khi định cấu hình bộ cân bằng tải:
- Một ứng dụng có thể chỉ định
service config
khi tạo kênh bằng cách sử dụngGrpcChannelOptions.ServiceConfig
. - Ngoài ra, trình phân giải có thể giải quyết một
service config
cho một kênh. Tính năng này cho phép một nguồn bên ngoài chỉ định cách người gọi nó thực hiện cân bằng tải. Việc trình phân giải có hỗ trợ giải quyếtservice config
ay không phụ thuộc vào việc triển khai trình phân giải. Tắt tính năng này bằngGrpcChannelOptions.DisableResolverServiceConfig
. - Nếu không có
service config
nào được cung cấp hoặcservice config
không có cấu hình cân bằng tải thì kênh sẽ mặc định làPickFirstLoadBalancerFactory
.
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new RoundRobinConfig() } }
});
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Đoạn code trên:
- Chỉ định một
RoundRobinLoadBalancerFactory
trongservice config
. - Bắt đầu lời gọi gRPC
SayHello
:DnsResolverFactory
tạo một trình phân giải lấy địa chỉ cho tên máy chủmy-example-host
.- Bộ cân bằng tải quay vòng cố gắng kết nối với tất cả các địa chỉ đã được phân giải.
- Các lời gọi gRPC được phân bổ đồng đều bằng cách sử dụng logic luân phiên.
Định cấu hình thông tin xác thực kênh
Kênh phải biết liệu các lời gọi gRPC có được gửi bằng bảo mật truyền tải hay không. http
và https
không còn là một phần của địa chỉ, lược đồ hiện chỉ định một trình phân giải, do đó Credentials
phải được định cấu hình trên các tùy chọn kênh khi sử dụng cân bằng tải.
ChannelCredentials.SecureSsl
- Các lời gọi gRPC được bảo mật bằng Transport Layer Security (TLS). Tương đương với một địa chỉhttps
ChannelCredentials.Insecure
- Lời gọi gRPC không sử dụng bảo mật truyền tải. Tương đương với một địa chỉhttp
var channel = GrpcChannel.ForAddress(
"dns:///my-example-host",
new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure });
var client = new Greet.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = "world" });
Sử dụng cân bằng tải với factory máy khách gRPC
Factory máy khách gRPC có thể được cấu hình để sử dụng cân bằng tải:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("dns:///my-example-host");
})
.ConfigureChannel(o => o.Credentials = ChannelCredentials.Insecure);
builder.Services.AddSingleton<ResolverFactory>(
sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));
var app = builder.Build();
Đoạn code trên:
- Định cấu hình máy khách bằng địa chỉ cân bằng tải.
- Chỉ định thông tin xác thực kênh.
- Đăng ký các loại DI với IServiceCollection của ứng dụng.
Viết trình phân giải tùy chỉnh và bộ cân bằng tải
Bộ cân bằng tải phía máy khách có thể mở rộng:
- Triển khai
Resolver
để tạo trình phân giải tùy chỉnh và phân giải địa chỉ từ nguồn dữ liệu mới. - Triển khai
LoadBalancer
để tạo bộ cân bằng tải tùy chỉnh với hành vi cân bằng tải mới.
Quan trọng
Các API được sử dụng để mở rộng tính năng cân bằng tải phía máy khách chỉ mang tính thử nghiệm. Chúng có thể thay đổi mà không cần thông báo trước.
Tạo một trình phân giải tùy chỉnh
Trình phân giải:
- Triển khai
Resolver
và được tạo bởiResolverFactory
. Tạo một trình phân giải tùy chỉnh bằng cách triển khai các kiểu này. - Chịu trách nhiệm giải quyết các địa chỉ mà bộ cân bằng tải sử dụng.
- Có thể tùy chọn cung cấp một cấu hình dịch vụ.
public class FileResolver : PollingResolver
{
private readonly Uri _address;
private readonly int _port;
public FileResolver(Uri address, int defaultPort, ILoggerFactory loggerFactory)
: base(loggerFactory)
{
_address = address;
_port = defaultPort;
}
public override async Task ResolveAsync(CancellationToken cancellationToken)
{
// Tải JSON từ file trên ổ đĩa và giải tuần tự hóa vào endpoint.
var jsonString = await File.ReadAllTextAsync(_address.LocalPath);
var results = JsonSerializer.Deserialize<string[]>(jsonString);
var addresses = results.Select(r => new BalancerAddress(r, _port)).ToArray();
// Truyền kết quả ngược trở về kênh.
Listener(ResolverResult.ForResult(addresses));
}
}
public class FileResolverFactory : ResolverFactory
{
// Tạo một FileResolver khi URI có một lược đồ 'file'.
public override string Name => "file";
public override Resolver Create(ResolverOptions options)
{
return new FileResolver(options.Address, options.DefaultPort, options.LoggerFactory);
}
}
Trong đoạn code trên:
FileResolverFactory
thực thiResolverFactory
. Nó ánh xạ tới lược đồfile
và tạo ra các thể hiệnFileResolver
.FileResolver
thực thiPollingResolver
.PollingResolver
là kiểu cơ sở trừu tượng giúp dễ dàng triển khai trình phân giải với logic không đồng bộ bằng cách ghi đèResolveAsync
.- Trong
ResolveAsync
:- URI file được chuyển đổi thành đường dẫn cục bộ. Ví dụ,
file:///c:/addresses.json
trở thànhc:\addresses.json
. - JSON được tải từ đĩa và được chuyển đổi thành tập hợp các địa chỉ.
- Listener được gọi với kết quả để cho kênh biết rằng địa chỉ có sẵn.
- URI file được chuyển đổi thành đường dẫn cục bộ. Ví dụ,
Tạo bộ cân bằng tải tùy chỉnh
Bộ cân bằng tải:
- Triển khai
LoadBalancer
và được tạo bởiLoadBalancerFactory
. Tạo factory và bộ cân bằng tải tùy chỉnh bằng cách triển khai các kiểu này. - Được cung cấp địa chỉ từ trình phân giải và tạo các thể hiện của
Subchannel
. - Theo dõi trạng thái về kết nối và tạo file
SubchannelPicker
. Kênh nội bộ sử dụng bộ chọn để chọn địa chỉ khi thực hiện lệnh gọi gRPC.
SubchannelsLoadBalancer
là:
- Một lớp cơ sở trừu tượng triển khai
LoadBalancer
. - Quản lý việc tạo thể hiện
Subchannel
từ địa chỉ. - Giúp dễ dàng thực hiện chính sách chọn tùy chỉnh trên một tập hợp các kênh phụ.
public class RandomBalancer : SubchannelsLoadBalancer
{
public RandomBalancer(IChannelControlHelper controller, ILoggerFactory loggerFactory)
: base(controller, loggerFactory)
{
}
protected override SubchannelPicker CreatePicker(List<Subchannel> readySubchannels)
{
return new RandomPicker(readySubchannels);
}
private class RandomPicker : SubchannelPicker
{
private readonly List<Subchannel> _subchannels;
public RandomPicker(List<Subchannel> subchannels)
{
_subchannels = subchannels;
}
public override PickResult Pick(PickContext context)
{
// Chọn một kênh phụ tùy chỉnh.
return PickResult.ForSubchannel(_subchannels[Random.Shared.Next(0, _subchannels.Count)]);
}
}
}
public class RandomBalancerFactory : LoadBalancerFactory
{
// Tạo một RandomBalancer khi tên là 'random'.
public override string Name => "random";
public override LoadBalancer Create(LoadBalancerOptions options)
{
return new RandomBalancer(options.Controller, options.LoggerFactory);
}
}
Trong đoạn code trên:
RandomBalancerFactory
thực thiLoadBalancerFactory
. Nó ánh xạ tới tên chính sáchrandom
và tạo các thể hiện củaRandomBalancer
.RandomBalancer
thực thiSubchannelsLoadBalancer
. Nó tạo ra mộtRandomPicker
để chọn ngẫu nhiên một kênh con.
Định cấu hình trình phân giải tùy chỉnh và bộ cân bằng tải
Bộ phân giải tùy chỉnh và bộ cân bằng tải cần phải được đăng ký với tính năng chèn phụ thuộc (DI) khi chúng được sử dụng. Có một vài sự lựa chon:
- Nếu một ứng dụng đã sử dụng DI, chẳng hạn như ứng dụng web ASP.NET Core, thì chúng có thể được đăng ký với cấu hình DI hiện có. IServiceProvider có thể được phân giải từ DI và được chuyển đến kênh bằng cách sử dụng
GrpcChannelOptions.ServiceProvider
. - Nếu một ứng dụng không sử dụng DI thì hãy tạo:
- Một ServiceCollection với các kiểu được đăng ký với nó.
- Nhà cung cấp dịch vụ sử dụng BuildServiceProvider.
var services = new ServiceCollection();
services.AddSingleton<ResolverFactory, FileResolverFactory>();
services.AddSingleton<LoadBalancerFactory, RandomLoadBalancerFactory>();
var channel = GrpcChannel.ForAddress(
"file:///c:/data/addresses.json",
new GrpcChannelOptions
{
Credentials = ChannelCredentials.Insecure,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new LoadBalancingConfig("random") } },
ServiceProvider = services.BuildServiceProvider()
});
var client = new Greet.GreeterClient(channel);
Đoạn code trên:
- Tạo
ServiceCollection
và đăng ký triển khai trình phân giải và cân bằng tải mới. - Tạo một kênh được định cấu hình để sử dụng các triển khai mới:
ServiceCollection
được tích hợp vào mộtIServiceProvider
và được đặt thànhGrpcChannelOptions.ServiceProvider
.- Địa chỉ kênh là
file:///c:/data/addresses.json
. Lược đồfile
ánh xạ tớiFileResolverFactory
. - Tên của bộ cân bằng tải
service config
làrandom
. Ánh xạ tớiRandomLoadBalancerFactory
.
Tại sao cân bằng tải lại quan trọng
HTTP/2 ghép nhiều lời gọi trên một kết nối TCP. Nếu gRPC và HTTP/2 được sử dụng với bộ cân bằng tải mạng (NLB), thì kết nối sẽ được chuyển tiếp đến một máy chủ và tất cả lệnh gọi gRPC sẽ được gửi đến một máy chủ đó. Các phiên bản máy chủ khác trên NLB không hoạt động.
Cân bằng tải mạng là một giải pháp phổ biến để cân bằng tải vì chúng nhanh và nhẹ. Ví dụ: Kubernetes theo mặc định sử dụng bộ cân bằng tải mạng để cân bằng kết nối giữa các phiên bản nhóm. Tuy nhiên, bộ cân bằng tải mạng không hiệu quả trong việc phân phối tải khi sử dụng với gRPC và HTTP/2.
Cân bằng tải phía proxy hay phía máy khách?
gRPC và HTTP/2 có thể được cân bằng tải một cách hiệu quả bằng cách sử dụng proxy cân bằng tải ứng dụng hoặc cân bằng tải phía máy khách. Cả hai tùy chọn này đều cho phép các lệnh gọi gRPC riêng lẻ được phân phối trên các máy chủ có sẵn. Quyết định giữa cân bằng tải phía proxy và phía máy khách là một lựa chọn mang tính kiến trúc, có những ưu và nhược điểm riêng.
-
Proxy: các lệnh gọi gRPC được gửi đến proxy, proxy đưa ra quyết định cân bằng tải và lệnh gọi gRPC được gửi đến endpoint cuối cùng. Proxy có trách nhiệm biết về endpoint. Sử dụng proxy sẽ thêm:
- Một bước nhảy (hop) mạng bổ sung tới các lời gọi gRPC.
- Độ trễ và tiêu thụ tài nguyên bổ sung.
- Máy chủ proxy phải được thiết lập và cấu hình chính xác.
-
Cân bằng tải phía máy khách: Máy khách gRPC đưa ra quyết định cân bằng tải khi lời gọi gRPC được bắt đầu. Lời gọi gRPC được gửi trực tiếp đến endpoint cuối cùng. Khi sử dụng cân bằng tải phía máy khách:
- Máy khách có trách nhiệm biết về các endpoint có sẵn và đưa ra quyết định cân bằng tải.
- Cần có cấu hình máy khách bổ sung.
- Các lệnh gọi gRPC cân bằng tải, hiệu suất cao loại bỏ nhu cầu về proxy.