Nguon: Microsoft Learn · .NET 8.0

Versioning (quản lý phiên bản) các dịch vụ gRPC

Nguồn: Versioning gRPC services

Bởi James Newton-King

Các tính năng mới được thêm vào ứng dụng có thể yêu cầu thay đổi các dịch vụ gRPC cung cấp cho client, đôi khi theo những cách bất ngờ và gây ra breaking change (thay đổi phá vỡ tương thích). Khi các dịch vụ gRPC thay đổi:

Backwards compatibility (tương thích ngược)

Giao thức gRPC được thiết kế để hỗ trợ các dịch vụ thay đổi theo thời gian. Nhìn chung, việc thêm vào các dịch vụ và phương thức gRPC là non-breaking (không phá vỡ tương thích). Các thay đổi non-breaking cho phép client hiện tại tiếp tục hoạt động mà không cần thay đổi. Việc thay đổi hoặc xóa các dịch vụ gRPC là breaking change. Khi các dịch vụ gRPC có breaking change, client sử dụng dịch vụ đó phải được cập nhật và triển khai lại.

Việc thực hiện các thay đổi non-breaking với một dịch vụ mang lại nhiều lợi ích:

Non-breaking changes (thay đổi không phá vỡ tương thích)

Những thay đổi này là non-breaking ở cấp độ giao thức gRPC và cấp độ .NET binary.

Binary breaking changes (thay đổi phá vỡ binary)

Những thay đổi sau là non-breaking ở cấp độ giao thức gRPC, nhưng client cần được cập nhật nếu nó nâng cấp lên .proto contract hoặc .NET assembly mới nhất. Binary compatibility (tương thích binary) quan trọng nếu bạn có kế hoạch xuất bản thư viện gRPC lên NuGet.

Protocol breaking changes (thay đổi phá vỡ giao thức)

Các mục sau là breaking change ở cả giao thức và binary:

Behavior breaking changes (thay đổi phá vỡ hành vi)

Khi thực hiện các thay đổi non-breaking, bạn cũng phải xem xét liệu client cũ có thể tiếp tục hoạt động với hành vi dịch vụ mới hay không. Ví dụ, thêm một trường mới vào request message:

Khả năng tương thích hành vi được xác định bởi code đặc thù của ứng dụng.

Version number services (đánh số phiên bản dịch vụ)

Các dịch vụ nên cố gắng duy trì backwards compatibility với client cũ. Cuối cùng, các thay đổi đối với ứng dụng của bạn có thể yêu cầu breaking change. Việc làm client cũ không hoạt động và buộc họ phải cập nhật cùng với dịch vụ của bạn không phải là trải nghiệm tốt cho người dùng. Một cách để duy trì backwards compatibility trong khi thực hiện breaking change là xuất bản nhiều phiên bản của một dịch vụ.

gRPC hỗ trợ một package specifier tùy chọn, hoạt động giống như .NET namespace. Thực tế, package sẽ được sử dụng làm .NET namespace cho các kiểu .NET được tạo ra nếu option csharp_namespace không được thiết lập trong file .proto. Package có thể được dùng để chỉ định số phiên bản cho dịch vụ và các message của nó:

protobuf
syntax = "proto3";

package greet.v1;

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

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Tên package được kết hợp với tên service để xác định địa chỉ dịch vụ. Địa chỉ dịch vụ cho phép nhiều phiên bản của một dịch vụ được lưu trú song song:

Các triển khai của dịch vụ có phiên bản được đăng ký trong Startup.cs:

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

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

Việc thêm số phiên bản vào tên package cho bạn cơ hội xuất bản phiên bản v2 của dịch vụ với breaking change, trong khi vẫn hỗ trợ client cũ gọi phiên bản v1. Khi client đã cập nhật để sử dụng dịch vụ v2, bạn có thể chọn xóa phiên bản cũ. Khi lên kế hoạch xuất bản nhiều phiên bản của dịch vụ:

Xuất bản nhiều phiên bản của một dịch vụ sẽ tạo ra sự trùng lặp. Để giảm trùng lặp, hãy xem xét việc chuyển business logic (logic nghiệp vụ) từ các triển khai dịch vụ sang một vị trí tập trung có thể được tái sử dụng bởi cả triển khai cũ và mới:

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

Các dịch vụ và message được tạo ra với các tên package khác nhau là các kiểu .NET khác nhau. Việc chuyển business logic sang vị trí tập trung yêu cầu ánh xạ các message sang các kiểu chung.