自学内容网 自学内容网

动手搓一个kubernetes管理平台(3)-后端框架

后端框架的选择面比较大,由于不涉及复杂的调度/分布式管理等场景,所以后端选用一个标准的web server即可,比如gin, iris, beego等等,因为正好最近在看iris的一些项目,所以就选用了irsi+corba的框架进行后端开发 。

通过cobra进行初始化的操作就不在赘述,这边先show一下相关的目录结构

cmd # 启动命令
common # 通用组件,如mysql, conf, log等
conf # 配置表
core/server #  核心服务,http server
dao/v1 # 数据层
docs # 文档
http # 接口层
img # readme的截图,架构图
migrate # 首次启动的数据同步 
pkg # 非通用组件,如k8s客户端,websocket等
service/v1 # 业务逻辑的封装
structs/v1 # 所有结构体
main.go # 入口

封装KubeMgrServer的对象

package server

import (
"fmt"
"github.com/iris-contrib/swagger/v12"
"github.com/iris-contrib/swagger/v12/swaggerFiles"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/sessions"
"github.com/kataras/iris/v12/sessions/sessiondb/redis"
cfg "kubemgr/common/configparse"
log "kubemgr/common/formatlog"
"kubemgr/common/i18n"
"kubemgr/common/mysql"
redis_common "kubemgr/common/redis"
_ "kubemgr/docs" // docs is generated by Swag CLI, you have to import it.
"kubemgr/migrate"
"strings"
"time"
)

// session管理
const SessionCookieName = "KUBEMGR_SESS"

var SessionMgr *sessions.Sessions

type KubeMgrServer struct {
app        *iris.Application
rootRoute  iris.Party
configPath string
bind       string
}

var km *KubeMgrServer

// 注册redis
func NewRedis() *redis.Database {
db := redis.New(redis.Config{
Network:   "tcp",
Addr:      cfg.GlobalConf.GetStr("common", "redisbind"),
Timeout:   time.Duration(30) * time.Second,
MaxActive: cfg.GlobalConf.GetInt("common", "redismaxactive"),
Password:  cfg.GlobalConf.GetStr("common", "redispasswd"),
Database:  cfg.GlobalConf.GetStr("common", "redisdb"),
Prefix:    cfg.GlobalConf.GetStr("common", "redisprefix"),
Driver:    redis.GoRedis(), // redis.Radix() can be used instead.
})
// defer db.Close()
return db
}

// 启动服务
func Listen(route func(party iris.Party), confgPath string, serverBindHost string, serverBindPort int) error {
bind := fmt.Sprintf("%v:%v", serverBindHost, serverBindPort)
km = NewServer(confgPath, bind)
route(km.rootRoute)
return km.app.Run(iris.Addr(bind))
}

// 初始化服务对象
func NewServer(confgPath, bind string) *KubeMgrServer {
c := &KubeMgrServer{}
c.app = iris.New()
c.configPath = confgPath
c.bind = bind
return c.bootstrap()
}

// 初始化KubeMgrServer对象,初始化各个模块
func (e *KubeMgrServer) bootstrap() *KubeMgrServer {
e.setUpRootRoute()
e.setUpConfig()
e.setUpLogger()
e.setUpDB()
e.setUpSession()
e.setResultHandler()
e.setUpErrHandler()
e.setDBInitial()
return e
}

// 初始化路由,所有路由的/ 路径,均为kubemgr
func (e *KubeMgrServer) setUpRootRoute() {
e.app.Any("/", func(ctx *context.Context) {
ctx.Redirect("/kubemgr")
})
c := swagger.Config{
URL: "/kubemgr/swagger/doc.json",
}
e.app.Get("/kubemgr/swagger/{any:path}", swagger.CustomWrapHandler(&c, swaggerFiles.Handler))
e.rootRoute = e.app.Party("/kubemgr")
}

// 初始化配置文件
func (e *KubeMgrServer) setUpConfig() {
cfg.GlobalConf.CfgInit(e.configPath)
}

// 初始化日志
func (e *KubeMgrServer) setUpLogger() {
// logname := cfg.GlobalConf.GetStr("common", "logname")
loglevel := cfg.GlobalConf.GetStr("common", "loglevel")
log.InitLog(loglevel)
}

// 初始化DB
func (e *KubeMgrServer) setUpDB() {
mysql.DB.InitConn()
redis_common.DB.NewRds()
}

// 初始化session
func (e *KubeMgrServer) setUpSession() {
SessionMgr = sessions.New(sessions.Config{Cookie: SessionCookieName, AllowReclaim: true, Expires: time.Duration(cfg.GlobalConf.GetInt("common", "sessiontimeout")) * time.Hour})
db := NewRedis()
SessionMgr.UseDatabase(db)
e.rootRoute.Use(SessionMgr.Handler())
}

// 初始化数据库实例
func (e *KubeMgrServer) setDBInitial() {
DataSourceUrl := fmt.Sprintf("%s%s%s", "mysql://", cfg.GlobalConf.GetStr("mysql", "datasource"), "?multiStatements=true")
migrate.InitDB(DataSourceUrl, 5*time.Second)
}

// 初始化response
func (e *KubeMgrServer) setResultHandler() {
e.rootRoute.Use(func(ctx *context.Context) {
ctx.Next()
if ctx.GetStatusCode() >= iris.StatusOK && ctx.GetStatusCode() < iris.StatusBadRequest {
if ctx.Values().Get("token") != nil {
_, _ = ctx.Write(ctx.Values().Get("token").([]uint8))
} else {
resp := iris.Map{
"success": true,
"data":    ctx.Values().Get("data"),
}
_ = ctx.JSON(resp)
}
}
})
}

// 捕获异常返回
func (e *KubeMgrServer) setUpErrHandler() {
e.rootRoute.OnAnyErrorCode(func(ctx iris.Context) {
//如果报错message为空,且errcode为404,则进行错误返回
if ctx.Values().GetString("message") == "" {
switch ctx.GetStatusCode() {
case iris.StatusNotFound:
ctx.Values().Set("message", "the server could not find the requested resource")
}
}

message := ctx.Values().Get("message")
if message == nil || message == "" {
message = ctx.Values().Get("iris.context.error")
}
// 默认接口返回错误信息为US
lang := ctx.Values().GetString("language")
if lang == "" {
lang = i18n.LanguageEnUS
}
var (
translateMessage string
err              error
originMessage    string
)

switch value := message.(type) {
case string:
originMessage = message.(string)
translateMessage, err = i18n.Translate(lang, value)
case []string:
originMessage = strings.Join(value, ",")
if len(value) > 0 {
translateMessage, err = i18n.Translate(lang, value[0], value[1:])
}
case context.ErrPrivate:
err := message.(context.ErrPrivate)
translateMessage = err.Error()
}
msg := translateMessage
if err != nil {
e.app.Logger().Debug(err)
msg = originMessage
}
er := iris.Map{
"success": false,
"code":    ctx.GetStatusCode(),
"message": msg,
}
_ = ctx.JSON(er)
})
}

上述代码对KubeMgrServer的对象进行了初步的初始化,封装了日志/DB/缓存的/异常/返回,在完整基本的封装以后,可以进行接口的封装了,iris或者gin之类的 框架,最大的优势在于有ctx的概念,可以在请求的上下文中进行数据的修改,提取,插入等等,这就很方便了,可以利用这点就行token的提取,参数的抓取 ,甚至于请求头的改写(比如将http请求升级到websocket)。

在完成基本主体的封装后,开始编写接口,首先将接口分个类, 分成对应功能的目录 :

http
├── api
│   └── v1
│       ├── audit
│       │   ├── audit.go
│       │   └── types.go
│       ├── cluster
│       │   ├── cluster.go
│       │   ├── clusterrole.go
│       │   ├── member.go
│       │   └── types.go
│       ├── kubernetes
│       │   ├── prometheus.go
│       │   ├── proxy.go
│       │   └── types.go
│       ├── role
│       │   ├── role.go
│       │   └── types.go
│       ├── session
│       │   ├── session.go
│       │   └── types.go
│       ├── user
│       │   ├── types.go
│       │   └── user.go
│       ├── v1.go
│       └── ws
│           └── ws.go
└── route
    └── route.go

上述目录将不同功能的接口基于不同目录进行了封装,类似如下代码:

package audit

import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
log "kubemgr/common/formatlog"
v1AuditService "kubemgr/service/v1/audit"
)

type Handler struct {
auditService v1AuditService.Service
}

func NewHandler() *Handler {
return &Handler{
auditService: v1AuditService.NewService(),
}
}

func (h *Handler) ListPlatFormAuditRecord() iris.Handler {
return func(ctx *context.Context) {
pageNum := ctx.URLParamIntDefault("pageNum", 0)
pageSize := ctx.URLParamIntDefault("pageSize", 10)
fromTime := ctx.URLParam("fromTime")
endTime := ctx.URLParam("toTime")
total, auditLogs, err := h.auditService.ListPlatFormAuditRecord(pageNum, pageSize, fromTime, endTime)
if err != nil {
log.Errorf("获取审计日志失败: %v", err.Error())
ctx.StatusCode(iris.StatusInternalServerError)
ctx.Values().Set("message", err.Error())
return
}

var allAuditLogs ListAuditLogs
allAuditLogs.Total = total
allAuditLogs.AuditLogs = auditLogs

ctx.StatusCode(iris.StatusOK)
ctx.Values().Set("data", &allAuditLogs)
}
}

func Install(parent iris.Party) {
handler := NewHandler()
sp := parent.Party("/audit")
sp.Get("/list", handler.ListPlatFormAuditRecord())
}

然后在v1.go里面,将每个目录的接口接入root_route,并进行基础功能的封装,如日志,认证等等

func AddV1Route(app iris.Party) {
v1Party := app.Party("/v1")
session.Install(v1Party)

  // 接口国际化,基于profile内的语言标识,来返回中文/英文
v1Party.Use(langHandler())
  // websocket请求,将http请求升级到websocket,由于websocket获取token的方式比较特殊,所以不放入统一认证的方法,单独编写
wss.Install(v1Party)

// 基础路由的首次封装,添加认证,权限,角色的相关解析操作
authParty := v1Party.Party("")
authParty.Use(authHandler())
authParty.Use(resourceExtractHandler())
authParty.Use(roleHandler())

// 将封装好的路由导入平台用户/角色权限管理的相关接口
user.Install(authParty)
role.Install(authParty)

// 将封装好的路由导入平台审计/集群管理的相关接口
audit.Install(authParty)
cluster.Install(authParty)
kubernetes.Install(authParty)
}

至此,可以基于上面的代码看到api的请求路径

请求→v1.go(进行路由封装和分发)→分发到的明细路由处理(例如audit.go)→转发到业务实际处理逻辑,比如调用k8或者crud等

httpRequest
http/api/v1.go
audit...
service
dao/pkg

大致上的后端逻辑就是上面这样了,平台的基本操作用不到pkg,简单的crud即可。这个和其他运维或者中台管理的逻辑大致上没什么区别,主要后面要看对于k8s的操作。

个人公众号, 分享一些日常开发,运维工作中的日常以及一些学习感悟,欢迎大家互相学习,交流

在这里插入图片描述


原文地址:https://blog.csdn.net/zyxpaomian/article/details/135695403

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!