Поделиться через


Управление версиями gRPC Services

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 8 этой статьи.

Автор: Джеймс Ньютон-Кинг (James Newton-King)

Новые функции, добавленные в приложение, могут требовать изменения служб gRPC, предоставляемых клиентам, и иногда это может приводить к непредвиденному поведению и сбоям. При изменении служб gRPC:

  • Следует учитывать, как изменения влияют на клиентов.
  • Должна быть реализована стратегия управления версиями для поддержки изменений.

обратная совместимость;

Протокол gRPC предназначен для поддержки служб, которые изменяются со временем. Как правило, дополнения к службам и методам gRPC не являются критическими. Некритические изменения позволяют существующим клиентам продолжать работу без изменений. Изменение или удаление служб gRPC являются критическими изменениями. Когда в службах gRPC возникают критические изменения, клиенты, использующие эту службу, необходимо обновить и повторно развернуть.

Внесение некритических изменений в службу имеет ряд преимуществ:

  • Существующие клиенты продолжают работать.
  • Не приходится уведомлять клиентов о критических изменениях и обновлять их.
  • Только одна версия службы должна документироваться и поддерживаться.

Некритические изменения

Эти изменения не нарушают уровень протокола gRPC и двоичный уровень .NET.

  • Добавление новой службы
  • Добавление нового метода в службу
  • Добавление поля в сообщение запроса — поля, добавленные в сообщение запроса, десериализуются со значением по умолчанию на сервере, если не задано значение. Чтобы изменение было некритическим, служба должна выполняться, если новое поле не задается старыми клиентами.
  • Добавление поля в ответное сообщение. Если старый клиент не был обновлен с новым полем, значение десериализуется в коллекцию неизвестных полей ответа.
  • Добавление значения в перечисление — перечисления сериализуются как числовое значение. Новые значения перечисления десериализуются на клиенте в значение перечисления без имени перечисления. Чтобы изменение было некритическим, более старые клиенты должны правильно работать при получении нового значения перечисления.

Критические изменения двоичного кода

Следующие изменения не являются критическими на уровне протокола gRPC, но клиент должен быть обновлен, если он обновляется до последней .proto сборки контракта или клиентской сборки .NET. Совместимость двоичного кода важна, если вы планируете публиковать библиотеку gRPC в NuGet.

  • Удаление поля — значения из удаленного поля десериализуются в неизвестные поля сообщения. Это не является критическим изменением протокола gRPC, но клиент должен быть обновлен, если выполняется обновление до последней версии контракта. Важно, чтобы номер удаленного поля случайно не использовался повторно в будущем. Чтобы этого не произошло, укажите удаленные номера и имена полей в сообщении с помощью ключевого слова Protobuf reserved.
  • Переименование сообщения — имена сообщений обычно не отправляются по сети, поэтому это не является критическим изменением протокола gRPC. Клиент необходимо будет обновить, если выполняется обновление до последней версии контракта. Одна из ситуаций, в которой имена сообщений отправляются в сеть, — использование полей Any, когда имя сообщения используется для распознавания типа сообщений.
  • Вложение или отмена вложения сообщения — типы сообщений могут быть вложенными. При вложении или отмене вложения имя сообщения изменяется. Изменение способа вложения типа сообщения имеет то же воздействие на совместимость, что и переименование.
  • Изменение csharp_namespace — изменение csharp_namespace приведет к изменению пространства имен созданных типов .NET. Это не является критическим изменением протокола gRPC, но клиент должен быть обновлен, если выполняется обновление до последней версии контракта.

Критические изменения протокола

Ниже перечислены критические изменения протокола и двоичного кода:

  • Переименование поля — при использовании содержимого Protobuf имена полей используются только в созданном коде. Номер поля используется для идентификации полей в сети. Переименование поля не является критическим изменением протокола для Protobuf. Однако, если сервер использует содержимое JSON, то переименование поля является критическим изменением.
  • Изменение типа данных поля — изменение типа данных поля на несовместимый тип приведет к ошибкам при десериализации сообщения. Даже если новый тип данных совместим, скорее всего, клиент должен быть обновлен для поддержки нового типа, если выполняется обновление до последней версии контракта.
  • Изменение номера поля — с полезными данными Protobuf номер поля используется для идентификации полей в сети.
  • Переименование пакета, службы или метода — gRPC использует имя пакета, имя службы и имя метода для создания URL-адреса. Клиент получает состояние UNIMPLEMENTED от сервера.
  • Удаление службы или метода — клиент получает состояние UNIMPLEMENTED от сервера при вызове удаленного метода.

Критическое изменение поведения

При внесении некритических изменений необходимо также определить, могут ли старые клиенты продолжать работу с новым поведением службы. Например, добавление нового поля в сообщение запроса:

  • Не является критическим изменением протокола.
  • Возврат состояния ошибки на сервере, если новое поле не задано, приводит к критическому изменению старых клиентов.

Совместимость поведения определяется кодом конкретного приложения.

Службы номера версии

Службы должны стремиться обеспечить обратную совместимость со старыми клиентами. В конечном итоге изменения в приложении могут потребовать критических изменений. Прерывание старых клиентов и их принудительное обновление вместе со службой — это неудачное решение. Для обеспечения обратной совместимости при внесении критических изменений необходимо опубликовать несколько версий службы.

gRPC поддерживает необязательный описатель пакета, который во многом схож с пространством имен .NET. На самом деле package будет использоваться как пространство имен .NET для созданных типов .NET, если option csharp_namespace не задано в файле .proto. Пакет можно использовать для указания номера версии службы и ее сообщений:

syntax = "proto3";

package greet.v1;

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

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Имя пакета объединяется с именем службы для идентификации адреса службы. Адрес службы позволяет размещать несколько версий службы параллельно:

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

Реализации версии службы зарегистрированы в Startup.cs:

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

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

Включение номера версии в имя пакета дает возможность публиковать версию службы v2 с критическими изменениями, продолжая поддерживать более старые клиенты, вызывающие версию v1. После обновления клиентов для использования службы v2 можно удалить старую версию. При планировании публикации нескольких версий службы:

  • Избегайте критических изменений, если это оправданно.
  • Не обновляйте номер версии, если не внесены критические изменения.
  • Обновляйте номер версии, если внесены критические изменения.

Публикация нескольких версий службы дублирует ее. Чтобы уменьшить дублирование, рассмотрите возможность перемещения бизнес-логики из реализаций службы в централизованное расположение, которое может быть повторно использовано старыми и новыми реализациями:

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

Службы и сообщения, созданные с разными именами пакетов, являются различными типами .NET. Для перемещения бизнес-логики в централизованное расположение требуется сопоставление сообщений с общими типами.

Дополнительные ресурсы