写点什么

才云开源 Nirvana:Golang REST API 框架

  • 2019-01-24
  • 本文字数:4945 字

    阅读完需:约 16 分钟

才云开源 Nirvana:Golang REST API框架

AI 大模型超全落地场景&金融应用实践,8 月 16 - 19 日 FCon x AICon 大会联诀来袭、干货翻倍!

自 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 文档)。


在这个例子里,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 不仅能帮助开发者了解业务的运行状态,也为开发者定位业务问题提供了强有力的帮助。


  1. profiling 插件


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


  1. 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 直接提供文档服务(更多信息请查看文档)。


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


$ nirvana api --serve=":8081"


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



Nirvana 的客户端生成同样基于 API Definition ——读取类型信息,生成客户端包(目前仅支持生成 Golang 包)。客户可以直接引用这个包来调用服务,整个客户端的使用方式与本地调用一致(更多信息请查看文档)。


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


$ 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。它包含一个 API 组件 Cyclone Server, 基于 Nirvana 对外提供简单易用的 API。

Nirvana:涅槃

Nirvana,梵语中的涅槃。


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


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


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


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

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

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

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

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


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


也许你的加入,能让这场涅槃更加炫目!


感谢参与开源本项目的所有开发者!


Nirvana GitHub:https://github.com/caicloud/nirvana


Nirvana 文档:https://caicloud.github.io/nirvana/zh-hans/


2019-01-24 02:0015411

评论 1 条评论

发布
用户头像
2-3年没更新了,公司不在了吗
2023-10-24 10:33 · 北京
回复
没有更多了
发现更多内容

设计消息队列存储消息数据的 MySQL 表格

小虎

#架构训练营

Verilog HDL门级建模

timerring

FPGA

Sam Altman的成功学|升维指南

OneFlow

人工智能 深度学习

NodeJS 实战系列:如何设计 try catch

光毅

JavaScript node.js

CBM业务模型是什么和为什么?

涛哥 数字产品和业务架构

企业架构

认识区块链,认知区块链——NFT

MavenTalker

区块链 NFT web3 数字藏品

网络安全中API常见漏洞

郑州埃文科技

API漏洞

启科QuTrunk+Runtime+QuSaaS+AWS量子计算编程实战

启科量子开发者官方号

量子计算

云原生应用你应该这么管- 谐云发布基于KubeVela增强的应用版本管理和在线升级

谐云

云计算 Kubernetes OAM 容器云 企业号 2 月 PK 榜

程序员培训班哪家教的比较好

小谷哥

架构实战营4.3 存储分片分区架构随堂练习

西山薄凉

「架构实战营」

vivo 超大规模消息中间件实践之路

vivo互联网技术

kafka 中间件 pulsar 消息中间件 RoketMQ

15个 高并发系统设计 锦囊帮助你设计高并发!

风铃架构日知录

Java 程序员 后端 高并发 分库分表

Trie树简介及应用

京东科技开发者

数据结构 算法 高性能 双数组trie树 企业号 1 月 PK 榜

Source Map 原理

道道里

前端 webpack sourcemap

资产梳理与应用自动化部署技术实践

智维数据

大数据 数据可视化 智能运维 应用交付平台 流量分析系统

我们要的一种自学方式,也许是看书自学成才。

叶小鍵

认知篇:CQRS架构模式的本质

京东科技开发者

架构 微服务 后端 CQRS 企业号 1 月 PK 榜

WEB前端编程培训靠谱吗?

小谷哥

架构实战营4.2 存储复制架构随堂练习

西山薄凉

「架构实战营」

运维训练营第十二课作业

好吃不贵

火山引擎DataTester:“在字节,A/B实验是一种信仰”

字节跳动数据平台

大数据 字节跳动 AB testing实战

微信小程序实训|基于云数据库的语文听写工具

TiAmo

微信小程序 云开发 微信开发

一文揭晓,我是如何在Linux中查找自如

蔡农曰

Linux 程序员 运维 后端

Python 内置界面开发框架 Tkinter入门篇 乙

eng八戒

Python GUI tkinter

架构实战营4.4 如何设计存储架构随堂练习

西山薄凉

「架构实战营」

生产环境10分钟黄金时间快速排障:CPU不定时飙高怎么排查?

KINDLING

Java Linux 后端 监控 ebpf

如何使用 Java8 改造模板方法模式!

风铃架构日知录

Java 程序员 后端 设计模式 模板方法

高频JavaScript手写面试题

梁木由

JavaScript 前端 前端面试题

Java编程培训中心怎么样呢

小谷哥

数据可视化图表系列解析——瀑布图

Data 探险实验室

数据分析 数据可视化 图表 可视化数据

才云开源 Nirvana:Golang REST API框架_架构_才云_InfoQ精选文章