Bagikan melalui


penyeimbangan beban sisi klien gRPC

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Oleh James Newton-King

Penyeimbangan beban sisi klien adalah fitur yang memungkinkan klien gRPC mendistribusikan beban secara optimal di seluruh server yang tersedia. Artikel ini membahas cara mengonfigurasi penyeimbangan beban sisi klien untuk membuat aplikasi gRPC berkinerja tinggi yang dapat diskalakan di .NET.

Penyeimbangan beban sisi klien memerlukan:

  • .NET 5 atau yang lebih baru.
  • Grpc.Net.Client versi 2.45.0 atau yang lebih baru.

Mengonfigurasi penyeimbangan beban sisi klien gRPC

Penyeimbangan beban sisi klien dikonfigurasi saat saluran dibuat. Dua komponen yang perlu dipertimbangkan saat menggunakan penyeimbangan beban:

  • Resolver, yang menyelesaikan alamat untuk saluran. Resolver mendukung mendapatkan alamat dari sumber eksternal. Ini juga dikenal sebagai penemuan layanan.
  • Load balancer, yang membuat koneksi dan memilih alamat yang akan digunakan oleh panggilan gRPC.

Implementasi bawaan resolver dan load balancer disertakan dalam Grpc.Net.Client. Penyeimbangan beban juga dapat diperluas dengan menulis pemecah masalah kustom dan load balancer.

Alamat, koneksi, dan status penyeimbangan beban lainnya disimpan dalam GrpcChannel instans. Saluran harus digunakan kembali saat melakukan panggilan gRPC agar penyeimbangan beban berfungsi dengan benar.

Catatan

Beberapa konfigurasi penyeimbangan beban menggunakan injeksi dependensi (DI). Aplikasi yang tidak menggunakan DI dapat membuat ServiceCollection instans.

Jika aplikasi sudah memiliki penyiapan DI, seperti situs web ASP.NET Core, maka jenis harus didaftarkan dengan instans DI yang ada. GrpcChannelOptions.ServiceProvider dikonfigurasi dengan mendapatkan IServiceProvider dari DI.

Mengonfigurasi pemecah masalah

Resolver dikonfigurasi menggunakan alamat yang dibuat saluran. Skema URI alamat menentukan resolver.

Skema Tipe Deskripsi
dns DnsResolverFactory Menyelesaikan alamat dengan mengkueri nama host untuk rekaman alamat DNS.
static StaticResolverFactory Menyelesaikan alamat yang telah ditentukan aplikasi. Disarankan jika aplikasi sudah mengetahui alamat yang dipanggilnya.

Saluran tidak secara langsung memanggil URI yang cocok dengan resolver. Sebagai gantinya, resolver yang cocok dibuat dan digunakan untuk menyelesaikan alamat.

Misalnya, menggunakan GrpcChannel.ForAddress("dns:///my-example-host", new GrpcChannelOptions { Credentials = ChannelCredentials.Insecure }):

  • dns Skema memetakan ke DnsResolverFactory. Instans baru pemecah masalah DNS dibuat untuk saluran.
  • Resolver membuat kueri DNS untuk my-example-host dan mendapatkan dua hasil: 127.0.0.100 dan 127.0.0.101.
  • Load balancer menggunakan 127.0.0.100:80 dan 127.0.0.101:80 untuk membuat koneksi dan melakukan panggilan gRPC.

DnsResolverFactory

membuat DnsResolverFactory resolver yang dirancang untuk mendapatkan alamat dari sumber eksternal. Resolusi DNS umumnya digunakan untuk memuat keseimbangan atas instans pod yang memiliki layanan tanpa kepala Kubernetes.

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" });

Kode sebelumnya:

  • Mengonfigurasi saluran yang dibuat dengan alamat dns:///my-example-host.
    • dns Skema memetakan ke DnsResolverFactory.
    • my-example-host adalah nama host untuk diselesaikan.
    • Tidak ada port yang ditentukan dalam alamat, sehingga panggilan gRPC dikirim ke port 80. Ini adalah port default untuk saluran yang tidak aman. Port secara opsional dapat ditentukan setelah nama host. Misalnya, dns:///my-example-host:8080 mengonfigurasi panggilan gRPC untuk dikirim ke port 8080.
  • Tidak menentukan load balancer. Saluran default ke load balancer pertama pilihan.
  • Memulai panggilan SayHellogRPC :
    • Resolver DNS mendapatkan alamat untuk nama my-example-hosthost .
    • Pilih penyeimbang muatan pertama yang mencoba menyambungkan ke salah satu alamat yang diselesaikan.
    • Panggilan dikirim ke alamat pertama yang berhasil disambungkan oleh saluran.
Penembolokan alamat DNS

Performa penting saat penyeimbangan beban. Latensi mengatasi alamat dihilangkan dari panggilan gRPC dengan penembolokan alamat. Resolver akan dipanggil saat melakukan panggilan gRPC pertama, dan panggilan berikutnya menggunakan cache.

Alamat disegarkan secara otomatis jika koneksi terganggu. Penyegaran penting dalam skenario di mana alamat berubah saat runtime. Misalnya, di Kubernetes sebuah pod yang dimulai ulang memicu pemecah masalah DNS untuk merefresh dan mendapatkan alamat baru pod.

Secara default, pemecah masalah DNS disegarkan jika koneksi terganggu. Pemecah masalah DNS juga dapat secara opsional menyegarkan dirinya sendiri pada interval berkala. Ini dapat berguna untuk mendeteksi instans pod baru dengan cepat.

services.AddSingleton<ResolverFactory>(
    sp => new DnsResolverFactory(refreshInterval: TimeSpan.FromSeconds(30)));

Kode sebelumnya membuat DnsResolverFactory dengan interval refresh dan mendaftarkannya dengan injeksi dependensi. Untuk informasi selengkapnya tentang menggunakan pemecah masalah yang dikonfigurasi khusus, lihat Mengonfigurasi pemecah masalah kustom dan load balancer.

StaticResolverFactory

Resolver statis disediakan oleh StaticResolverFactory. Pemecah masalah ini:

  • Tidak memanggil sumber eksternal. Sebagai gantinya, aplikasi klien mengonfigurasi alamat.
  • Dirancang untuk situasi di mana aplikasi sudah mengetahui alamat yang dipanggilnya.
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);

Kode sebelumnya:

  • StaticResolverFactoryMembuat . Pabrik ini tahu tentang dua alamat: localhost:80 dan localhost:81.
  • Mendaftarkan pabrik dengan injeksi dependensi (DI).
  • Mengonfigurasi saluran yang dibuat dengan:
    • Alamat static:///my-example-host. static Skema memetakan ke resolver statis.
    • GrpcChannelOptions.ServiceProvider Diatur dengan penyedia layanan DI.

Contoh ini membuat baru ServiceCollection untuk DI. Misalkan aplikasi sudah memiliki penyiapan DI, seperti situs web ASP.NET Core. Dalam hal ini, jenis harus didaftarkan dengan instans DI yang ada. GrpcChannelOptions.ServiceProvider dikonfigurasi dengan mendapatkan IServiceProvider dari DI.

Mengonfigurasi penyeimbang beban

Load balancer ditentukan dalam service config menggunakan ServiceConfig.LoadBalancingConfigs koleksi. Dua load balancer adalah bawaan dan memetakan ke nama konfigurasi load balancer:

Nama Tipe Deskripsi
pick_first PickFirstLoadBalancerFactory Mencoba menyambungkan ke alamat hingga koneksi berhasil dibuat. Semua panggilan gRPC dilakukan ke koneksi pertama yang berhasil.
round_robin RoundRobinLoadBalancerFactory Mencoba menyambungkan ke semua alamat. Panggilan gRPC didistribusikan di semua koneksi yang berhasil menggunakan logika round-robin .

service config adalah singkatan dari konfigurasi layanan dan diwakili oleh jenisnya ServiceConfig . Ada beberapa cara saluran bisa mendapatkan dengan load balancer yang service config dikonfigurasi:

  • Aplikasi dapat menentukan service config kapan saluran dibuat menggunakan GrpcChannelOptions.ServiceConfig.
  • Atau, resolver dapat menyelesaikan service config untuk saluran. Fitur ini memungkinkan sumber eksternal untuk menentukan bagaimana pemanggilnya harus melakukan penyeimbangan beban. Apakah resolver mendukung penyelesaian service config tergantung pada implementasi resolver. Nonaktifkan fitur ini dengan GrpcChannelOptions.DisableResolverServiceConfig.
  • Jika tidak service config disediakan, atau tidak memiliki load balancer yang dikonfigurasi service config , saluran default ke 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" });

Kode sebelumnya:

  • RoundRobinLoadBalancerFactory Menentukan di service config.
  • Memulai panggilan SayHellogRPC :
    • DnsResolverFactory membuat resolver yang mendapatkan alamat untuk nama my-example-hosthost .
    • Load balancer round-robin mencoba menyambungkan ke semua alamat yang diselesaikan.
    • Panggilan gRPC didistribusikan secara merata menggunakan logika round-robin.

Mengonfigurasi kredensial saluran

Saluran harus mengetahui apakah panggilan gRPC dikirim menggunakan keamanan transportasi. http dan https bukan lagi bagian dari alamat, skema sekarang menentukan pemecah masalah, jadi Credentials harus dikonfigurasi pada opsi saluran saat menggunakan penyeimbangan beban.

  • ChannelCredentials.SecureSsl - Panggilan gRPC diamankan dengan Keamanan Lapisan Transportasi (TLS). Setara dengan https alamat.
  • ChannelCredentials.Insecure - Panggilan gRPC tidak menggunakan keamanan transportasi. Setara dengan http alamat.
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" });

Menggunakan penyeimbangan beban dengan pabrik klien gRPC

Pabrik klien gRPC dapat dikonfigurasi untuk menggunakan penyeimbangan beban:

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();

Kode sebelumnya:

  • Mengonfigurasi klien dengan alamat penyeimbang beban.
  • Menentukan kredensial saluran.
  • Mendaftarkan jenis DI dengan aplikasi IServiceCollection.

Menulis pemecah masalah kustom dan load balancer

Penyeimbangan beban sisi klien dapat diperluas:

  • Terapkan Resolver untuk membuat resolver kustom dan mengatasi alamat dari sumber data baru.
  • Terapkan LoadBalancer untuk membuat load balancer kustom dengan perilaku penyeimbangan beban baru.

Penting

API yang digunakan untuk memperluas penyeimbangan beban sisi klien bersifat eksperimental. Mereka dapat berubah tanpa pemberitahuan.

Membuat pemecah masalah kustom

Pemecah masalah:

  • Resolver Mengimplementasikan dan dibuat oleh ResolverFactory. Buat resolver kustom dengan menerapkan jenis ini.
  • Bertanggung jawab untuk menyelesaikan alamat yang digunakan load balancer.
  • Dapat secara opsional menyediakan konfigurasi layanan.
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)
    {
        // Load JSON from a file on disk and deserialize into endpoints.
        var jsonString = await File.ReadAllTextAsync(_address.LocalPath);
        var results = JsonSerializer.Deserialize<string[]>(jsonString);
        var addresses = results.Select(r => new BalancerAddress(r, _port)).ToArray();

        // Pass the results back to the channel.
        Listener(ResolverResult.ForResult(addresses));
    }
}

public class FileResolverFactory : ResolverFactory
{
    // Create a FileResolver when the URI has a 'file' scheme.
    public override string Name => "file";

    public override Resolver Create(ResolverOptions options)
    {
        return new FileResolver(options.Address, options.DefaultPort, options.LoggerFactory);
    }
}

Dalam kode sebelumnya:

  • FileResolverFactory penerapan ResolverFactory. Ini memetakan ke file skema dan membuat instans FileResolver .
  • FileResolver penerapan PollingResolver. PollingResolver adalah jenis dasar abstrak yang memudahkan untuk mengimplementasikan resolver dengan logika asinkron dengan mengambil ResolveAsyncalih .
  • Dalam: ResolveAsync
    • URI file dikonversi ke jalur lokal. Misalnya, file:///c:/addresses.json menjadi c:\addresses.json.
    • JSON dimuat dari disk dan dikonversi menjadi kumpulan alamat.
    • Listener dipanggil dengan hasil untuk memberi tahu saluran bahwa alamat tersedia.

Membuat load balancer kustom

Load balancer:

  • LoadBalancer Mengimplementasikan dan dibuat oleh LoadBalancerFactory. Buat load balancer dan pabrik kustom dengan menerapkan jenis ini.
  • Diberikan alamat dari resolver dan membuat instans Subchannel .
  • Melacak status tentang koneksi dan membuat SubchannelPicker. Saluran secara internal menggunakan pemilih untuk memilih alamat saat melakukan panggilan gRPC.

adalah SubchannelsLoadBalancer :

  • Kelas dasar abstrak yang mengimplementasikan LoadBalancer.
  • Mengelola pembuatan Subchannel instans dari alamat.
  • Memudahkan penerapan kebijakan pemilihan kustom atas kumpulan subchannels.
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)
        {
            // Pick a random subchannel.
            return PickResult.ForSubchannel(_subchannels[Random.Shared.Next(0, _subchannels.Count)]);
        }
    }
}

public class RandomBalancerFactory : LoadBalancerFactory
{
    // Create a RandomBalancer when the name is 'random'.
    public override string Name => "random";

    public override LoadBalancer Create(LoadBalancerOptions options)
    {
        return new RandomBalancer(options.Controller, options.LoggerFactory);
    }
}

Dalam kode sebelumnya:

  • RandomBalancerFactory penerapan LoadBalancerFactory. Ini memetakan ke random nama kebijakan dan membuat instans RandomBalancer .
  • RandomBalancer penerapan SubchannelsLoadBalancer. Ini membuat RandomPicker yang secara acak memilih subsaluran.

Mengonfigurasi resolver kustom dan load balancer

Resolver kustom dan load balancer perlu didaftarkan dengan injeksi dependensi (DI) saat digunakan. Ada beberapa opsi:

  • Jika aplikasi sudah menggunakan DI, seperti aplikasi web ASP.NET Core, aplikasi web tersebut dapat didaftarkan dengan konfigurasi DI yang ada. Dapat IServiceProvider diselesaikan dari DI dan diteruskan ke saluran menggunakan GrpcChannelOptions.ServiceProvider.
  • Jika aplikasi tidak menggunakan DI, buat:
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);

Kode sebelumnya:

  • ServiceCollection Membuat dan mendaftarkan implementasi resolver dan load balancer baru.
  • Membuat saluran yang dikonfigurasi untuk menggunakan implementasi baru:
    • ServiceCollection dibangun ke dalam IServiceProvider dan diatur ke GrpcChannelOptions.ServiceProvider.
    • Alamat saluran adalah file:///c:/data/addresses.json. file Skema memetakan ke FileResolverFactory.
    • service config nama load balancer adalah random. Memetakan ke RandomLoadBalancerFactory.

Mengapa penyeimbangan beban penting

Multipleks HTTP/2 beberapa panggilan pada satu koneksi TCP. Jika gRPC dan HTTP/2 digunakan dengan penyeimbang beban jaringan (NLB), koneksi diteruskan ke server, dan semua panggilan gRPC dikirim ke satu server tersebut. Instans server lain pada NLB tidak aktif.

Penyeimbang beban jaringan adalah solusi umum untuk penyeimbangan beban karena cepat dan ringan. Misalnya, Kubernetes secara default menggunakan penyeimbang beban jaringan untuk menyeimbangkan koneksi antar instans pod. Namun, penyeimbang beban jaringan tidak efektif dalam mendistribusikan beban saat digunakan dengan gRPC dan HTTP/2.

Proksi atau penyeimbangan beban sisi klien?

gRPC dan HTTP/2 dapat secara efektif dimuat seimbang menggunakan proksi load balancer aplikasi atau penyeimbangan beban sisi klien. Kedua opsi ini memungkinkan panggilan gRPC individual didistribusikan di seluruh server yang tersedia. Memutuskan antara proksi dan penyeimbangan beban sisi klien adalah pilihan arsitektur. Ada pro dan kontra untuk masing-masing.

  • Proksi: panggilan gRPC dikirim ke proksi, proksi membuat keputusan penyeimbangan beban, dan panggilan gRPC dikirim ke titik akhir. Proksi bertanggung jawab untuk mengetahui tentang titik akhir. Menggunakan proksi menambahkan:

    • Lompatan jaringan tambahan ke panggilan gRPC.
    • Latensi dan mengonsumsi sumber daya tambahan.
    • Server proksi harus disiapkan dan dikonfigurasi dengan benar.
  • Penyeimbangan beban sisi klien: Klien gRPC membuat keputusan penyeimbangan beban saat panggilan gRPC dimulai. Panggilan gRPC dikirim langsung ke titik akhir. Saat menggunakan penyeimbangan beban sisi klien:

    • Klien bertanggung jawab untuk mengetahui tentang titik akhir yang tersedia dan membuat keputusan penyeimbangan beban.
    • Konfigurasi klien tambahan diperlukan.
    • Panggilan gRPC berkinerja tinggi dan seimbang beban menghilangkan kebutuhan akan proksi.

Sumber Daya Tambahan: