Udostępnij za pośrednictwem


Przechowywanie wersji usług gRPC

Autor: James Newton-King

Nowe funkcje dodane do aplikacji mogą wymagać, aby usługi gRPC udostępniane klientom mogły ulec zmianie, czasami w nieoczekiwany i przerywany sposób. Gdy zmienią się usługi gRPC:

  • Należy wziąć pod uwagę, jak zmiany wpływają na klientów.
  • Należy zaimplementować strategię przechowywania wersji do obsługi zmian.

Zgodność z poprzednimi wersjami

Protokół gRPC jest przeznaczony do obsługi usług, które zmieniają się w czasie. Ogólnie rzecz biorąc, dodatki do usług i metod gRPC są niezgodne. Zmiany powodujące niezgodność umożliwiają istniejącym klientom kontynuowanie pracy bez zmian. Zmiana lub usunięcie usług gRPC powoduje niezgodność zmian. Gdy usługi gRPC mają zmiany powodujące niezgodność, klienci korzystający z tej usługi muszą być aktualizowani i wdrażani ponownie.

Wprowadzanie zmian powodujących niezgodność w usłudze ma wiele korzyści:

  • Istniejący klienci nadal działają.
  • Pozwala unikać pracy z powiadamianiem klientów o zmianach powodujących niezgodność i aktualizowaniem ich.
  • Tylko jedna wersja usługi musi być udokumentowana i obsługiwana.

Zmiany powodujące niezgodność

Te zmiany nie są przerywane na poziomie protokołu gRPC i na poziomie binarnym platformy .NET.

  • Dodawanie nowej usługi
  • Dodawanie nowej metody do usługi
  • Dodawanie pola do komunikatu żądania — pola dodane do komunikatu żądania są deserializowane z wartością domyślną na serwerze, jeśli nie zostanie ustawiona. Aby zmiana nie ulegała zmianie powodującej niezgodność, usługa musi zakończyć się pomyślnie, gdy nowe pole nie jest ustawione przez starszych klientów.
  • Dodawanie pola do komunikatu odpowiedzi — pola dodane do komunikatu odpowiedzi są deserializowane do kolekcji nieznanych pól wiadomości na kliencie.
  • Dodawanie wartości do wyliczenia — wyliczenia są serializowane jako wartość liczbowa. Nowe wartości wyliczenia są deserializowane na kliencie do wartości wyliczenia bez nazwy wyliczenia. Aby zmiana nie ulegała zmianie powodującej niezgodność, starsi klienci muszą działać poprawnie podczas odbierania nowej wartości wyliczenia.

Zmiany powodujące niezgodność binarną

Poniższe zmiany nie są przerywane na poziomie protokołu gRPC, ale klient musi zostać zaktualizowany, jeśli uaktualnia go do najnowszego .proto kontraktu lub zestawu .NET klienta. Zgodność binarna jest ważna, jeśli planujesz opublikować bibliotekę gRPC w usłudze NuGet.

  • Usuwanie pola — wartości z usuniętego pola są deserializowane do nieznanych pól wiadomości. Nie jest to zmiana powodująca niezgodność protokołu gRPC, ale klient musi zostać zaktualizowany, jeśli uaktualni go do najnowszej umowy. Ważne jest, aby w przyszłości usunięty numer pola nie był przypadkowo ponownie używany. Aby upewnić się, że tak się nie stanie, określ usunięte numery pól i nazwy w wiadomości przy użyciu zastrzeżonego słowa kluczowego Protobuf.
  • Zmiana nazwy komunikatu — nazwy komunikatów nie są zwykle wysyłane w sieci, więc nie jest to zmiana powodująca niezgodność protokołu gRPC. Klient musi zostać zaktualizowany, jeśli uaktualnia go do najnowszej umowy. Jedną z sytuacji, w której nazwy komunikatów wysyłane w sieci, jest pole Dowolne , gdy nazwa komunikatu jest używana do identyfikowania typu komunikatu.
  • Zagnieżdżanie lub usuwanie komunikatu — można zagnieżdżać typy komunikatów. Zagnieżdżanie lub usuwanie komunikatu zmienia jego nazwę. Zmiana sposobu zagnieżdżenia typu komunikatu ma taki sam wpływ na zgodność co zmiana nazwy.
  • Zmiana csharp_namespace — zmiana csharp_namespace spowoduje zmianę przestrzeni nazw wygenerowanych typów platformy .NET. Nie jest to zmiana powodująca niezgodność protokołu gRPC, ale klient musi zostać zaktualizowany, jeśli uaktualni go do najnowszej umowy.

Zmiany powodujące niezgodność protokołu

Następujące elementy to zmiany powodujące niezgodność protokołu i plików binarnych:

  • Zmiana nazwy pola — w przypadku zawartości Protobuf nazwy pól są używane tylko w wygenerowanym kodzie. Numer pola służy do identyfikowania pól w sieci. Zmiana nazwy pola nie jest zmianą powodującą niezgodność protokołu dla narzędzia Protobuf. Jeśli jednak serwer używa JSzawartości ON, zmiana nazwy pola to zmiana powodująca niezgodność.
  • Zmiana typu danych pola — zmiana typu danych pola na niezgodny typ spowoduje błędy podczas deserializacji komunikatu. Nawet jeśli nowy typ danych jest zgodny, prawdopodobnie klient musi zostać zaktualizowany w celu obsługi nowego typu, jeśli uaktualnia go do najnowszej umowy.
  • Zmiana numeru pola — w przypadku ładunków Protobuf numer pola jest używany do identyfikowania pól w sieci.
  • Zmiana nazwy pakietu, usługi lub metody — gRPC używa nazwy pakietu, nazwy usługi i nazwy metody do skompilowania adresu URL. Klient otrzymuje stan UNIMPLEMENTED z serwera.
  • Usuwanie usługi lub metody — klient otrzymuje stan UNIMPLEMENTED z serwera podczas wywoływania usuniętej metody.

Zmiany powodujące niezgodność zachowania

Podczas wprowadzania zmian powodujących niezgodność należy również rozważyć, czy starsi klienci mogą kontynuować pracę z nowym zachowaniem usługi. Na przykład dodanie nowego pola do komunikatu żądania:

  • Nie jest zmianą powodującą niezgodność protokołu.
  • Zwracanie stanu błędu na serwerze, jeśli nowe pole nie zostało ustawione, powoduje, że zmiana powodująca niezgodność dla starych klientów.

Zgodność zachowania jest określana przez kod specyficzny dla aplikacji.

Usługi numerów wersji

Usługi powinny dążyć do zachowania zgodności z poprzednimi wersjami ze starymi klientami. Ostatecznie zmiany w aplikacji mogą wymagać zmian powodujących niezgodność. Zerwanie starych klientów i wymuszanie aktualizacji wraz z usługą nie jest dobrym środowiskiem użytkownika. Sposobem zachowania zgodności z poprzednimi wersjami podczas wprowadzania zmian powodujących niezgodność jest opublikowanie wielu wersji usługi.

GRPC obsługuje opcjonalny specyfikator pakietu , który działa podobnie jak przestrzeń nazw platformy .NET. W rzeczywistości element będzie używany jako przestrzeń nazw platformy .NET dla wygenerowanych typów platformy .NET, package jeśli option csharp_namespace nie jest ustawiony w .proto pliku. Pakiet może służyć do określenia numeru wersji usługi i jego komunikatów:

syntax = "proto3";

package greet.v1;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Nazwa pakietu jest połączona z nazwą usługi w celu zidentyfikowania adresu usługi. Adres usługi umożliwia hostowania wielu wersji usługi obok siebie:

  • greet.v1.Greeter
  • greet.v2.Greeter

Implementacje wersji usługi są rejestrowane w programie Startup.cs:

app.UseEndpoints(endpoints =>
{
    // Implements greet.v1.Greeter
    endpoints.MapGrpcService<GreeterServiceV1>();

    // Implements greet.v2.Greeter
    endpoints.MapGrpcService<GreeterServiceV2>();
});

Dołączenie numeru wersji w nazwie pakietu umożliwia opublikowanie wersji 2 usługi z powodu zmian powodujących niezgodność, a jednocześnie obsługę starszych klientów, którzy nazywają wersję v1 . Po zaktualizowaniu klientów do korzystania z usługi w wersji 2 możesz usunąć starą wersję. Podczas planowania publikowania wielu wersji usługi:

  • Unikaj zmian powodujących niezgodność, jeśli jest to uzasadnione.
  • Nie aktualizuj numeru wersji, chyba że wprowadzono zmiany powodujące niezgodność.
  • Zaktualizuj numer wersji podczas wprowadzania zmian powodujących niezgodność.

Publikowanie wielu wersji usługi duplikuje ją. Aby zmniejszyć duplikację, rozważ przeniesienie logiki biznesowej z implementacji usług do scentralizowanej lokalizacji, która może zostać ponownie użyta przez stare i nowe implementacje:

using Greet.V1;
using Grpc.Core;
using System.Threading.Tasks;

namespace Services
{
    public class GreeterServiceV1 : Greeter.GreeterBase
    {
        private readonly IGreeter _greeter;
        public GreeterServiceV1(IGreeter greeter)
        {
            _greeter = greeter;
        }

        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = _greeter.GetHelloMessage(request.Name)
            });
        }
    }
}

Usługi i komunikaty generowane przy użyciu różnych nazw pakietów to różne typy platformy .NET. Przeniesienie logiki biznesowej do scentralizowanej lokalizacji wymaga mapowania komunikatów na typowe typy.

Dodatkowe zasoby