rk-boot/v2: 搭建团队/企业基础设施(代码篇)

479 阅读7分钟

前言

在介绍 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-xxxRK 系列插件,由一系列包组成,用户根据需要自行引入,每一个 rk-xxx 插件都可以应设成 YAML 文件里的配置

如果把实现 Golang 后端微服务比喻成制作一个产品,RK 在其中的作用相当于【材料提供商】,与使用【原生开源材料】不同点在于, 使用者不必考虑和学习如何初始化【原生开源材料】,如何配置,如何进行监控,错误处理。使用者唯一要做的就是,熟悉【原生开源材料】的使用方法,嵌入到代码里使用。 我们希望通过这种方式,节省开发者的时间成本,以及保证代码库的标准性。

使用者通过配置 boot.yaml 文件,快速让代码获得【装备】,使用者可以在代码中,轻松获得【原生开源材料】,并使用,无需考虑使用方式意外的问题。

RK 插件

RK 的插件我们使用了【节外生枝】的设计理念。对于 RK 来说,流行的开源产品,属于【原料】,这些【原料】通过实现 rk-entry 抽象,就形成了 RK 系列的一个插件,形成【材料】。

rk-boot/v2 会自动识别这些插件,在启动进程的时候,进行相应初始化。

RK 插件列表

目前,rk-boot/v2 还处于萌芽阶段,实现的功能插件比较少,后续会持续迭代,也希望能在开源社区里得到更多的关注,并参与进来。

分类开源插件
Web 框架gin-gonic/gin
gRPC
labstack/echo
gogf/gf
gofiber/fiber
zeromicro/go-zero
gorilla/mux
数据库 GORMMySQL
SQLite
SQL Server
postgreSQL
ClickHouse
MongoDB
Redis
CacheRedis
CloudAWS
AWS/KMS
AWS/KMS/Signer
AWS/KMS/Crypto
  • Web 框架中间件
中间件介绍
PromAPI prometheus 监控中间件
LoggingAPI 日志中间件
Trace基于 open-telemetry/opentelemetry-go API 调用链中间件
PanicPanic 捕获中间件
Meta自动添加 RequestId,时间等 API 元数据中间件
Auth[Basic Auth] & [API Key] 授权中间件
RateLimitAPI 限流中间件
TimeoutAPI 超时中间件
GzipAPI 压缩中间件
CORSAPI CORS 中间件
JWTAPI JWT 验证中间件
SecureAPI 通用安全中间件
CSRFAPI CSRF 中间件
  • Web 框架启动项
启动项介绍
Configspf13/viper 初始化
Loggeruber-go/zap 日志初始化
Eventrk-query event 初始化
CertificateTLS/SSL 证书初始化
PrometheusPrometheus 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
}