前言
在介绍 rk-boot/v2 之前,先分析一下 IT 企业/团队在进行基础设施搭建的时候,遇到的问题。
通过引入问题,可以更好的解释 rk-boot/v2 能为开发者和团队带来什么价值。
基础设施
基础设施的重要性毋庸置疑,团队业务经过【创业期】步入【拓展期】之后,我们都会开始琢磨基础设施的问题。
一定需要基础设施建设吗?
这个问题存在【博弈】,有很多业绩很好的团队/企业,没什么像样的基础设施,依然发展良好。有些基础设施良好的团队/企业,业绩却不怎么样。 所以,基础设施的好坏,并不是判断业务发展前景的【必要条件】。
如何判断需不需要基础设施建设?
实话实说,基础设施的建设有否,最关键的因素取决于【大领导】。这个现象无需争论,毕竟他们是掌舵人,需要根据大局判断下一步的计划。那如何去判断需不需要基础设施建设? 仅仅是个人意见,仅供参考。如果满足任意两个条件,则可考虑基础设施建设。
- 团队/企业人数大于 10人
- 团队业务属性为长期发展性业务
- 客户群体同属于 IT 行业
- 企业希望将团队/业务打造为团队标杆
- 团队人员结构不会有大变动
之所以列出上述几项,主要原因是为了避免【无用功】。
基础设施有什么好处?
基础设施的建设,无非就是通过降低成本(资源,人力,时间),提升企业/团队核心竞争力。有一点需要注意,基础设施不会带来直接收益,而是支撑持续发展。 如果团队/业务只是临时性的,或者说,属于边缘性业务,不推荐建设基础设施,反而会适得其反。
【代码】基础设施
基础设施里包含太多的方面,包括,代码,测试,集成,安全,成本等等。属于一个整体解决方案,并不是一套工具,一个解决方案供应商就能完美解决的。
这篇文章中,我们聊聊代码基础设施。代码应该存在那里,如果管理权限,如何控制代码泄漏风险,这类问题,我们先不讨论,搜索引擎会给出一大堆成熟方案。 我们重点聊聊【代码框架】的问题。
代码框架
当我们开始思考代码基础设施的时候,第一个想法就是搭建【自定义框架】,比如,Spring 脚手架。有时候还会创建一套框架。如果不是大企业,不要尝试创建一套框架,因为做出来了,也不会得到推广,只是闭门造车。 在已有框架之上,简单自定义是最好的选择。在自定义的过程中,尽量避免【无用的封装】,保持原生用法。个人推荐的方法是,自定义初始化,错误处理,日志处理,监控处理逻辑。核心逻辑不要动。 说白一点,就是自定义那些配置了一次就不会再动的代码,核心逻辑,留给社区,因为我们不是要创造出什么新东西出来。
合理的自定义框架应符合哪种条件?
- 可二次开发
- 提供模版
- 配置灵活(即可代码配置,也可参数文件配置)
- 功能,核心模块可扩展
- 跟随主流开源社区
在代码基础设施的搭建过程中,最困难的地方就是【众口难调】,很难把握住框架和业务逻辑的界线。要想解决这个问题,自定义框架本身需要满足上述条件,同时领导也要下【命令】,否则会在一片质疑声中,不了了之。
接下来就通过 rk-boot/v2 的例子来介绍一下如何搭建一个代码框架基础设施,希望对有意者有些帮助。
rk-boot/v2 介绍
在形容 rk-boot/v2 的时候,一直没有找到一个合适的词语,类似 Go版本的 Spring boot,也类似 Go 进程启动器。当然,功能,完整性,理念等远不及 Spring boot。
暂时还没有想到更好的词汇。不过不耽误介绍其用法和设计理念。
RK 设计理念
在设计 rk-boot/v2 的时候,我们有几个【不做清单】。
- 不创造 RPC 框架
- 不重复造轮子
- 不封装开源 API/函数,保留原生用法
- 不引入小众开源
- 不绑定特定业务
我们想要做的是,让使用者可以通过 YAML 配置文件,以及几行简单代码,一键启动【高标准】Golang 微服务。简单点说,就是在 YAML 文件里,填写几行配置,就能让进程自动提供监控,日志,安全等等附属功能。
整个 RK 系列由3个大部分组成。
| 模块 | 介绍 |
|---|---|
| rk-boot/v2 | 使用者的统一入口,Golang 项目推荐通过 rk-boot/v2 作为入口使用 |
| rk-entry/v2 | 所有 RK 系列的通用接口和通用功能实现,核心库,用户可以通过实现 rk-entry 里的接口,自定义 rk-xxx 插件 |
| rk-xxx | RK 系列插件,由一系列包组成,用户根据需要自行引入,每一个 rk-xxx 插件都可以应设成 YAML 文件里的配置 |
如果把实现 Golang 后端微服务比喻成制作一个产品,RK 在其中的作用相当于【材料提供商】,与使用【原生开源材料】不同点在于, 使用者不必考虑和学习如何初始化【原生开源材料】,如何配置,如何进行监控,错误处理。使用者唯一要做的就是,熟悉【原生开源材料】的使用方法,嵌入到代码里使用。 我们希望通过这种方式,节省开发者的时间成本,以及保证代码库的标准性。
使用者通过配置 boot.yaml 文件,快速让代码获得【装备】,使用者可以在代码中,轻松获得【原生开源材料】,并使用,无需考虑使用方式意外的问题。
RK 插件
RK 的插件我们使用了【节外生枝】的设计理念。对于 RK 来说,流行的开源产品,属于【原料】,这些【原料】通过实现 rk-entry 抽象,就形成了 RK 系列的一个插件,形成【材料】。
rk-boot/v2 会自动识别这些插件,在启动进程的时候,进行相应初始化。
RK 插件列表
目前,rk-boot/v2 还处于萌芽阶段,实现的功能插件比较少,后续会持续迭代,也希望能在开源社区里得到更多的关注,并参与进来。
- Web 框架中间件
| 中间件 | 介绍 |
|---|---|
| Prom | API prometheus 监控中间件 |
| Logging | API 日志中间件 |
| Trace | 基于 open-telemetry/opentelemetry-go API 调用链中间件 |
| Panic | Panic 捕获中间件 |
| Meta | 自动添加 RequestId,时间等 API 元数据中间件 |
| Auth | [Basic Auth] & [API Key] 授权中间件 |
| RateLimit | API 限流中间件 |
| Timeout | API 超时中间件 |
| Gzip | API 压缩中间件 |
| CORS | API CORS 中间件 |
| JWT | API JWT 验证中间件 |
| Secure | API 通用安全中间件 |
| CSRF | API CSRF 中间件 |
- Web 框架启动项
| 启动项 | 介绍 |
|---|---|
| Config | spf13/viper 初始化 |
| Logger | uber-go/zap 日志初始化 |
| Event | rk-query event 初始化 |
| Certificate | TLS/SSL 证书初始化 |
| Prometheus | Prometheus client 初始化 |
| Swagger | 内嵌 swagger UI |
| Docs | 内嵌 RapiDoc API 在线文档 |
| CommonService | 通用 API |
| StaticFileHandler | 内嵌静态文件下载 Web UI |
rk-boot/v2 例子
我们通过启动 gin-gonic/gin 服务,作为简单例子。更多例子可参考:例子
- 安装
go get github.com/rookie-ninja/rk-boot/v2
go get github.com/rookie-ninja/rk-gin/v2
- 创建 boot.yaml
---
gin:
- name: greeter # Required
port: 8080 # Required
enabled: true # Required
sw:
enabled: true # Optional, default: false
docs:
enabled: true # Optional, default: false
- 创建 main.go
// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/rookie-ninja/rk-boot/v2"
"github.com/rookie-ninja/rk-gin/v2/boot"
"net/http"
)
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot()
// Register handler
entry := rkgin.GetGinEntry("greeter")
entry.Router.GET("/v1/greeter", Greeter)
// Bootstrap
boot.Bootstrap(context.TODO())
boot.WaitForShutdownSig(context.TODO())
}
func Greeter(ctx *gin.Context) {
ctx.JSON(http.StatusOK, &GreeterResponse{
Message: fmt.Sprintf("Hello %s!", ctx.Query("name")),
})
}
type GreeterResponse struct {
Message string
}
- 运行
$ go run main.go
$ curl -X GET localhost:8080/v1/greeter?name=rk-dev
{"Message":"Hello rk-dev!"}
$ curl -X GET localhost:8080/rk/v1/ready
{
"ready": true
}
$ curl -X GET localhost:8080/rk/v1/alive
{
"alive": true
}
- Swagger UI: http://localhost:8080/sw
- Docs UI via: http://localhost:8080/docs