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

news2024/11/18 18:40:22

后端框架的选择面比较大,由于不涉及复杂的调度/分布式管理等场景,所以后端选用一个标准的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的操作。

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1396324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka-消费者-KafkaConsumer分析-PartitionAssignor

Leader消费者在收到JoinGroupResponse后&#xff0c;会按照其中指定的分区分配策略进行分区分配&#xff0c;每个分区分配策略就是一个PartitionAssignor接口的实现。图是PartitionAssignor的继承结构及其中的组件。 PartitionAssignor接口中定义了Assignment和Subscription两个…

KubeSphere 社区双周报 | 2024.01.04-01.18

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2024.01.04-01.18…

【SpringBoot3】IDEA常用插件介绍Tabnine,GsonFormat,Maven Helper等

文章目录 一、Tabnine二、Auto filling Java call arguments三、GsonFormat四、Rainbow Brackets五、Maven Helper 本文主要介绍一些比较实用的IDEA插件&#xff0c;帮助广大开发者提升开发效率&#xff0c;愉悦编码&#xff01; 一、Tabnine 官网地址&#xff1a;https://ww…

C语言:预处理详解

创作不易&#xff0c;来个三连呗&#xff01; 一、预定义符号 C语⾔设置了⼀些预定义符号&#xff0c;可以直接使⽤&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编…

从 GPT1 - GPT4 拆解

从 GPT1 - GPT4 拆解 从 GPT1 - GPT4GPT1&#xff1a;更适用于文本生成领域GPT2&#xff1a;扩展数据集、模型参数&#xff0c;实现一脑多用&#xff08;多个任务&#xff09;GPT3&#xff1a;元学习 大力出奇迹InstructGPT&#xff1a;指示和提示学习 人工反馈强化学习 RLHF…

视频直播新时代,低延时直播交互,Web,Android,WebRtc推流拉流测试

直播现在已经深入了生活&#xff0c;学习&#xff0c;工作和娱乐方方面面&#xff0c;由于前些年的技术所限&#xff0c;传统rtmp,flv,m3u8 技术让直播快速启动项目产品&#xff0c;但也有很多不足&#xff0c;特别的交互式直播&#xff0c;一直是其中的痛点&#xff0c;延时较…

【C++初阶】第二站:类与对象(上) -- 上部分

前言: C学习的第二站&#xff1a;类和对象(上)文章的上半部分,知识点:面向过程和面向对象初步认识、类的引入、类的定义、类的访问限定符及封装、类的作用域、类的实例化. 目录 面向过程和面向对象初步认识 类的引入 类的定义 类的访问限定符及封装 访问限定符 封装 类的…

Docker服务以及容器如何设置服务器开机自动启动

Docker服务以及容器如何设置服务器开机自动启动 今天在客户现场遇到了如下问题&#xff1a; 他们服务器的机房&#xff0c;有时候需要关机重启&#xff0c;进行机房改造&#xff01; 这时候就会遇到断电重启&#xff0c;在没有设置自动启动的情况下&#xff0c;我们所有的软…

经典ABR算法介绍:Pensieve (SIGCOMM ‘17) 原理及训练指南

文章目录 前言Pensieve原理*Pensieve重训练参考Oboe [SIGCOMM 18]Comyco [MM 19]Fugu [NSDI 20] A3C熵权重衰减思路实现 前言 Pensieve是DASH点播视频中最经典的ABR算法之一&#xff0c;也是机器学习类&#xff08;Learning-based&#xff09;ABR算法的代表性工作。Pensieve基…

Unity-场景

创建场景 创建新的场景后&#xff1a; 文件 -> 生成设置 -> Build中的场景 -> 将项目中需要使用的场景拖进去 SceneTest public class SceneTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){// 两个类&#xff1a; 场景类、场…

VUE--组件通信(父子)

1、什么是组件通信 组件通信就是指组件与组件之间的数据传递。因为组件的数据是独立的&#xff0c;无法直接访问其他组件的数据&#xff0c;想获取其他组件的数据&#xff0c;就需要用到组件通信。 2、组件关系分类&#xff08;如图&#xff09; ● 父子关系&#xff08;props…

【Linux】进程的概念 进程状态 进程优先级

Content 一、什么是进程1. 进程的概念2. 进程的描述 - 进程控制块&#xff08;PCB&#xff09;3. Linux下的进程 二、进程状态1. 教科书中的进程状态运行状态阻塞状态挂起状态 2. Linux下的进程状态R&#xff08;running&#xff09;- 运行状态S&#xff08;sleeping) - 睡眠状…

排序:非递归的归并排序

目录 递归与非递归的思想对比&#xff1a; 递归&#xff1a; 非递归&#xff1a; 代码解析&#xff1a; 完整代码&#xff1a; 递归与非递归的思想对比&#xff1a; 递归&#xff1a; 在之前的归并排序&#xff0c;它的核心思想是通过不断的分割&#xff0c;从一个数组变…

docker-compose部署

目录 一、什么是docker-compose&#xff1f; 二、compose部署 一、什么是docker-compose&#xff1f; Docker-Compose项目是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用Y…

MySQL(四)——约束

上期文章 MySQL&#xff08;三&#xff09;——函数 文章目录 上期文章概述约束演示外键约束添加外键删除外键删除/更新行为 总结 概述 概念&#xff1a;作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据 目的&#xff1a;保证数据库中数据的正确、有效性和完整性…

2024华数杯国际赛数学建模A题完整论文来啦!最后的助攻!

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024华数杯国际数学建模竞赛A题日本放射性废水完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 论文共40页&a…

48-DOM节点,innerHTML,innerText,outerHTML,outerText,静态获取,单机click,cssText

1.DOM基础 Document Object Module,文档对象模型,window对象,document文档,都可以获取和操作 1)文档节点 2)属性节点(标签内的属性href,src) 3)文本节点(标签内的文字) 4)注释节点 5)元素节点(标签) 2.获取元素节点 2.1通过标签名获取getElementsByTagName() …

[NSSRound#16 Basic]RCE但是没有完全RCE

题目代码&#xff1a; <?php error_reporting(0); highlight_file(__file__); include(level2.php); if (isset($_GET[md5_1]) && isset($_GET[md5_2])) {if ((string)$_GET[md5_1] ! (string)$_GET[md5_2] && md5($_GET[md5_1]) md5($_GET[md5_2])) {i…

微信内测“听一听” 音乐音频业务提至一级入口;美团 AI 平台视觉中心负责人魏晓林离职;腾讯视频生成模型 VideoCrafter2;广州房价连跌12个月

今日精选 • 微信内测“听一听” 音乐音频业务提至一级入口• 美团 AI 平台视觉中心负责人魏晓林离职• 腾讯推出视频生成模型 VideoCrafter2&#xff0c;• 广州房价连跌12个月 投融资与企业动态 • TikTok 越南推出 Thu Duc Market 在线销售渠道• 亚马逊将在五年内在日本…

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05-3+4

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05-34 3. Step by step : Deriation of Kalmen Gain 卡尔曼增益/因数 详细推导4. Priori/Posterrori error Covariance Martix 误差协方差矩阵 3. Step by step :…