所有文章 > API开发 > Amazon的API描述语言Smithy概述
Amazon的API描述语言Smithy概述

Amazon的API描述语言Smithy概述

描述语言是定义开发周期的一种非常有效且高效的方法。它们可以帮助解锁新的迭代、精细控制以及对逻辑和底层系统的深入理解。一种新的定义语言 Smithy 的用户群正在稳步增长。在 Amazon 的支持下,这种接口描述语言 (IDL) 提供了高效且可移植的基于模型的解决方案。

下面,我们将研究Smithy ,看看它的独特之处。我们还将考虑使用 Smithy 的一些具体优点和缺点,并了解它在实践中的工作原理。

什么是Smithy?

那么,史密斯到底是什么? Smithy是一个与协议无关的 IDL。从最基本的角度来说,IDL 旨在通过定义接口本身来连接系统,从而消除对语言相似性或内聚性的需求。 Smithy 既是一个 IDL,也是一组相关工具,承诺通过可以利用代码生成来创建新工件的模型来解锁客户端、服务器和文档。

Smithy 是由 Amazon 的一个团队开发的,旨在创建一个代码生成建模系统,该系统解决了类似系统当前存在的许多问题。代码生成通常依赖于协议或框架,并且哪些环境或协议可以在这些系统中工作的特殊性受到很大限制。 Smithy 的目的是创建一些与协议无关、可以在各种环境中工作的东西。

Smithy如何工作?

Smithy的工作方式是创建模型。这些模型是构建其他一切的基本模块。它们是围绕资源和操作的思想构建的,这是大多数开发人员应该熟悉的常见范例。

The Smithy model as defined in its documentation.

Smithy 模型在其文档中定义。

通过使用语义模型中的形状和特征,可以实现额外的扩展和可定制性。这些特征可以帮助约束或定义形状的特定元素,从而允许更精细的模型创建和开发。

The Smithy model as defined in its documentation.

Smithy 模型在其文档中定义。

这种特征的使用允许更高的定制和约束,并且对于 Smithy,元模型特征进化是一个关键功能。基于资源的模型规范和特征演化之间的这种结合使 Smithy 能够变形并具有令人难以置信的可扩展性。 Smithy 还提供了一个相当强大的验证系统,以确保这些变形的更改在更大的模型规则和策略中仍然有效。

更进一步,Smithy 甚至允许模型本身根据兴趣进行分段和变形。模型的不同部分可以由不同的团队拥有,从而允许更好的协作以及对整体各个部分的影响进行细分。这是朝着将协作构建到底层系统中迈出的一大步。 Smithy 提供的投影系统还允许为不同的受众创建模型视图,而无需分割或更改核心模型本身。这允许创建不同的分支和专门构建的模型,例如,作为 B2B 游戏。

最终,Smithy 专注于创建一个单一模型,无论环境如何,都可以使用该模型来创建、变形和扩展——这是一个崇高的目标,反映了微服务和不可知部署的发展。 IDL 分为三个部分,有助于约束和定义输出:

  • Control :定义 IDL 的版本以及生成过程的其他控件。
  • 元数据:定义并应用描述模型的元数据。
  • 形状:定义数据结构的形状和特征。

实践中的Smithy

以下是文档中提供的基本示例 – 该代码在 Smithy 中形成了天气服务:

$version: "2"

namespace example.weather

/// Provides weather forecasts.
@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize")
service Weather {
version: "2006-03-01"
resources: [
City
]
operations: [
GetCurrentTime
]
}

resource City {
identifiers: { cityId: CityId }
properties: { coordinates: CityCoordinates }
read: GetCity
list: ListCities
resources: [
Forecast
]
}

resource Forecast {
identifiers: { cityId: CityId }
properties: { chanceOfRain: Float }
read: GetForecast
}

//"pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input := for City {
//"cityId" provides the identifier for the resource and
// has to be marked as required.
@required
$cityId
}

output := for City {
//"required" is used on output to indicate if the service
// will always provide a value for the member.
//"notProperty" indicates that top-level input member "name"
// is not bound to any resource property.
@required
@notProperty
name: String

@required
$coordinates
}

errors: [
NoSuchResource
]
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float

@required
longitude: Float
}

//"error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

// The paginated trait indicates that the operation may
// return truncated results.
@readonly
@paginated(items: "items")
operation ListCities {
input := {
nextToken: String
pageSize: Integer
}

output := {
nextToken: String

@required
items: CitySummaries
}
}

// CitySummaries is a list of CitySummary structures.
list CitySummaries {
member: CitySummary
}

// CitySummary contains a reference to a City.
@references([
{
resource: City
}
])
structure CitySummary {
@required
cityId: CityId

@required
name: String
}

@readonly
operation GetCurrentTime {
output := {
@required
time: Timestamp
}
}

@readonly
operation GetForecast {
input := for Forecast {
//"cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
@required
$cityId
}

output := for Forecast {
$chanceOfRain
}
}

将其分成单独的部分,我们可以在顶部看到代码的控制部分:

$version: "2"

下面是实质性代码:

namespace example.weather

/// Provides weather forecasts.
@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize")
service Weather {
version: "2006-03-01"
resources: [
City
]
operations: [
GetCurrentTime
]
}

resource City {
identifiers: { cityId: CityId }
properties: { coordinates: CityCoordinates }
read: GetCity
list: ListCities
resources: [
Forecast
]
}

resource Forecast {
identifiers: { cityId: CityId }
properties: { chanceOfRain: Float }
read: GetForecast
}

这里我们有服务的定义(及其分页)以及所利用的资源以及对这些资源采取的操作。

//"pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input := for City {
//"cityId" provides the identifier for the resource and
// has to be marked as required.
@required
$cityId
}

output := for City {
//"required" is used on output to indicate if the service
// will always provide a value for the member.
//"notProperty" indicates that top-level input member "name"
// is not bound to any resource property.
@required
@notProperty
name: String

@required
$coordinates
}

errors: [
NoSuchResource
]
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float

@required
longitude: Float
}

//"error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

// The paginated trait indicates that the operation may
// return truncated results.
@readonly
@paginated(items: "items")
operation ListCities {
input := {
nextToken: String
pageSize: Integer
}

output := {
nextToken: String

@required
items: CitySummaries
}
}

// CitySummaries is a list of CitySummary structures.
list CitySummaries {
member: CitySummary
}

// CitySummary contains a reference to a City.
@references([
{
resource: City
}
])
structure CitySummary {
@required
cityId: CityId

@required
name: String
}

@readonly
operation GetCurrentTime {
output := {
@required
time: Timestamp
}
}

@readonly
operation GetForecast {
input := for Forecast {
//"cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
@required
$cityId
}

output := for Forecast {
$chanceOfRain
}
}

从这里开始,我们有一系列特征定义和嵌套结构,允许在模型内排列输出和功能,从而更好地控制基本输出和功能。查看此代码,您可以看到特征定义如何解锁功能控制。本质上,特征允许在不从根本上改变底层实体的情况下约束和格式化数据和服务。

Smithy的优点和缺点

考虑到这一点,使用像 Smithy 这样的东西有什么优点和缺点?

优点

对于大多数用户来说,采用 Smithy 的最大好处是它与协议无关。并非所有开发都经过提前考虑,您今天使用的协议永远都是正确的。采用像 Smithy 这样的解决方案,您可以为当前的工作创建模型,这些模型可以随着新的开发而变形、改变和改变。这为您提供了很大的灵活性,最终使您的开发摆脱了严格的限制。

专注于模型驱动系统可以实现机器和人类的高效可读性。模型可以从一个系统移植到另一个系统,并转换为其他格式和类型,从而将底层系统从外部控制的负担中解放出来。模型也很容易比较,可以实现更好的自动化,从而提高效率。

Smithy 也是开源的,这是一个巨大的好处!与协议无关的解决方案很棒,但是当这种解决方案的开发也与源代码控制解耦时,可能会使其关闭并集中控制,这会使其开发和迭代速度更快。 Smithy 是一个可以独立完成很多事情的工具,但从长远来看,将其开放给社区开发和迭代可以使其成为一些非常令人惊奇的事情的平台!

缺点

虽然 Smithy 在其基于模型的系统上做了很多出色的工作,但它提供代码生成作为一个重要功能。事实上,代码生成仍然存在重大问题,即使这里提供了大量的修复和系统,这些问题也不会消失。使用 Smithy 进行代码生成通常很有效,但与任何代码生成工具一样,99% 的情况下,输出都可以,但这 1% 意味着您需要非常密切地关注并投入资源进行审查,这削弱了从代码生成中获得的一些好处。

Smithy 功能齐全。在某些情况下,这可能是负面的。如果您希望构建一些非常简单的东西,只有少数人与之交互,那么为其他观看者进行投影或为多个团队创建模型访问规则的想法可能不适合您。在这种情况下,让史密斯加入的成本可能不合理。与任何事情一样,您应该考虑 Smithy 是您需要使用的东西还是您只想使用的东西。

最后,需要简单说明的是,这是亚马逊开发的产品。虽然这当然不应该是采用或不采用工具的唯一原因,但该项目的长期维护和健康与亚马逊本身的长期维护和健康息息相关。虽然亚马逊看起来不会很快走向任何地方,但对于那些在网络开发领域工作了很长一段时间的人来说,在其首次发布十年或二十年之后,企业发展陷入困境和崩溃的故事比比皆是,而这个肯定应该是一个考虑因素。即使产品是开源的,开源并不意味着永久开发,并且在任何特定时刻都可能会认为 Smithy 不值得花费时间或资源。

开源软件也有转变为更多商业许可证的趋势。从版本 2.15 开始,Buoyant Enterprise for Linkerd 宣布 Linkerd 将不再生成开源稳定版本。截至 2024 年,Redis 有争议地放弃了 BSD 许可证,引起社区许多人的严重担忧。不幸的是,这些故事很常见,并说明了像 Smithy 这样的解决方案的一个巨大问题——它适合开源框架,直到它不适合,而这种转变可能会产生巨大的影响。

 结论

最终,对于采用强大的基于模型的解决方案的系统来说,Smithy 是一个不错的选择。虽然它对于所有实现来说可能都太重了,但它的灵活性和可扩展性使其成为许多情况下可靠的价值主张。

原文链接:https://nordicapis.com/overview-of-smithy-an-api-description-language-from-amazon/

#你可能也喜欢这些API文章!