Spolehlivé služby gRPC s termíny a zrušením

Autor: James Newton-King

Konečné termíny a zrušení jsou funkce, které klienti gRPC používají k přerušení probíhajících volání. Tento článek popisuje, proč jsou konečné termíny a zrušení důležité a jak je používat v aplikacích .NET gRPC.

Lhůty

Konečný termín umožňuje klientovi gRPC určit, jak dlouho bude čekat na dokončení volání. Když dojde k překročení konečného termínu, hovor se zruší. Nastavení konečného termínu je důležité, protože poskytuje horní limit, jak dlouho může volání běžet. Zastaví chybné chování služeb v provozu navždy a vyčerpává prostředky serveru. Konečné termíny jsou užitečným nástrojem pro vytváření spolehlivých aplikací a měly by být nakonfigurované.

Konfigurace konečného termínu:

  • Konečný termín se konfiguruje při CallOptions.Deadline volání.
  • Výchozí hodnota konečného termínu neexistuje. Volání gRPC nejsou časově omezená, pokud není zadán konečný termín.
  • Konečný termín je čas UTC, kdy je termín překročen. Například DateTime.UtcNow.AddSeconds(5) je konečný termín 5 sekund od této chvíle.
  • Pokud se použije minulý nebo aktuální čas, volání okamžitě překročí konečný termín.
  • Konečný termín se odešle s voláním gRPC do služby a je nezávisle sledován klientem i službou. Je možné, že se volání gRPC dokončí na jednom počítači, ale v době, kdy se odpověď vrátila klientovi, byl překročen konečný termín.

Pokud dojde k překročení konečného termínu, klient a služba mají jiné chování:

  • Klient okamžitě přeruší základní požadavek HTTP a vyvolá DeadlineExceeded chybu. Klientská aplikace může chybu zachytit a zobrazit uživateli zprávu o vypršení časového limitu.
  • Na serveru je spuštěný požadavek HTTP přerušen a ServerCallContext.CancellationToken je vyvolán . I když se požadavek HTTP přeruší, volání gRPC se bude dál spouštět na serveru, dokud se metoda neskončí. Je důležité, aby se token zrušení předával asynchronním metodám, aby se zrušily společně s voláním. Například předání tokenu zrušení asynchronním databázovým dotazům a požadavkům HTTP. Předání tokenu zrušení umožňuje rychlé dokončení zrušeného volání na serveru a uvolnění prostředků pro další volání.

Nakonfigurujte CallOptions.Deadline nastavení konečného termínu pro volání gRPC:

var client = new Greet.GreeterClient(channel);

try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = "World" },
        deadline: DateTime.UtcNow.AddSeconds(5));
    
    // Greeting: Hello World
    Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Greeting timeout.");
}

Použití ServerCallContext.CancellationToken ve službě gRPC:

public override async Task<HelloReply> SayHello(HelloRequest request,
    ServerCallContext context)
{
    var user = await _databaseContext.GetUserAsync(request.Name,
        context.CancellationToken);

    return new HelloReply { Message = "Hello " + user.DisplayName };
}

Konečné termíny a opakování

Když je nakonfigurované volání gRPC s opakovaným zpracováním chyb a termínem, konečný termín sleduje čas napříč všemi opakováními volání gRPC. Pokud dojde k překročení konečného termínu, volání gRPC okamžitě přeruší podkladový požadavek HTTP, přeskočí zbývající opakování a vyvolá DeadlineExceeded chybu.

Šíření konečných termínů

Při volání gRPC ze spuštěné služby gRPC by se měl konečný termín rozšířit. Příklad:

  1. Volání FrontendService.GetUser klientské aplikace s termínem
  2. FrontendService volání UserService.GetUser. Konečný termín určený klientem by měl být zadán pomocí nového volání gRPC.
  3. UserService.GetUser obdrží konečný termín. Správně vyprší časový limit, pokud je překročen konečný termín klientské aplikace.

Kontext volání poskytuje konečný termín ServerCallContext.Deadline:

public override async Task<UserResponse> GetUser(UserRequest request,
    ServerCallContext context)
{
    var client = new User.UserServiceClient(_channel);
    var response = await client.GetUserAsync(
        new UserRequest { Id = request.Id },
        deadline: context.Deadline);

    return response;
}

Ruční šíření termínů může být těžkopádné. Konečný termín musí být předán každému hovoru a je snadné omylem zmeškat. Automatické řešení je k dispozici v klientské továrně gRPC. Určení EnableCallContextPropagation:

  • Automaticky rozšíří konečný termín a token zrušení do podřízených volání.
  • Nerozšíší konečný termín, pokud podřízené volání určuje menší termín. Například šířený termín 10 sekund se nepoužije, pokud volání dítěte určuje nový termín 5 sekund pomocí CallOptions.Deadline. Pokud je k dispozici více termínů, použije se nejmenší termín.
  • Je vynikající způsob, jak zajistit, aby složité a vnořené scénáře gRPC vždy rozšířily konečný termín a zrušení.
services
    .AddGrpcClient<User.UserServiceClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

Další informace najdete v tématu integrace klientské továrny gRPC v .NET.

Zrušení

Zrušení umožňuje klientovi gRPC zrušit dlouhotrvající volání, která už nejsou potřeba. Například volání gRPC, které streamuje aktualizace v reálném čase, se spustí, když uživatel navštíví stránku na webu. Datový proud by se měl zrušit, když uživatel přejde mimo stránku.

Volání gRPC lze v klientovi zrušit předáním tokenu zrušení pomocí CallOptions.CancellationToken nebo voláním Dispose volání.

private AsyncServerStreamingCall<HelloReply> _call;

public void StartStream()
{
    _call = client.SayHellos(new HelloRequest { Name = "World" });

    // Read response in background task.
    _ = Task.Run(async () =>
    {
        await foreach (var response in _call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Greeting: " + response.Message);
        }
    });
}

public void StopStream()
{
    _call.Dispose();
}

Služby gRPC, které je možné zrušit, by měly:

  • Předejte ServerCallContext.CancellationToken asynchronním metodám. Zrušení asynchronních metod umožňuje rychlé dokončení volání na serveru.
  • Rozšíření tokenu zrušení do podřízených volání. Šíření tokenu zrušení zajistí, že se podřízená volání zruší s nadřazeným objektem. Klientská továrna gRPC a EnableCallContextPropagation() automaticky rozšíří token zrušení.

Další prostředky