写点什么

才云 Caicloud 开源 Nirvana:让 API 从对框架的依赖中涅槃重生

  • 2020-03-03
  • 本文字数:5032 字

    阅读完需:约 17 分钟

才云 Caicloud 开源 Nirvana:让 API 从对框架的依赖中涅槃重生

自 2009 年开源以来,Go 作为一种强大、高效、简洁、易上手的编程语言,在帮助阅读、调试和维护大型软件系统上发挥着越来越重要的作用。而依托其健康生态,Golang 社区也相继涌现出诸如 beego、gin、chi、go-restful 等知名框架,为 Go 提供额外功能支持。


但选择过多,反受其乱。面对层出不穷的优秀框架,不同团队、不同开发者在框架选择上往往会出现分歧,不同框架之间也彼此壁垒高筑,导致业务与框架耦合,开发效率大大降低。


为了解决这类问题,才云 Caicloud 实现了 Golang API 框架 Nirvana,把 API 从对框架的依赖中彻底解放出来。它专为提高生产力和可用性设计,可扩展、性能高,旨在成为才云 Caicloud 所有 Golang 服务的基石,助力业务的高速开发。

接轨 Kubernetes 这些痛点不可忽视

近年来,为了满足业务发展需要,应对日益激烈的市场竞争,大量企业开始使用容器部署云工作负载。而随着容器使用量的增长、Kubernetes 成为容器编排的事实标准,在 Kubernetes 上打造新一代容器云平台成了企业谋求发展的必由之路。


为了顺应时局,技术团队除了进行思维上的转变,还要应对团队和业务方向的调整,对团队项目和产品完成切割分化。这之中就涉及对 Go 框架变更的抉择。


两年前,才云 Caicloud 团队在构建基于 Kubernetes 的云平台时,在框架上遇到了不少麻烦:当时 Kubernetes 正值发展期,为了快速开发,工程师们往往倾向于选择自己熟悉的框架。因此,虽然大多数项目用的是 go-restful(Kubernetes 的选型),但用 beego、gin、chi 等框架的工程师也不在少数,这就给业务整合带来了很多问题:


  • 不同框架对 API 的描述形式不一致,这增加了不同团队间的沟通成本;

  • 不同框架 API 风格差距较大,文档自动化困难;

  • 错误定义和处理方式在不同项目之间存在差别,会导致客户端处理困难。


曾经开放的工程师文化成了变革的最大阻力。

业务和框架隔离 Nirvana 出奇招

Nirvana 就是在这个背景下诞生的。


它借鉴了 Kubernetes 声明式 API 的设计,巧妙规避了框架对 API 的侵入。同时,为了实现业务和框架的隔离,它定义一个规范,让 API 按照规范书写,完全屏蔽了框架对 API 的影响。


一言以蔽之,Nirvana 框架的设计思路始终围绕工程师们亲历的种种痛点:


  • 构建风格一致的 API 项目;

  • 使用统一的 RESTful 路由与声明式 API 定义,避免业务对框架产生依赖;

  • 使用统一的错误生成和处理方式;

  • 提供开箱即用的基础功能,包括 Prometheus metrics、tracing、profiling 等;

  • 自动生成 API 文档和客户端代码。


在 Nirvana 中,我们用一套 API Definition 来声明式地描述业务 API。下面是一个列出消息列表 API 的例子:


Definition{    // 这个 API 返回的是资源数组,所以使用 List 方法。    Method:     def.List,    // Summary 是一个短语,用于描述这个 API 的用途。这个短语在生成文档和客户端的时候用于区分 API。    // 这个字符串去掉空格后会作为生成客户端时的函数名,因此请确保这个字符串是有意义的。    Summary:    "List Messages",    // 详细描述这个 API 的用途。    Description: "Query a specified number of messages and returns an array",    // 业务函数    Function:   message.ListMessages,    // 对应业务函数的参数信息。用于告知 Nirvana 从请求的那一部分取得数据,然后传递给业务函数。    Parameters: []def.Parameter{        {            // 参数来源            Source:     def.Query,            // 参数名称,作为 key 从 Source 里取值。            // 与业务函数的参数名称无关。            Name:       "count",            // 默认值            Default:    10,            // 参数描述            Description: "Number of messages",        },    },    // 对应业务函数的返回结果。用于告知 Nirvana 业务函数返回结果如何放到请求的响应中。    Results: def.DataErrorResults("A list of messages"),}
复制代码


而对应业务 API 的形式则是:


type Message struct {
ID int `json:"id"` Title string `json:"title"` Content string `json:"content"`}
func ListMessages(ctx context.Context, count int) ([]Message, error) { messages := make([]Message, count) for i := 0; i < count; i++ { messages[i].ID = i messages[i].Title = fmt.Sprintf("Example %d", i) messages[i].Content = fmt.Sprintf("Content of example %d", i) } return messages, nil}
复制代码


很显然,业务函数并不关心自身是如何暴露给外部的,实现方法也和其他内部函数没有差别(这只是一个简单的例子,更多详细内容请查阅 Nirvana Definition 文档:https://caicloud.github.io/nirvana/zh-hans/topics/definition.html)。


在这个例子里,API Definition 描述了完整的 API 结构,包括 RESTful 路径、请求方法、请求描述、请求参数、请求响应和请求 handler。框架只需要解析 API Definition,就能得到业务逻辑的入口和出口处理方式。对开发者而言,API 的开发过程从“ 命令式路由 + 数据转换 + 业务逻辑” 变成了“API Definition + 业务逻辑”。框架与业务逻辑之间通过 API Definition 进行桥接。

Nirvana 框架

API Definition 作为声明式 API,除了让框架读取信息生成路由和对外提供服务,它本身也完整描述了一个 API 的工作方式。因此,我们还能用它生成 openapi 文档和客户端。在接下来的几个小节中,我们将详述 Nirvana 提供的这些能力。

路由工作流

任何一个 API 框架都具备基本的 HTTP Serve 能力( HTTP 接口服务能力)。在 Nirvana 中,HTTP Serve 由 Golang 基础库 http 提供。HTTP 请求的路由方式如下图所示:



在这个请求的工作流中:


  • Filters 是请求过滤器,可以对不符合要求的请求进行提前响应,而不会进入路由匹配过程;

  • Middlewares 是围绕请求的中间件,能够控制请求的处理行为;

  • Executor 是最终处理请求的执行器,负责通过参数生成器(ParameterGenerators)和结果处理器(DestinationHandlers)将请求注入到业务逻辑之中,并将返回结果处理后返回。

  • 在这里,Filters、Middlewares、ParameterGenerators 和 DestinationHandlers 都是可以被开发者重新定义的。这也意味着开发者可以改变 Nirvana 的原生处理行为,使之符合自身需求。

服务构建工作流

Nirvana 另一个工作流是将 API Definition 构建到路由中去,并完成整个服务的启动工作。



在 Nirvana 启动时,除 API Definition 之外,它还能够通过插件往框架里注入一些功能,包括注册 Filters、Middlewares、添加其他路由 endpoint 等。目前 Nirvana 已经提供对 metrics、profiling、tracing、log 等常用插件的支持。


通过以上两个工作流,以及 Nirvana 中大量的模块方法注册接口,开发者可以自行扩展 API Definition 的能力,让 API Definition 与业务逻辑更加贴合。API Definition 单向依赖并描述了业务逻辑,并作为框架和业务之间的桥梁,既达到了声明式 API 的定义形式,也满足了业务不依赖框架的目标。

框架扩展

无论是路由还是服务构建过程,Nirvana 的实现都不是 hardcode 的。在框架的大部分包里,你能发现类似以下形式的代码结构:


var consumers = map[string]Consumer{    definition.MIMENone:        &NoneSerializer{},    definition.MIMEText:        NewSimpleSerializer(definition.MIMEText),    definition.MIMEJSON:        &JSONSerializer{},    definition.MIMEXML:         &XMLSerializer{},    definition.MIMEOctetStream: NewSimpleSerializer(definition.MIMEOctetStream),    definition.MIMEURLEncoded:  &URLEncodedConsumer{},    definition.MIMEFormData:    &FormDataConsumer{},}
// RegisterConsumer register a consumer. A consumer must not handle "*/*".func RegisterConsumer(c Consumer) error { if c.ContentType() == definition.MIMEAll { return invalidConsumer.Error(definition.MIMEAll) } consumers[c.ContentType()] = c return nil}
复制代码


在这种结构里,开发者可以自定义每一块的实现方式,甚至替换框架默认行为。帮助自己最大限度地利用 Nirvana,使它符合业务特定需求。

插件机制

在服务构建工作流中,我们提到了插件机制。下面我们会介绍三个重要插件 metrics、profiling 和 tracing。


1. metrics 插件


Prometheus 作为 CNCF 的开源项目,为我们提供了非常适于描述业务指标的格式:Prometheus Metrics。Nirvana 提供的 metrics 插件可以在启动时默认暴露 /metrics 接口。对此,开发者可以将业务指标注册到 Prometheus 中,通过这个接口让 Prometheus 采集。也就是说,Nirvana 不仅能帮助开发者了解业务的运行状态,也为开发者定位业务问题提供了强有力的帮助。


2. profiling 插件


Golang 提供了进程级别的 profiling 能力。Nirvana 通过 profiling 插件直接将这个能力注入到框架中。开发者只需要启用插件即可获得这些调试和性能测试能力,获得业务内部执行的状态信息。


3. tracing 插件


在 CloudNative 时代,业务通常会以微服务或 Serverless 的形式进行架构。多个服务之间的交互通常是黑盒的,这易导致我们难以定位分布式架构中的服务问题。Tracing 作为解决分布式架构下的调用链方案,可以在分布式系统出现问题时为开发者提供大量的信息,帮助开发者排查问题。Nirvana 的 tracing 插件使用了 open-tracing 的接口规范,并借助 CNCF 开源项目 jaeger 的能力,为开发者提供一站式 tracing 方案。

文档和客户端生成

API Definition 和 Nirvana 的结合帮助我们完成了服务构建。在服务之外,通过 API Definition 的描述能力,Nirvana 还实现了基于 openapi 的文档生成和客户端生成。


Nirvana 的文档生成基于 openapi 2.0(即 swagger 2.0)规范,从 API Definition 中提取 API 信息,生成 API 描述文件 swagger.json。除了生成 API 描述文件之外,它还能通过 ReDoc 直接提供文档服务(更多信息请查看 https://caicloud.github.io/nirvana/zh-hans/guides/doc.html)。


在 Nirvana 中,生成文档的方式十分简单:


$ nirvana api --serve=":8081"
复制代码


只需一条命令,API 文档即可生成,并通过 8081 端口提供服务。之后我们就能通过网页查看文档了:



Nirvana 的客户端生成同样基于 API Definition ——读取类型信息,生成客户端包(目前仅支持生成 Golang 包)。客户可以直接引用这个包来调用服务,整个客户端的使用方式与本地调用一致(更多信息请查看 https://caicloud.github.io/nirvana/zh-hans/guides/client.html)。


下面是一键生成客户端的示例:


$ nirvana client
复制代码


这个命令会在当前目录下生成一个 client 目录,在这个目录内生成 Golang 客户端代码:


type Client struct {    rest *rest.Client}
// ListMessages returns all messages.func (c *Client) ListMessages(ctx context.Context, count int) (messages []Message, err error) { err = c.rest.Request("GET", 200, "/apis/v1/messages"). Query("count", count). Data(&messages). Do(ctx) return}
复制代码


如果您想进一步了解 Nirvana,可以参考才云 Caicloud 云原生 CI/CD 项目 Cyclone(更多信息请查看 https://github.com/caicloud/cyclone)。它包含一个 API 组件 Cyclone Server, 基于 Nirvana 对外提供简单易用的 API。

Nirvana:涅槃

Nirvana,梵语中的涅槃。


一如我们对它的期待:让 API 从对框架的依赖中涅槃重生。


经过一年多的开源,目前 Nirvana 在基础 API 服务能力上已经经过内部验证。由于数据传输层和转换层的复杂度被大大降低,才云 Caicloud 也切实体验到了这个开源框架带来的便捷,以及它在加速业务开发上的显著效果。


未来,Nirvana 将在以下几方面继续发力:


  • 持续优化扩展文档和客户端生成的能力,降低开发者在这两块上的心智负担;

  • 持续优化 metrics、profiling、tracing 的能力,并增加新的云原生能力,让这些能力成为云原生应用的标配;

  • 框架模块化加强,让 Nirvana 的每一块代码皆可定制;

  • 优化框架性能,降低反射对服务的影响;

  • 让 Nirvana 成为 Golang 的 CloudNative & SOA 框架。


Nirvana 正致力于成为让业务无感知的框架。目前,才云 Caicloud 正在这条路上踽踽独行,我们也真诚地希望将来能有更多开发者愿意加入进来,一起构建 Nirvana,一起建设 Golang 社区,一起拥抱开源的胜利。


本文转载自才云 Caicloud 公众号。


原文链接:https://mp.weixin.qq.com/s/Nd2DrV6H0JN25ccHnuMRpQ


2020-03-03 17:29631

评论

发布
暂无评论
发现更多内容

架构师训练第七周总结

Hanson

架构师培训第七周练习

小蚂蚁

全国第一枚企业区块链电子印章诞生

CECBC

萝卜章 区块链印章 全流程上链 e签宝

乘风破浪的文思海辉,以及TA的行业数字化“新人设”

脑极体

【DevCloud·敏捷智库】如何利用故事点做估算

华为云开发者联盟

敏捷 敏捷开发 需求 故事 华为云

【week07】作业

chengjing

工作总结

Axe

JAVA已过气?中俄大佬对话告诉你俄罗斯最受欢迎的编程语言是什么!

华为云开发者联盟

Java 开源 程序员 Lambda 编程语言

第7周作业一

张倩

性能测试

考尔菲德

埋点全解析,你最关心的可视化埋点在这里!

易观大数据

性能压测工具

武鹏

第7章总结

武鹏

计算机网络基础(九)---网络层-内部网关路由协议

书旅

计算机网络 网络协议 操作系统 计算机基础

MySQL常用函数

Bruce Duan

mysql常用函数

SpringBoot分布式验证码登录方案

Bruce Duan

验证码 Kaptcha

【week07】总结

chengjing

通过双 key 来解决缓存并发问题

Bruce Duan

缓存穿透 缓存并发 双key解决缓存并发

一周信创舆情观察(7.13~7.19)

统小信uos

数据库 舆情 芯片

《深度工作》学习笔记(1)

石云升

读书笔记 专注 深度工作

作业1

东哥

极客大学架构师训练营

【研报下载】InfoQ《2020中国技术发展白皮书》重磅发布

InfoQ写作社区官方

写作平台 InfoQ 白皮书 研究报告 热门活动

Kubernetes 1.0 发布刚六周年,IBM 却想招 12 年经验的

神经星星

程序员 Kubernetes 云原生 招聘 ibm

你只加了两行代码,为什么要花两天时间?

Yukun

程序员 debug bug

总结

张倩

架构师第七周学习总结

小蚂蚁

阿里官方 Redis 开发规范

Bruce Duan

redis Redis开发规范

秒懂云通信:如何用阿里云平台发短信?

阿里云Edge Plus

架构师训练营作业-web性能压测示例代码

superman

极客大学架构师训练营

报销流程太慢太复杂?区块链技术引入票据系统效率翻一倍

CECBC

数据共享 电子票据 优化业务 可信体系

架构师训练营第七周作业

Bruce Xiong

才云 Caicloud 开源 Nirvana:让 API 从对框架的依赖中涅槃重生_文化 & 方法_才云科技_InfoQ精选文章