K8S管理系统项目实战[API开发]-2

 后端: go+gin

后端代码地址GitHub - yunixiangfeng/k8s-platform: K8s管理系统后端: go+gin

5、存储与配置

5.1 ConfigMap

5.2 Secret

5.3 PersistentVolumeClaims

6、工作流

6.1 流程设计

6.2 数据库操作(GORM)

(1)初始化数据库

db/db.go

6.3 Workflow

service/workflow.go

(1)列表

(2)获取Workflow详情

(3)新增Workflow

(4)表数据列表

7、中间件

7.1 什么是中间件

7.2 gin中间件用法

7.2 Cors跨域

7.3 JWT token验证

8、WebShell终端

8.1 kubectl exec原理

8.2 实现思路

8.3 代码实现

9、总结

API开发:存储与配置资源

5、存储与配置

5.1 ConfigMap

(1)列表

(2)获取ConfigMap详情

(3)更新ConfigMap

(4)   删除ConfigMap

5.2 Secret

(1)列表

(2)获取Secret详情

(3)更新Secret

(4)   删除Secret

5.3 PersistentVolumeClaims

(1)列表

(2)获取Pvc详情

(3)更新Pvc

(4)   删除Pvc

API开发:部署工作流

6、工作流

6.1 流程设计

6.2 数据库操作(GORM)

(1)初始化数据库

db/init.go

package db

import (
	"fmt"
	"k8s-plantform/config"
	"time"

	"github.com/wonderivan/logger"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
	isInit bool
	GORM   *gorm.DB
	err    error
)

// DB的初始化函数,与数据库建立连接
func Init() {
	// 判断是否已经初始化
	if isInit {
		return
	}
	// 组装连接配置
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",
		config.DbUser,
		config.DbPass,
		config.DbHost,
		config.DbPort,
		config.DbName)
	GORM, err := gorm.Open(config.DbType, dsn)
	if err != nil {
		panic("数据库连接失败," + err.Error())
	}
	// 打印sql语句
	GORM.LogMode(config.LogMode)
	// 开启连接池
	GORM.DB().SetMaxIdleConns(config.MaxIdleConns)
	GORM.DB().SetMaxOpenConns(config.MaxOpenConns)
	GORM.DB().SetConnMaxLifetime(time.Duration(config.MaxLifeTime))
	isInit = true
	logger.Info("数据库初始化成功")
}

// 关闭数据库连接
func Close() error {
	return GORM.Close()
}

加数据库配置

service/config.go

package config

import "time"

const (
	ListenAddr = "0.0.0.0:9090"
	KubeConfig = "C:\\Users\\Administrator\\.kube\\config"
	// tail的日志行数
	// tail -n 2000
	PodLogTailLine = 2000

	// DB Config
	DbType = "mysql"
	DbHost = "192.168.204.129"
	DbPort = 3306
	DbName = "k8s_dashboard"
	DbUser = "root"
	DbPass = ""
	// 打印mysql debug的sql日志
	LogMode = false
	// 连接池配置
	MaxIdleConns = 10               // 最大空闲连接
	MaxOpenConns = 100              // 最大连接数
	MaxLifeTime  = 30 * time.Second // 会话时间
)

SetMaxOpenConns
默认情况下,连接池的最大数量是没有限制的,一般来说,连接数越多,访问数据库的性能越高,但是系统资源不是无限的,数据库的并发能力也不是无限的,因此为了减少系统和数据据库崩溃的风险,可以给并发连接教设置一个上限,这个数值一般不超过进程的最大文件句柄打开数,不超过数据库服务自身支持的并发连接数,比如1000。
SetMaxldleConns
理论上maxldleConns连接的上限越高,也即允许在连接池中的空闲连接最大值越大,可以有效减少连接创建和消毁的次数,提高程序的性能,但是连接对象也是占用内存资源的,而且如果空闲连接越多,存在于连接池内的时间可能越长,连接在经过一段时间后有可能会变得不可用,而这时连接还在连接池内没有回收的话,后续被征用的时候就会出问题,一般建议maxidleConns的值为MaxOpenConns的1/2仅供参考。
SetConnMaxLifetime
设置一个连接被使用的最长时间,即过了一段时间后会被强制回收,理论上这可以有效减少不可用连接出现的概率,当数据库方面也设置了连接的超时时间时,这个值应当不超过数据库的超时参数值。

main.go

初始化

package main

import (
	"k8s-platform/config"
	"k8s-platform/controller"
	"k8s-platform/db"
	"k8s-platform/service"

	"github.com/gin-gonic/gin"
)

func main() {
	// 初始化k8s client
	service.K8s.Init() // 可以使用service.K8s.clientset 进行跨包调用

	// 初始化数据库
	db.Init()
	// 初始化gin对象/路由配置
	r := gin.Default()
	// 初始化路由规则
	controller.Router.InitApiRouter(r)
	// gin程序启动
	r.Run(config.ListenAddr)

	// 关闭数据库
	db.Close()
}

创建数据库k8s_dashboard

PS C:\Users\Administrator\Desktop\k8s-platform> go run main.go
2023-05-07 10:37:11 [INFO] [C:/Users/Administrator/Desktop/k8s-platform/service/init.go:26] 获取K8s配置成功!
2023-05-07 10:37:11 [INFO] [C:/Users/Administrator/Desktop/k8s-platform/service/init.go:33] 创建K8s client 成功!
2023-05-07 10:37:11 [INFO] [C:/Users/Administrator/Desktop/k8s-platform/db/init.go:44] 数据库初始化成功
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /api/k8s/pods             --> k8s-platform/controller.(*pod).GetPods-fm (3 handlers)
[GIN-debug] GET    /api/k8s/pod/detail       --> k8s-platform/controller.(*pod).GetPodDetail-fm (3 handlers)
[GIN-debug] DELETE /api/k8s/pod/del          --> k8s-platform/controller.(*pod).DeletePod-fm (3 handlers)
[GIN-debug] PUT    /api/k8s/pod/update       --> k8s-platform/controller.(*pod).UpdatePod-fm (3 handlers)
[GIN-debug] GET    /api/k8s/pod/container    --> k8s-platform/controller.(*pod).GetPodContainer-fm (3 handlers)
[GIN-debug] GET    /api/k8s/pod/log          --> k8s-platform/controller.(*pod).GetPodLog-fm (3 handlers)
[GIN-debug] GET    /api/k8s/pod/numnp        --> k8s-platform/controller.(*pod).GetPodNumPerNp-fm (3 handlers)
[GIN-debug] GET    /api/k8s/deployments      --> k8s-platform/controller.(*deployment).GetDeployments-fm (3 handlers)
[GIN-debug] GET    /api/k8s/deployment/detail --> k8s-platform/controller.(*deployment).GetDeploymentDetail-fm (3 handlers)
[GIN-debug] PUT    /api/k8s/deployment/scale --> k8s-platform/controller.(*deployment).ScaleDeployment-fm (3 handlers)
[GIN-debug] DELETE /api/k8s/deployment/del   --> k8s-platform/controller.(*deployment).DeleteDeployment-fm (3 handlers)
[GIN-debug] PUT    /api/k8s/deployment/restart --> k8s-platform/controller.(*deployment).RestartDeployment-fm (3 handlers)
[GIN-debug] PUT    /api/k8s/deployment/update --> k8s-platform/controller.(*deployment).UpdateDeployment-fm (3 handlers)
[GIN-debug] GET    /api/k8s/deployment/numnp --> k8s-platform/controller.(*deployment).GetDeployNumPerNp-fm (3 handlers)
[GIN-debug] POST   /api/k8s/deployment/create --> k8s-platform/controller.(*deployment).CreateDeployment-fm (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on 0.0.0.0:9090

(2)建立表的映射关系

表结构

model/workflow.go

package model

import "time"

// 定义结构体,属性与mysql表字段对齐
type Workflow struct {
	// gorm:"primarykey"用于声明主键
	ID       uint       `json:"id" gorm:"primaryKey"`
	CreateAt *time.Time `json:"created_at"`
	UpdateAt *time.Time `json:"update_at"`
	DeleteAt *time.Time `json:"deleted_at"`

	Name       string `json:"name"`
	Namespace  string `json:"namespace"`
	Replicas   int32  `json:"replicas"`
	Deployment string `json:"deployment"`
	Service    string `json:"service"`
	Ingress    string `json:"ingress"`
	// gorm:"column:type"用于声明mysql中表的字段名
	Type string `json:"type" gorm:"column:type"`
}

// 定义TableName方法,返回mysql表名,以次定义mysql中的表名
func (*Workflow) TableName() string {
	return "workflow"
}

(3)数据库创建表

db\workflow.sql

CREATE TABLE `workflow` ( 
	`id` int NOT NULL AUTO_INCREMENT,
	`name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL,
	`namespace` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
	`replicas` int DEFAULT NULL,
	`deployment` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
	`service` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
	`ingress` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
	`type` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL,
	`created_at` datetime DEFAULT NULL,
	`updated_at` datetime DEFAULT NULL,
	`deleted_at` datetime DEFAULT NULL,
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE KEY `name` (`name`)
	) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

(4)表数据列表

// 获取workflow列表/获取列表分页查询GetList
func (w *workflow) GetWorkflows(filterName, namespace string, limit, page int) (data *WorkflowResp, err error) {
	//定义分页的起始位置
	startSet := (page - 1) * limit
	//定义数据库查询返回的内容
	var (
		workflowList []*model.Workflow
		total        int
	)
	//数据库查询,Limit方法用于限制条数,Offset方法用于设置起始位置
	tx := db.GORM.
		Model(&model.Workflow{}).
		Where("name like ?", "%"+filterName+"%").
		Count(&total).
		Limit(limit).
		Offset(startSet).
		Order("id desc").
		Find(&workflowList)
	if tx.Error != nil && tx.Error.Error() != "record not found" {
		logger.Error("获取Workflow列表失败, " + tx.Error.Error())
		return nil, errors.New("获取Workflow列表失败, " + tx.Error.Error())
	}
	return &WorkflowResp{
		Items: workflowList,
		Total: total,
	}, nil
}

(5)获取单条

// 获取详情
func (w *workflow) GetById(id int) (workflow *model.Workflow, err error) {
	workflow = &model.Workflow{}
	tx := db.GORM.Where("id = ?", id).First(&workflow)
	if tx.Error != nil && tx.Error.Error() != "record not found" {
		logger.Error("获取Workflow详情失败, " + tx.Error.Error())
		return nil, errors.New("获取Workflow详情失败, " + tx.Error.Error())
	}
	return workflow, nil
}

(6)表数据新增

// 创建
func (w *workflow) Add(workflow *model.Workflow) (err error) {
	tx := db.GORM.Create(&workflow)
	if tx.Error != nil && tx.Error.Error() != "record not found" {
		logger.Error("创建Workflow失败, " + tx.Error.Error())
		return errors.New("创建Workflow失败, " + tx.Error.Error())
	}
	return nil
}

(7)表数据删除

// 删除
func (w *workflow) DelById(id int) (err error) {
	tx := db.GORM.Where("id = ?", id).Delete(&model.Workflow{})
	if tx.Error != nil && tx.Error.Error() != "record not found" {
		logger.Error("获取Workflow详情失败, " + tx.Error.Error())
		return errors.New("获取Workflow详情失败, " + tx.Error.Error())
	}
	return nil
}
package dao

import (
	"errors"
	"k8s-platform/db"
	"k8s-platform/model"

	"github.com/wonderivan/logger"
)

var Workflow workflow

type workflow struct{}

//定义列表的返回内容,Items是workflow元素列表,Total为workflow元素数量
type WorkflowResp struct {
	Items []*model.Workflow `json:"items"`
	Total int               `json:"total"`
}

6.3 Workflow

service/workflow.go

package service

import (
	"k8s-platform/dao"
	"k8s-platform/model"
)

var Workflow workflow

type workflow struct{}

//定义workflowCreate类型
type WorkflowCreate struct {
	Name          string                 `json:"name"`
	Namespace     string                 `json:"namespace"`
	Replicas      int32                  `json:"replicas"`
	Image         string                 `json:"image"`
	Label         map[string]string      `json:"label"`
	Cpu           string                 `json:"cpu"`
	Memory        string                 `json:"memory"`
	ContainerPort int32                  `json:"container_port"`
	HealthCheck   bool                   `json:"health_check"`
	HealthPath    string                 `json:"health_path"`
	Type          string                 `json:"type"`
	Port          int32                  `json:"port"`
	NodePort      int32                  `json:"node_port"`
	Hosts         map[string][]*HttpPath `json:"hosts"`
}

(1)列表

//获取列表分页查询
func(w *workflow) GetList(name, namespace string, page, limit int) (data *dao.WorkflowResp, err error) {
	data, err = dao.Workflow.GetWorkflows(name, namespace, page, limit)
	if err != nil {
		return nil, err
	}
	return data, nil
}

(2)获取Workflow详情

//查询workflow单条数据
func(w *workflow) GetById(id int) (data *model.Workflow, err error) {
	data, err = dao.Workflow.GetById(id)
	if err != nil {
		return nil, err
	}
	return data, nil
}

(3)新增Workflow

//创建workflow
func(w *workflow) CreateWorkFlow(data *WorkflowCreate) (err error) {
	//定义ingress名字
	var ingressName string
	if data.Type == "Ingress" {
		ingressName = getIngressName(data.Name)
	} else {
		ingressName = ""
	}

	//workflow数据落库
	workflow := &model.Workflow{
		Name:       data.Name,
		Namespace:  data.Namespace,
		Replicas:   data.Replicas,
		Deployment: data.Name,
		Service:    getServiceName(data.Name),
		Ingress:    ingressName,
		Type:       data.Type,
	}
	err = dao.Workflow.Add(workflow)
	if err != nil {
		return err
	}
	//创建k8s资源
	err = createWorkflowRes(data)
	if err != nil {
		return err
	}

	return err
}

//创建k8s资源 deployment service ingress
func createWorkflowRes(data *WorkflowCreate) (err error) {

	//创建deployment
	dc := &DeployCreate{
		Name:          data.Name,
		Namespace:     data.Namespace,
		Replicas:      data.Replicas,
		Image:         data.Image,
		Label:         data.Label,
		Cpu:           data.Cpu,
		Memory:        data.Memory,
		ContainerPort: data.ContainerPort,
		HealthCheck:   data.HealthCheck,
		HealthPath:    data.HealthPath,
	}
	err = Deployment.CreateDeployment(dc)
	if err != nil {
		return err
	}
	var serviceType string
	if data.Type != "Ingress" {
		serviceType = data.Type
	} else {
		serviceType = "ClusterIP"
	}
	//创建service
	sc := &ServiceCreate{
		Name:          getServiceName(data.Name),
		Namespace:     data.Namespace,
		Type:          serviceType,
		ContainerPort: data.ContainerPort,
		Port:          data.Port,
		NodePort:      data.NodePort,
		Label:         data.Label,
	}
	if err := Servicev1.CreateService(sc); err != nil {
		return err
	}
	//创建ingress
	var ic *IngressCreate
	if data.Type == "Ingress" {
		ic = &IngressCreate{
			Name:      getIngressName(data.Name),
			Namespace: data.Namespace,
			Label:     data.Label,
			Hosts:     data.Hosts,
		}
		err = Ingress.CreateIngress(ic)
		if err != nil {
			return err
		}
	}

	return nil
}

//workflow名字转换成service名字,添加-svc后缀
func getServiceName(workflowName string) (serviceName string) {
	return workflowName + "-svc"
}
//workflow名字转换成ingress名字,添加-ing后缀
func getIngressName(workflowName string) (ingressName string) {
	return workflowName + "-ing"
}

(4)删除workflow

//删除workflow
func(w *workflow) DelById(id int) (err error) {
	//获取数据库数据
	workflow, err := dao.Workflow.GetById(id)
	if err != nil {
		return err
	}
	//删除k8s资源
	err = delWorkflowRes(workflow)
	if err != nil {
		return err
	}
	//删除数据库数据
	err = dao.Workflow.DelById(id)
	if err != nil {
		return err
	}

	return
}

//删除k8s资源 deployment service ingress
func delWorkflowRes(workflow *model.Workflow) (err error) {
	err = Deployment.DeleteDeployment(workflow.Name, workflow.Namespace)
	if err != nil {
		return err
	}
	err = Servicev1.DeleteService(getServiceName(workflow.Name), workflow.Namespace)
	if err != nil {
		return err
	}

	if workflow.Type == "Ingress" {
		err = Ingress.DeleteIngress(getIngressName(workflow.Name), workflow.Namespace)
		if err != nil {
			return err
		}
	}

	return nil
}

controller/workflow.go

package controller

import (
	"k8s-platform/service"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/wonderivan/logger"
)

var Workflow workflow

type workflow struct{}

// 获取列表分页查询
func (w *workflow) GetList(ctx *gin.Context) {
	params := new(struct {
		Name      string `form:"name"`
		Namespace string `form:"namespace"`
		Page      int    `form:"page"`
		Limit     int    `form:"limit"`
	})
	if err := ctx.Bind(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	data, err := service.Workflow.GetList(params.Name, params.Namespace, params.Limit, params.Page)
	if err != nil {
		logger.Error("获取Workflow列表失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "获取Workflow列表成功",
		"data": data,
	})
}

// 查询workflow单条数据
func (w *workflow) GetById(ctx *gin.Context) {
	params := new(struct {
		ID int `form:"id"`
	})
	if err := ctx.Bind(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	data, err := service.Workflow.GetById(params.ID)
	if err != nil {
		logger.Error("查询Workflow单条数据失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "查询Workflow单条数据成功",
		"data": data,
	})
}

// 创建workflow
func (w *workflow) Create(ctx *gin.Context) {
	var (
		wc  = &service.WorkflowCreate{}
		err error
	)

	if err = ctx.ShouldBindJSON(wc); err != nil {
		logger.Error("Bind请求参数dc失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}
	if err = service.Workflow.CreateWorkFlow(wc); err != nil {
		logger.Error("创建Workflow失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "创建Workflow成功",
		"data": nil,
	})

}

// 删除workflow
func (w *workflow) DelById(ctx *gin.Context) {
	params := new(struct {
		ID int `json:"id"`
	})
	if err := ctx.ShouldBindJSON(params); err != nil {
		logger.Error("Bind请求参数失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	if err := service.Workflow.DelById(params.ID); err != nil {
		logger.Error("删除Workflow失败, " + err.Error())
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg":  err.Error(),
			"data": nil,
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"msg":  "删除Workflow成功",
		"data": nil,
	})
}

配置workflow路由

controller/router.go

package controller

import (
	"github.com/gin-gonic/gin"
)

// // 初始化router类型对象,首字母大写,用于跨包调用
// var Router router

// // 声明一个router的结构体
// type router struct{}

// func (r *router) InitApiRouter(router *gin.Engine) {
// 	router.GET("/", Index)
// }

// func Index(ctx *gin.Context) {
// 	ctx.JSON(200, gin.H{
// 		"code": 200,
// 		"msg":  "In index",
// 	})
// }

// 实例化router结构体,可使用该对象点出首字母大写的方法(包外调用)
var Router router

// 创建router的结构体
type router struct{}

// // 初始化路由规则,创建测试api接口
// func (r *router) InitApiRouter(router *gin.Engine) {
// 	router.GET("/testapi", func(ctx *gin.Context) {
// 		ctx.JSON(http.StatusOK, gin.H{
// 			"msg":  "testapi success!",
// 			"data": nil,
// 		})
// 	})
// }
// 初始化路由规则
// func (r *router) InitApiRouter(router *gin.Engine) {
// 	router.
// 		GET("/api/k8s/pods", Pod.GetPods).
// 		GET("/api/k8s/pod/detail", Pod.GetPodDetail).
// 		POST("/api/k8s/pods", Pod.DeletePod).
func (r *router) InitApiRouter(router *gin.Engine) {
	router.
		// Pods
		GET("/api/k8s/pods", Pod.GetPods).
		GET("/api/k8s/pod/detail", Pod.GetPodDetail).
		DELETE("/api/k8s/pod/del", Pod.DeletePod).
		PUT("/api/k8s/pod/update", Pod.UpdatePod).
		GET("/api/k8s/pod/container", Pod.GetPodContainer).
		GET("/api/k8s/pod/log", Pod.GetPodLog).
		GET("/api/k8s/pod/numnp", Pod.GetPodNumPerNp).
		//deployment操作
		GET("/api/k8s/deployments", Deployment.GetDeployments).
		GET("/api/k8s/deployment/detail", Deployment.GetDeploymentDetail).
		PUT("/api/k8s/deployment/scale", Deployment.ScaleDeployment).
		DELETE("/api/k8s/deployment/del", Deployment.DeleteDeployment).
		PUT("/api/k8s/deployment/restart", Deployment.RestartDeployment).
		PUT("/api/k8s/deployment/update", Deployment.UpdateDeployment).
		GET("/api/k8s/deployment/numnp", Deployment.GetDeployNumPerNp).
		POST("/api/k8s/deployment/create", Deployment.CreateDeployment).
		// workflows
		GET("/api/k8s/workflows", Workflow.GetList).
		GET("/api/k8s/workflow/detail", Workflow.GetById).
		POST("/api/k8s/workflow/create", Workflow.Create).
		DELETE("/api/k8s/workflow/del", Workflow.DelById)

}

测试api接口

7、中间件

7.1 什么是中间件

中间件,英译middleware,顾名思义,放在中间的物件,那么放在谁中间呢?本来,客户端可以直接请求到服务端接口。现在,中间件横插一脚它能在请求到达接口之前拦截请求,做一些特殊处理,比如日志记录,故障处理等

7.2 gin中间件用法

7.2 Cors跨域

7.3 JWT token验证

8、WebShell终端

8.1 kubectl exec原理

8.2 实现思路

8.3 代码实现

(1)处理终端交互

service/terminal.go

9、总结

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

交换机-Exchanges

交换机 Exchanges 概念 RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简…

正则表达式-基本元字符和语法规则

© Ptw-cwl 文章目录 字符匹配元字符.元字符[]元字符[^]元字符*元字符元字符?元字符{}元字符|元字符()元字符^元字符$元字符\元字符\d元字符\w元字符\s元字符\b元字符\B元字符*?、?、??、{n,m}?元字符(?)、(?!)元字符(?:)元字符\1、\2等元字符^、$元字符&#x…

JavaSE基础(二)—— 类型转换、运算符、键盘录入

目录 一、类型转换 1. 自动类型转换 1.1 自动类型转换的底层原理: ​1.2 自动类型转换的其他形式​编辑 2. 表达式的自动类型转换 3. 强制类型转换 3.1 强制类型转换底层原理​编辑 3.2 注意事项 二、运算符 1. 算数运算符 1.1 案例:数值拆分…

PCA主成成分分析例题详解

主成分分析是一种降维算法,它能将多个指标转换为少数几个主成分,这些主成分是原始变量的线性组合,且彼此之间互不相关,其能反映出原始数据的大部分信息 需要了解具体细节可看此视频👉:什么是主成成分分析PC…

Linux安装MongoDB数据库,并内网穿透远程连接

文章目录 前言1. 配置Mongodb源2. 安装MongoDB3. 局域网连接测试4. 安装cpolar内网穿透5. 配置公网访问地址6. 公网远程连接7. 固定连接公网地址8. 使用固定地址连接 转载自Cpolar Lisa文章:Linux服务器安装部署MongoDB数据库 - 无公网IP远程连接「内网穿透」 前言 …

SpringBoot访问静态资源

SpringBoot项目中没有WebApp目录,只有src目录。在src/main/resources下面有static和templates两个文件夹。SpringBoot默认在static目录中存放静态资源,而templates中放动态页面。 static目录 SpringBoot通过/resources/static目录访问静态资源&#xff…

完成A轮融资,倍思如何发力场景化为品牌创造广阔未来?

凛冬过后的消费电子正在重新凝聚资本的目光。 近日,深圳市倍思科技有限公司宣布完成由深创投、中金资本联合领投,越秀产业基金、高榕资本跟投,金额数亿元人民币的A轮融资。 分析人士指出,消费电子的行业景气度在逐渐恢复&#x…

中国社科院与美国杜兰大学金融管理硕士项目——迎接立夏,切莫忘记自我成长

五月的风吹走了春季,今天我们迎来立夏。作为夏季的第一个节气,立夏常被人们当做万物蓄满能量,即将加速生长的标志。而在职的我们,也应该跟这世间万物一样,在季节交替之时沉淀自己、努力向上成长。在社科院与杜兰大学金…

“人工智能教父”从谷歌离职 称后悔发展AI,为世人敲响警钟?

在加入谷歌的第十年、深度学习迎来爆发式发展的当下,被誉为“人工智能教父”的Geoffrey Hinton已从谷歌离职,只是为了告诫人们AI已经变得很危险。 公开资料显示,Geoffrey Hinton在2013年加入谷歌,曾任副总裁,研究机器学…

成为数据分析师,需要具备哪些技能?

随着互联网的发展,数据分析师的特点越来越明显,对数据分析师综合素质的要求也较高。 1、较强的数据挖掘、信息整理、和逻辑分析能力 数据分析,也是数据分析师的一个方向。 制作日常性的经营报表,对公司或者行业KPI指标进行拆解…

Mysql索引(3):索引分类

1 索引分类 在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。 分类含义特点关键字主键索引针对于表中主键创建的索引 默认自动创建, 只能有一个 PRIMARY 唯一索引 避免同一个表中某数据列中的值重复可以有多…

【Android入门到项目实战-- 8.4】—— 如何解析JSON格式数据

目录 一、准备工作 二、使用JSONObject 三、使用GSON 比起XML,JSON的主要优势在于它的体积更小,在网络上传输的时候可以更省流量,但缺点是语义性较差,看起来不直观。 一、准备工作 还是使用前面文章的方法,在服务器…

每日学术速递4.29

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.LG 1.A Cookbook of Self-Supervised Learning 标题:自监督学习食谱 作者:Randall Balestriero, Mark Ibrahim, Vlad Sobal, Ari Morcos, Shashank Shekhar, Tom…

【黑马程序员 C++教程从0到1入门编程】【笔记8】 泛型编程——模板

https://www.bilibili.com/video/BV1et411b73Z?p167 C泛型编程是一种编程范式,它的核心思想是编写通用的代码,使得代码可以适用于多种不同的数据类型。 而模板是C中实现泛型编程的一种机制,它允许我们编写通用的代码模板,然后在需…

【Spring篇】IOC/DI注解开发

🍓系列专栏:Spring系列专栏 🍉个人主页:个人主页 目录 一、IOC/DI注解开发 1.注解开发定义bean 2.纯注解开发模式 1.思路分析 2.实现步骤 3.注解开发bean作用范围与生命周期管理 1.环境准备 2.Bean的作用范围 3.Bean的生命周期 4.注解开发依赖…

相当Python程序员,选择培训班还是自学?我结合自己的经历谈谈看法

前几天我写了一篇文章,分享了自己当上程序员的经历。然后,我收到了很多小伙伴的提问,都在问同一个问题,即如何选择报培训班还是自学。今天,我结合自己的个人经历,来谈一下个人的看法。 我认为这个问题的第…

Linux线程:死锁

1. 死锁 (1)概念 死锁(DeadLock)指两个或两个以上的进程或线程执行时,由于竞争临界资源而造成阻塞的现象;若不干涉,则无法推进下去。 (2)死锁的原因 ① 竞争临界资源…

06_Uboot顶层Makefile分析_前期所做内容

目录 U-Boot顶层Makefile分析 版本号 MAKEFLAGS变量 命令输出 静默输出 设置编译结果输出目录 代码检查 模块编译 获取主机架构和系统 设置目标架构、交叉编译器和配置文件 调用scripts/Kbuild.include 交叉编译工具变量设置 导出其他变量 U-Boot顶层Makefile分析…

TCP/IP网络编程(一)

TCP/IP网络编程读书笔记 第1章 理解网络编程和套接字1.1 理解网络编程和套接字1.1.1 构建打电话套接字1.1.2 编写 Hello World 套接字程序 1.2 基于Linux的文件操作1.2.1 底层访问和文件描述符1.2.2 打开文件1.2.3 关闭文件1.2.4 将数据写入文件1.2.5 读取文件中的数据1.2.6 文…

操作系统考试复习——第四章 存储器管理 4.1 4.2

存储器的层次结构: 存储器的多层结构: 存储器至少分为三级:CPU寄存器,主存和辅存。 但是一般分为6层为寄存器,高速缓存,主存储器,磁盘缓存,固定磁盘,可移动存储介质。…