基于gRPC的REST web服务版本控制

4
我已经使用协议缓冲和 gRPC 实现了一个 API 服务,然后使用 grpc-gateway 将其公开为一组 REST web 服务。
现在我到了需要维护不同版本的 API 的阶段,但是我陷入了困境。
例如,在我的 proto 文件中,我定义了这样一个处理程序。
rpc MerchantGet (MerchantRequest) returns (MerchantResponse) {
    option (google.api.http) = {
        get: "/v1.1.0/myapi/merchant/{MerchantID}"
    };
}

当然,在我的Go代码中,我有一个函数MerchantGet,它映射到/v1.1.0/myapi/merchant/{MerchantID}GET操作。
现在,假设我想要为MerchantGet方法添加更多功能并发布一个新版本。我打算按照 语义化版本规范保持向后兼容性,因此如果我理解正确,这意味着我可以对我的MerchantGet方法进行基础更改,并将其替换旧方法,只要它不需要第三方(MerchantRequest)提供不同的输入或更改发送给第三方(MerchantResponse)的响应,除非通过在响应末尾添加附加字段。(如果我在这个假设上错了,请纠正我)。
我的问题是,如何编写proto处理程序以为不同版本的端点提供服务?其中一个想法可能如下所示:
rpc MerchantGet (MerchantRequest) returns (MerchantResponse) {
    option (google.api.http) = {
        get: "/v1.6.0/myapi/merchant/{MerchantID}"
        additional_bindings {
            get: "/v1.5.0/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.4.2/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.4.1/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.4.0/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.3.0/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.2.0/myapi/merchant/{MerchantID}"
        }
        additional_bindings {
            get: "/v1.1.0/myapi/merchant/{MerchantID}"
        }
    };
}

但是这肯定不是实现这个的惯用方式吧?这绝对不够优雅,因为每次新的小版本或补丁发布时,我都需要将这些additional_bindings扩展到我的每个方法中(上面只是举了一个例子)。


拥有已经包含RPC术语的东西是你所做的事情不符合REST的强烈指示。除此之外,如果你需要对API进行版本控制,那么你已经处于RPC领域了,在REST中,你会对媒体类型进行版本控制(类似于HTML,即v4.01v5.2),因为这是客户端和服务器约定交换消息的契约,如果他们协商使用HTML作为表示格式。 - Roman Vottner
谢谢@RomanVottner,但我不确定我理解了。是的,这些首先是gRPC服务,它们确实可以直接通过gRPC调用。但grpc-gateway的整个重点是将gRPC服务公开为REST Web服务。而且无疑,您希望始终保持版本控制策略,就像如果底层服务不是RPC一样。 - Dewald Swanepoel
1个回答

4
从 SemVer 规范中可以得知:
给定一个版本号 MAJOR.MINOR.PATCH,按以下方式递增:
1. 当您进行不兼容的 API 更改时,增加 MAJOR 版本; 2. 当您以向后兼容的方式添加功能时,增加 MINOR 版本;以及 3. 当您进行向后兼容的错误修复时,增加 PATCH 版本。
MAJOR.MINOR.PATCH 格式的预发布和构建元数据可作为扩展使用。
在 REST 端点版本控制方面,唯一重要的版本是 MAJOR 版本,因为所有 MINOR 和 PATCH 更改必须向后兼容。
回答您的问题: 在 REST URI 中仅使用主要版本号。从 REST 的角度来看,其余部分是实现细节。
因此,您的原型服务将是:
rpc MerchantGet (MerchantRequest) returns (MerchantResponse) {
    option (google.api.http) = {
        get: "/v1/myapi/merchant/{MerchantID}"
    };
}

谢谢,但我仍然不清楚proto处理程序的代码会是什么样子。考虑到输入(MerchantRequest)和/或输出(MerchantResponse)可能不同,我不能像在问题中的示例中那样使用additional_bindings。但是您已经消除了我对小版本的许多困惑。 - Dewald Swanepoel
1
“MerchantRequest”和“MerchantResponse” 的更改必须保持向后兼容(请记住在proto3中所有字段都是可选的,因此新字段始终是兼容的)。您的逻辑应该能够处理新的(可能缺少的)数据元素。 - SnoProblem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接