在SDK中公开微版本

虽然我们力求将OpenStack API设计得尽可能易于使用,但适用于各种编程语言的SDK对于开发者而言,始终是使用体验中重要的一部分。本文档包含了关于如何在针对OpenStack的SDK(软件开发工具包)中处理微版本的建议。

本文档识别出我们通常称为SDK的两种交付物。它们在向消费者公开微版本方面,推荐的方法将有所不同。

  • 高级SDK或简称SDK,是指向消费者隐藏底层API细节,并构建自身抽象层的SDK。它处理向后和向前兼容性以及功能发现的方法,独立于底层API所使用的方法。Shade是OpenStack此类SDK的一个示例。
  • 语言绑定紧密遵循底层API的结构和设计。它通常力求在底层API之上构建尽可能少的额外抽象层。示例包括所有OpenStackpython-<服务名称>客户端库。

注意

如有疑问,您应该编写一个高级SDK。使用SDK的好处在于以一种适合编程语言和任何所用框架的方式来使用API。像微版本这样的东西对于不专注于API设计的开发者来说,可能看起来很陌生和令人困惑。

本文档中使用的概念

消费者
与SDK交互的编程代码及其作者。
微版本

API版本,定义见:doc:microversion_specification。为简化起见,本指南将版本作为微版本的同义词。

注意

当在您的SDK中使用微版本时,请注意避免与语义版本控制产生关联。微版本与补丁版本不同,甚至在语义版本控制的意义上可以是主要版本。

主要版本

在:doc:microversion_specification的意义上并非真正的API版本,而是API的一个独立世代,与同一HTTP端点树中的其他世代共存。

主要版本通过URL中的/v<数字>部分进行区分,并且是微版本的第一个组成部分。例如,在微版本1.42, 1中,

注意

是主要版本。我们似乎没有为第二个组成部分设定一个既定的名称。

由于主要版本可能实质性地改变API的结构,包括改变微版本化的机制本身,因此SDK通常应尽量保持在所请求的主要版本范围内(如果有)。

协商
客户端和服务器之间就最适合的通用版本达成一致的过程。协商应该发生一次,其结果应该在整个会话中缓存。

注意

我们将在所有示例中使用Python编程语言,但这些建议适用于任何编程语言,包括静态编译语言。这里的示例将使用一个虚构的“猫即服务”API及其python-catsclientSDK。

高级SDK

通常,SDK不应将底层API微版本暴露给用户。输入和输出数据的结构不应依赖于所使用的微版本。应采用特定于编程语言和/或所用数据格式的方法来指示某些功能和行为的存在或缺失。

例如,在当前微版本中缺失的字段,可以在Python中表示为值,null值在Java中,或者其类型可以是Option<实际数据类型>在Rust中。

import catsclient

sdk = catsclient.SDK()

cat = sdk.get_cat('fluffy')
if cat.color is None:
    print("Cat colors are not supported by this cat server")
else:
    print("The cat is", cat.color)

在此示例中,SDK在get_cat调用期间协商可以返回尽可能多信息的API微版本。如果结果版本不包含颜色字段,则将其设置为.

。SDK应协商能更好地满足消费者需求的最高微版本。但是,它绝不应协商超出其编写和测试范围的微版本,以避免未来API更改时出现令人困惑的破坏。不言而喻,SDK不应在服务器返回的任何微版本上崩溃或表现出未定义的行为。任何不兼容性都应尽快以给定编程语言的自然形式表达。

例如,当调用一个SDK和服务器都支持的任何微版本中都无法表达的方法时,Python SDK应该抛出异常

import catsclient

sdk = catsclient.SDK()

cat = sdk.get_cat('fluffy')
try:
    cat.bark()
except catsclient.UnsupportedFeature:
    cat.meow()

允许在使用前检测支持的功能也很有用

import catsclient

sdk = catsclient.SDK()

cat = sdk.get_cat('fluffy')
if cat.can_bark():
    cat.bark()
else:
    cat.meow()

在这个例子中,can_bark使用协商的微版本来检查bark调用是否能够工作。

注意

如果可能,SDK应告知消费者所需的API微版本以及为何无法使用。这可能是微版本可以并且应该泄露给消费者的唯一地方。

如果可能,主要版本应以相同方式处理,不应向用户公开。如果不可能,SDK应从可用版本中选择最新的主要版本。

语言绑定

低级SDK,本质上只是API的语言绑定,它紧密遵循底层API。因此,它必须向消费者公开微版本,并且必须以最接近API处理方式的方式来完成。我们建议所有调用都接受一个显式的API微版本,该版本直接发送到底层API。如果没有提供,则不应发送任何版本

import catsclient

client = catsclient.v1.get_client()

cat = client.get_cat('fluffy')  # executed with no explicit version
try:
    cat.bark(api_version='1.42')  # executed with 1.42
except catsclient.IncompatibleApiVersion:
    # no support for 1.42, falling back to older behavior
    cat.meow()  # executed with no explicit version

注意

在某些编程语言中,特别是那些函数没有默认参数的语言,为所有调用添加版本参数可能不便。可以使用其他方法来实现相同的结果,例如临时上下文对象

import catsclient

client = catsclient.v1.get_client()

cat = client.get_cat('fluffy')  # executed with no explicit version
with cat.use_api_version('1.42') as new_cat:
    new_cat.bark()  # executed with 1.42

主要版本

低级SDK应该明确其正在使用哪个主要版本。可以通过命名空间API或接受显式主要版本作为参数来实现。首选方法取决于API主要版本之间的差异程度。

以Python为例,要么

import catsclient
client = catsclient.v1.get_client()

或者

import catsclient
client = catsclient.get_client(1)

支持的版本

强烈建议提供一种查询服务器以获取支持版本范围的方法

import catsclient

client = catsclient.v1.get_client()
min_version, max_version = client.supported_api_versions()

cat = client.get_cat('fluffy')  # executed with no explicit version
if max_version >= (1, 42):
    cat.bark(api_version='1.42')  # executed with 1.42
else:
    # no support for 1.42, falling back to older behavior
    cat.meow()  # executed with no explicit version

最小版本

应用程序通常有一个它们能够使用的基本最小API版本。建议提供一种方法来接受此类版本,并在未提供明确版本时将其用作默认值

import catsclient

try:
    client = catsclient.v1.get_client(api_version='1.2')
except catsclient.IncompatibleApiVersion:
    sys.exit("Cat API version 1.2 is not supported")

cat = client.get_cat('fluffy')  # executed with version 1.2
try:
    cat.bark(api_version='1.42')  # executed with 1.42
except catsclient.IncompatibleApiVersion:
    # no support for 1.42, falling back to older behavior
    cat.meow()  # executed with version 1.2

如本例所示,采用此方法的SDK必须提供一种清晰的方式来指出请求的版本不受支持,并尽快完成。

版本列表

作为简化扩展,语言绑定可以接受一个版本列表作为基本版本。服务器支持的最高版本必须被选中并用作默认值。

import catsclient

try:
    client = catsclient.v1.get_client(api_version=['1.0', '1.42'])
except catsclient.IncompatibleApiVersion:
    sys.exit("Neither Cat API 1.0 nor 1.42 is supported")

cat = client.get_cat('fluffy')  # executed with either 1.0 or 1.42
                                # whichever is available
if client.current_api_version == (1, 42):
    # Here we know that the negotiated version is 1.42
    cat.bark()  # executes with 1.42
else:
    # Here we know that the negotiated version is 1.0
    cat.meow()  # executes with 1.0

# The default version can still be overwritten
try:
    cat.drink(catsclient.MILK, api_version='1.66')  # executed with 1.66
except catsclient.IncompatibleApiVersion:
    # no support for 1.66, falling back to older behavior
    cat.drink()  # executed with either 1.0 or 1.42 whichever is available

目录

上一主题

表示结构约定

下一主题

标签

此页面