写点什么

才云 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:29733

评论

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

完美!腾讯技术官发布Android零基础就业宝典,不用再怀疑人生了

android 程序员 移动开发

巧用Android网络通信技术,在网络上直接传输对象(2),android开发入门基础教程

android 程序员 移动开发

已经说过千万遍了!!面试官,一线互联网大厂中高级Android面试真题收录

android 程序员 移动开发

孽缘啊,同套面试题中午我把你挂了,晚上你把我挂了,农民工看完都会了

android 程序员 移动开发

安卓11重磅上线,快来看看,是不是熟悉的配方,大专生面试阿里P7居然过了

android 程序员 移动开发

安卓指纹对称加密及登录功能的实现(1),技术实现

android 程序员 移动开发

应聘腾讯,面试官和我聊了一个小时的人生,flutterrow换行

android 程序员 移动开发

实现Flutter离线验证码组件,跳槽面试大厂被拒

android 程序员 移动开发

对标小程序 _ _快应用_开发入门指南,移动应用开发课程设计

android 程序员 移动开发

巧用Android网络通信技术,在网络上直接传输对象,android设计模式总结

android 程序员 移动开发

底层学习---Android-IPC机制(二)序列化机制,张口就来

android 程序员 移动开发

字节面试官:看了3000多份简历,面试1000场后,androidjni开发书籍

android 程序员 移动开发

学习 View 事件分发,就像外地人上了黑车!,真的已经讲烂了

android 程序员 移动开发

安卓指纹对称加密及登录功能的实现,阿里P8大牛从零开始教Android开源框架

android 程序员 移动开发

屌丝的眼泪,因为错过的初恋,才搞清楚PNG图片压缩原理(1)

android 程序员 移动开发

字节跳动正式启动2021届秋季校招!这份字节跳动历年校招Android面试真题解析,你确定不收藏

android 程序员 移动开发

学习不系统学过的Android知识全忘记了?思维导图+手写笔记

android 程序员 移动开发

学会这些Android开发热门前沿知识,2020年你就不用愁了!

android 程序员 移动开发

底层图像处理之微信32Kb图片压缩方案-(二),android双击事件响应

android 程序员 移动开发

年后想跳槽涨薪?你想要的面试题全在这里,activity事件分发

android 程序员 移动开发

年终总结攻略|3个步骤,让老板给你的工作汇报点赞,2021Android精选面试实战总结整理

android 程序员 移动开发

小厂开发五年,成功入职腾讯,这是我的复盘和总结,阿里P8Android架构师谈

android 程序员 移动开发

已拿字节、腾讯、墨墨 offer,【面试总结

android 程序员 移动开发

干货来袭-通过这份Android-中高级架构师教学资料(全套)在公司大裁员下

android 程序员 移动开发

字节跳动面试,第三面挂了,这原因我服了,快速上手

android 程序员 移动开发

工作出现瓶颈,学习效率下降?通学这份Android进阶知识体系

android 程序员 移动开发

巧用Android网络通信技术,在网络上直接传输对象(1),讲的真详细

android 程序员 移动开发

带你一起探究Android事件分发机制,-让面试提问不在畏惧!

android 程序员 移动开发

学习 View 事件分发,就像外地人上了黑车!(1),kotlin安卓开发书籍

android 程序员 移动开发

屌丝的眼泪,因为错过的初恋,才搞清楚PNG图片压缩原理

android 程序员 移动开发

巧用Android网络通信技术,在网络上直接传输对象 (1),安卓面试题宝典app

android 程序员 移动开发

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