Gin web框架初步认识

news2024/10/5 19:13:26

Goland使用及gin框架下载引入

第一次使用Goland时需要配置GOROOT、GOPATH、Go Modules

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

配置完成后进入面板,右键选择Go Modules文件,或者在go工作区通过命令go mod init [name]创建go mod项目。

在这里插入图片描述
创建完的项目一般都有go.mod文件和go.sum,前者是go包的依赖管理文件,后者是体现依赖管理的关系。
在这里插入图片描述

go get -u github.com/gin-gonic/gin

go get命令更新主模块的 go.mod 文件中的模块依赖关系,模块的go.mod文件记录了它需要哪些版本的依赖项。这些依赖项的源代码存储在本地缓存中。

go get更新go.mod文件中列出的要求。它还确保这些需求是自洽的,并根据需要添加新需求,以便您在命令行中命名的包导入的每个包都由您的需求中的某个模块提供。
作为更新和添加需求的副作用,go get还将包含命名包(及其依赖项)的模块下载到本地模块缓存。
go mod download 不添加新需求或更新现有需求。它只下载您请求的特定模块版本,也就是说模块存在于本地缓存,不需要go get再次远程下载,在go.mod引入后go mod download从缓存中加载。

gin框架下载后会在GOPATH的pkg/mod下面

在这里插入图片描述

创建一个包,编写web引擎:

package main

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

func main() {
	//创建一个服务器引擎
	engine := gin.Default()
	//配置web容器地址
	engine.GET("/", func(context *gin.Context) {
		context.JSON(200, gin.H{"msg": "Hello World"})
	})
	//配置服务器端口
	engine.Run(":8080")
}

在这里插入图片描述

Gin路由

import (
    "net/http"

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

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "hello word")
    })
    r.POST("/xxxpost",HandlerFunc)
    //监听端口默认为8080
    r.Run(":8000")
}

在上面函数中,创建了一个gin引擎,利用引擎设置路由规则,不同地址对应不同的方法,这个方法只能是HandlerFunc类型的方法即参数为gin.context
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes

type HandlerFunc func(*Context)

因此方法直接使用匿名方法即可。最后通过引擎的Run方法绑定端口即可。

Restful风格

gin引擎支持Restfu风格,gin框架封装了http库,提供了 GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS 这些http请求方式。

router := gin.Default()
router.GET("/get", func(c *gin.Context) { c.JSON(200, gin.H{"message": "get方法"}) })
router.POST("/post", func(c *gin.Context) { c.JSON(200, gin.H{"message": "post方法"}) })
router.PUT("/put", func(c *gin.Context) { c.JSON(200, gin.H{"message": "put方法"}) })
router.DELETE("/delete", func(c *gin.Context) { c.JSON(200, gin.H{"message": "delete"}) })
router.PATCH("/patch", func(c *gin.Context) { c.JSON(200, gin.H{"message": "patch"}) })
router.HEAD("/head", func(c *gin.Context) { c.JSON(200, gin.H{"message": "head"}) })
router.OPTIONS("/options", func(c *gin.Context) { c.JSON(200, gin.H{"message": "options"}) })
router.Run(":80")//指定端口  localhost:9999
engine.PATCH("/url", func(context *gin.Context) {
	
})

重定向

context.Redirect(code,rrl)方法实现重定向。

engine.GET("/redirect", func(context *gin.Context) {
	context.Redirect(301, "https://baidu.com")
})

错误页面

出现404页面的错误是没有找到对应的路由,一般情况服务器都有默认的404页面,但是有时需要定制的页面。就需要使用engine.NoRoute(func(context *gin.Context)方法。

/**
gin默认又模板引擎
*/

//加载静态页面
engine.LoadHTMLGlob("./templates/*")

//404页面没有路由
engine.NoRoute(func(context *gin.Context) {
	context.HTML(404, "404.html", nil)
})

路由组

处理方法除了使用匿名函数之外还可以直接定义方法,定义方法一般在路由组中,如下

v1 := r.Group("/v1")
{
	v1.GET("/login", login)
	v1.GET("/register", register)
}

v2 := r.Group("/v2")
{
	v2.GET("/index", page)
	v2.GET("/main", index)
}
r.Run()

func login(c *gin.Context) {
	c.String(200, "login method")
}

func register(c *gin.Context) {
	c.String(200, "register method")
}

func page(c *gin.Context) {
	c.String(200, "page main")
}

func index(c *gin.Context) {
	c.String(200, "index main")
}

每个r.Group("/v2")管理若干路由地址。

返回数据或页面

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

func main() {
	//创建一个服务器引擎
	engine := gin.Default()

	//加载静态页面
	engine.LoadHTMLGlob("./templates/*")
	//配置web容器地址
	engine.GET("/", func(context *gin.Context) {
		//返回json数据
		context.JSON(200, gin.H{"msg": "Hello World"})
	})
	engine.GET("/index", func(context *gin.Context) {
		//返回html页面
		context.HTML(200, "index.html", gin.H{
			"msg": "gin后台转递的数据",
		})
	})
	//配置服务器端口
	engine.Run(":8080")
}

gin.Context参数的JSON方法用于返回json数据,HTML方法返回页面,且是模板引擎的页面,需要使用引擎加载html文件engine.LoadHTMLGlob("./templates/*")

html文件中通过{{}}获取传递的数据,.数据接收。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hello World</h1>

{{.msg}}
</body>
</html>

在这里插入图片描述

参数处理

在web容器中,第一步有web容器处理统一资源定位符,分发到不同的路由地址,到达地址的数据均有两个参数接收与处理。在Gin中gin.Context参数,封装了request(请求参数)和response(响应参数),因此基于参数的处理都需要使用gin.Context参数。(Java是没有封装的)参数处理又分为请求行参数、请求头和请求体参数。

url?name=xxx&password=xxx

//url?name=xxx&password=xxx
engine.GET("/add", func(context *gin.Context) {
	name := context.Query("name")
	pass := context.Query("pass")
	context.JSON(200, gin.H{
		"user": name,
		"pass": pass,
	})
})

通过context.Query()查询参数的值。也可以使用DefaultQuery(),区别在于Query()没对应,返回空字符串,DefaultQuery()返回默认值,其有两个参数,第一个为绑定的查询变量,第二个为默认值。

r := gin.Default()
r.GET("/user", func(c *gin.Context) {
    //指定默认值
    //http://localhost:8080/user 才会打印出来默认的值
    name := c.DefaultQuery("name", "枯藤")
    c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
})
r.Run()

在这里插入图片描述

url/value1/value2

//url/value1/value2
engine.GET("/delete/:name/:pass", func(context *gin.Context) {
	name := context.Param("name")
	pass := context.Param("pass")
	context.JSON(200, gin.H{
		"name": name,
		"pass": pass,
	})
})

通过context.Param()获取获取参数的值。这是Restful风格的传参。url地址上的标识符需要前面加上:/delete/:name/:pass
在这里插入图片描述

接收json数据

//接收json数据
engine.POST("/json", func(context *gin.Context) {
	data, err := context.GetRawData()
	if err != nil {
		panic(err)
	}
	context.JSON(200, data)
})

context.GetRawData()用于接收请求体数据,(这个很好记)

在这里插入图片描述
在这里插入图片描述

在主流的API测试工具中JSON数据均被定义为raw数据。

context.GetRawData()返回的是字节数组,需要字节数组转化为Go的内置对象如结构体。

在这里插入图片描述

在Go序列化章节中说到,序列化结构体的返回值为字节数组,转为json字符串需要使用string()方法Go语言流处理,工厂模式,命令参数,序列化,单元测试。因此将其转化为字符串类型。

context.JSON(200, string(data))

在这里插入图片描述

但是这样转也没有意义,这个字符串字Go无法运用,在实际中,一般通过反序列化直接将字节数组转化为Go语言的内置对象。

type Person struct{
	Name string
	Age int
	Phone int8
	Address string
}
//接收json数据
engine.POST("/json", func(context *gin.Context) {
	data, _ := context.GetRawData()
	//context.JSON(200, string(data))
	var per Person
	_ = json.Unmarshal(data, &per)
	context.JSON(200, per)
})

在这里插入图片描述

定义一个结构体用于接收数据,但是又出现了新的问题,在反序列化时字符串名称要和结构体成员名称一致,于是有需要序列化的tag规则。修改后的结构体如下:

type Person struct {
	Name    string `json:"name"`
	Age     int    `json:"age"`
	Phone   string `json:"phone"`
	Address string `json:"address"`
}

改造后只要传入的数据和json:[name]的name对应即可。

表单数据

表单数据通过context.PostForm方法接收,其参数对应html的input标签的name属性即可。

//表单数据
engine.POST("/form", func(context *gin.Context) {
	user := context.PostForm("user")
	pass := context.PostForm("pass")

	context.JSON(200, gin.H{
		"username": user,
		"password": pass,
	})
})

在这里插入图片描述

表单传输为post请求,http常见的传输格式为四种:
application/json
application/x-www-form-urlencoded
application/xml
multipart/form-data

表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数

文件上传

<!DOCTYPE html>
<html>
    <head>
        <title>test</title>
        <meta lang="en" charset="utf-8">

    </head>  
    <body>

        <form action="/upload" method="post" enctype="multipart/form-data">
            upload file<input type="file" name="file">
            <input type="submit" value="submit">
        </form>

    </body>
</html>
package main

import (
	"net/http"

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

func main() {
	r := gin.Default()

	r.LoadHTMLGlob("./templates/*")

	r.GET("/", func(ctx *gin.Context) {
		ctx.HTML(200, "index.html", nil)
	})

	r.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("file")
		if err != nil {
			c.String(500, "上传图片出错")
		}
		c.SaveUploadedFile(file, file.Filename)
		c.String(http.StatusOK, file.Filename)
	})
	r.Run()

}

文件会上传到根目录下,如下:

在这里插入图片描述

中间件(拦截器)

中间件是在路由请求前的预处理,相当远Java的拦截器。中间件也是封装在gin.context中,定义在单个路由就对某个接口生效,定义在全局就对整体生效。

//中间件(拦截器)
func preHandler() gin.HandlerFunc {
	return func(context *gin.Context) {
		//设置值
		context.Set("isLog",true)
		//放行
		context.Next()
		//阻止
		context.Abort()
	}
}

加载静态资源

//加载静态页面
engine.LoadHTMLGlob("./templates/*")
//加载静态资源
engine.Static("/static", "./static")

前后端交互结构体绑定数据

json

import (
	"encoding/json"

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

func main() {
	engine := gin.Default()

	engine.GET("/", func(ctx *gin.Context) {
		ctx.String(200, "Hello World")
	})

	engine.GET("/index", func(ctx *gin.Context) {
		data, _ := ctx.GetRawData()
		var log Login
		_ = json.Unmarshal(data, &log)
		ctx.JSON(200, log)
	})
	engine.Run("127.0.0.1:8080")
}

type Login struct {
	User string `json:"username"`
	Pass string `json:"password"`
}

除了上述通过ctx.GetRawData()接收字节数组外,也可以直接gin封装的结构体函数将其绑定到结构体数据上。ctx.ShouldBindJSON()方法将传递的json数据直接绑定到结构体上,而不需要二次转化,要求是字段对应,或者序列化的tag规则即可。如下:

func main() {
	engine := gin.Default()
	engine.GET("/index", func(ctx *gin.Context) {
		var log Login
		_ = ctx.ShouldBindJSON(&log)
		ctx.JSON(200, log)
	})
	engine.Run("127.0.0.1:8080")
}

type Login struct {
	User string `json:"username"`
	Pass string `json:"password"`
}

form-data

注意这里的form-data不是x-www-form-urlencoded。form-data的数据通过序列化tag规则的form标签,如下:

在这里插入图片描述

func main() {
	engine := gin.Default()

	engine.GET("/per", func(ctx *gin.Context) {
		var per Person
		ctx.Bind(&per)
		ctx.JSON(200, per)
	})

	engine.Run("127.0.0.1:8080")

}

type Person struct {
	Name string `form:"username"`
	Age  int    `form:"age"`
}

在这里插入图片描述
x-form-urlencoded

x-form-urlencoded的转化使用的是ctx.ShouldBind()方法。如下:

type LoginForm struct {
	User     string `form:"user" binding:"required"`
	Password string `form:"password" binding:"required"`
}

func main() {
	router := gin.Default()
	router.POST("/login", func(c *gin.Context) {
		// 你可以使用显式绑定声明绑定 multipart form:
		// c.ShouldBindWith(&form, binding.Form)
		// 或者简单地使用 ShouldBind 方法自动绑定:
		var form LoginForm
		// 在这种情况下,将自动选择合适的绑定
		_ = c.ShouldBind(&form)
		c.JSON(200, form)
	})
	router.Run("127.0.0.1:8080")
}

url数据绑定结构体

url绑定结构体只支持url?user=xxx&pass=xxx的风格

func main() {
	engine := gin.Default()

	engine.GET("/per/:username/:age", func(ctx *gin.Context) {
		var per Person
		ctx.ShouldBindUri(&per)
		ctx.JSON(200, per)
	})

	engine.Run("127.0.0.1:8080")

}

type Person struct {
	Name string `uri:"username"`
	Age  int    `uri:"age"`
}

结构体序列化tag标签改为uri,方法使用ctx.ShouldBindUri()

将数据直接绑定结构体,会更加快捷,但是需要注意,使用序列化的tag规则定义结构体,另外还有一个binding:"required“标签,如果存在,则绑定后赋值不能为空否则回报错。
多个标签也可以一起用form:"username" json:"user" uri:"user" xml:"user" binding:"required"

前后端交互响应数据

上一节说明了前端传递的数据可以直接转化为结构体数据,也可以通过序列化转化为其他数据类型。在Gin框架中,封装了这些过程,可以直接通过相关方法将数据转化为对应的数据类型。

//JSON
engine.GET("/per", func(ctx *gin.Context) {
	var per Person
	ctx.ShouldBindUri(&per)
	ctx.JSON(200, per)
})

ctx.JSON()方法可以直接将Go的一些内置对象直接转化为json字符串。最常见的就是字典,结构体等。

// XML
r.GET("/someXML", func(c *gin.Context) {
    c.XML(200, gin.H{"message": "abc"})
})

ctx.XML()方法可以直接将数据转为XML文件的格式。

Gin模板引擎

如果用gin返回html页面,就会默认使用模板引擎,gin使用LoadHTMLGlob()方法加载模板文件。

import (
    "net/http"

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

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("tem/*")
    r.GET("/index", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是测试", "ce": "123456"})
    })
    r.Run()
}

r.LoadHTMLGlob("tem/*")方法加载文件下所有模板文件,通过gin.Context.HTML()方法渲染模板引擎,方法又三个参数,第一个状态码,第二个是模板文件,第三个是数据。

在模板引擎中通过.来获取数据。一般情况下通过gin.H返回的是一个map类型,通过.key获取。

gin中html文件也能模块化,通过{{ define ...}}定义模块{{ template ...}}引入模块。

在这里插入图片描述

<!--header.html-->
{{define "header"}}
<h1>this is header</h1>

{{end}}
<!--footer.html-->
{{ define "footer"}}
<h1>this is footer</h1>

{{end}}
<!--index.html->

<!DOCTYPE html>
<html>
    <head>
        <title>hello</title>
        <meta>
    </head>
    <body>
        {{define "index.html"}}
        <!--header-->
        {{ template "header" .}}


        <!--main-->
        <h1>this is main</h1>

        <!--footer-->
        {{ template "footer" .}}

        {{ end }}
    </body>
</html>
{{define "header"}}
...
{{ end }}

通过上面的关键字定义html模块,header为一个路径,分别是根路径递归,最后一个是模块名,如果header.html在head目录下,那么其路径对应的是head/header。另外还受engine.LoadHTMLGlob("templates/*")方法的影响,一般该方法加载到资源的更目录或者html文件的父目录即可。在结合define关键字定义路径。

在其他模板引入时注意不要忘了.{{ template "footer" .}},主模块也需要定义一下,将被引入模块包裹起来即{{define "index.html"}},这里直接定义为文件的名称即可。

package main

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

func main() {
	engine := gin.Default()

	engine.LoadHTMLGlob("templates/*")

	engine.GET("/", func(ctx *gin.Context) {
		ctx.HTML(200, "index.html", nil)
	})

	engine.Run("127.0.0.1:8080")

}

在这里插入图片描述

日志

import (
	"io"
	"os"

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

func main() {
	// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
	gin.DisableConsoleColor()

	// 记录到文件。
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f)

	// 如果需要同时将日志写入文件和控制台,请使用以下代码。
	// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

	router := gin.Default()
	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	router.Run("127.0.0.1:8080")
}

gin.DisableConsoleColor()禁用控制台颜色控制台没有日志
os.Create("gin.log")创建日志文件
gin.DefaultWriter = io.MultiWriter(f)写入日志文件
开启日志后日志会记录到日志文件中去。

gin在路由时会打印日志,这个日志的格式也是可以自定义的,请移步定义路由日志的格式

BasicAuth 中间件

Basic Auth是一种开放平台认证方式,简单的说就是需要你输入用户名和密码才能继续访问。BasicAuth授权认证中间件使用

官方网站:Gin Web Framework

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

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

相关文章

Mysql【安装教程】

Mysql安装教程 1.安装教程 可以去官网下载这个版本的&#xff1a;mysql-installer-community-8.0.31.0 双击点开&#xff0c;选择自定义&#xff1a; 选择主键&#xff1a;左边选择之后就点蓝色按钮添加到右边去&#xff0c;next&#xff1a; 如果出现这个页面&#xff0c…

机器视觉检测系统的基本流程你知道吗

工业制造业种&#xff0c;首先我们便需要了解其基本流程&#xff0c;作为工厂信息科人员&#xff0c;我们不能只依靠视觉服务商的巡检驻检来解决问题&#xff0c;为了产线的效率提升&#xff0c;我们更多的应该培养产线技术人员&#xff0c;出现问题便可以最快速度解决问题&…

领跑新能源车市“下半场”,这家企业凭什么?

中国新能源汽车市场行至下半场&#xff0c;将围绕技术升级、产品竞争力比拼、整合淘汰等趋势快速发展。 4月7日&#xff0c;在北京水立方发布的奇瑞新能源之夜上&#xff0c;奇瑞汽车全面展示新战略、新技术、新品牌和新产品&#xff0c;宣布将以全新的技术生态加速向全球科技…

光伏电池片技术N型迭代,机器视觉检测赋能完成产量“弯道超车”

电池片是光伏发电的核心部件&#xff0c;其技术路线和工艺水平直接影响光伏组件的发电效率和使用寿命。随着硅料、硅片技术逐渐接近其升级迭代空间的瓶颈&#xff0c;电池片环节正处于技术变革期&#xff0c;是光伏产业链中迭代最快的部分。P型中PERC电池片是现阶段市场的主流产…

已知原根多项式和寄存器初始值时求LFSR的简单例子

线性反馈移位寄存器&#xff08;LFSR&#xff09;是一种用于生成伪随机数序列的简单结构。在这里&#xff0c;我们有一个四项原根多项式 p(x)1x0x21102p(x) 1 x 0x^2 110_2p(x)1x0x21102​ 和初始值 S0100S_0 100S0​100。我们将使用 LFSR 动作过程来生成一个伪随机序列。…

2023美赛春季赛_赛题原文及翻译

目录 Problem Y: Understanding Used Sailboat Prices Y题翻译&#xff1a; Problem Z: The Future of the Olympics Z题翻译&#xff1a; Problem Y: Understanding Used Sailboat Prices ​Like many luxury goods, sailboats vary in value as they age and as market c…

看这一篇就够了!!!Java最全面试手册(性能优化+微服务架构+并发编程+开源框架)

Java面试手册 一、性能优化面试专栏 1.1、 tomcat性能优化整理 ​ 1.2、JVM性能优化整理 1.3、Mysql性能优化整理 二、微服务架构面试专栏 2.1、SpringCloud面试整理 2.2、SpringBoot面试整理 2.3、Dubbo面试整理 三、并发编程高级面试专栏 四、开源框架面试题专栏 4.1、Sp…

[Data structure]稀疏数组

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现&#xff0c;有时候有C/C代码。 ⭐如果觉得文章写的…

清肠化湿颗粒通过激活NLRP6信号和调节Th17/Treg平衡来改善DSS诱导的结肠炎

百趣代谢组学分享-文章标题&#xff1a;Qing-Chang-Hua-Shi granule ameliorates DSS-induced colitis by activating NLRP6 signaling and regulating Th17/Treg balance 代谢组学分享-发表期刊&#xff1a;Phytomedicine 代谢组学分享-影响因子&#xff1a;6.656 代谢组学…

python 对数函数

在 Python中&#xff0c;除了对数函数&#xff0c;还有其他的一些函数&#xff0c;例如&#xff1a; 这是一个在 python中用来计算两个整数之间的关系的函数。如果两个整数的值不同&#xff0c;则它们之间的关系是&#xff1a; 其中aa^2bb^2cc^2。下面是计算两个整数之间的关系…

微信小程序开发(学习记录1.0)

首先&#xff0c;把遇到的问题贴出来&#xff0c;主要是这个解决问题的思路&#xff0c;供大家参考。 现在的问题是将下面的导航栏做出来&#xff0c;但是在自己做的时候 遇到的问题 在app.json文件中输入tarBar&#xff0c;就会生成模板代码&#xff0c;默认会生成一个list的模…

【面试题】calc()计算函数的作用和理解

前言 最近项目中遇到calc()方法不生效问题&#xff0c;本着究其所以然的想法&#xff0c;彻底查了一下calc的方法及使用&#xff0c;还有为什么项目中使用不生效的问题&#xff0c;最后总结出此文&#xff0c;解决方法放在结尾了 cale()是什么 calc是英文单词calculate的缩写…

低功耗技术——流水线设计(加法器和乘法器)

文章目录前言一、流水线1、16bit加法器2、无符号4bit乘法器3、编写一个4bit乘法器模块&#xff0c;并例化该乘法器求解c12*a5*b二、降低FPGA功耗1、静态功耗2、动态功耗前言 2023.3.31 今天学习降低功耗的一些方法 一、流水线 电路最高工作频率&#xff1a;取决于最长的组合逻…

高扇出的危害及优化

高扇出有哪些危害&#xff1f; 危害1&#xff1a;驱动能力下降&#xff0c;时序紧张 扇出过高也就是也就意味了负载电容过大&#xff0c;电路原理基础告诉咱们&#xff0c;负载电容越大&#xff0c;充放电速度越慢&#xff0c;电平跳变所需要的时间增加&#xff0c;即驱动能力…

「SAP ABAP」OPEN SQL(八)【WHERE语句大全】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

MySQL主从复制、读写分离(MayCat2)实现数据同步

文章目录1.MySQL主从复制原理。2.实现MySQL主从复制&#xff08;一主两从&#xff09;。3.基于MySQL一主两从配置&#xff0c;完成MySQL读写分离配置。&#xff08;MyCat2&#xff09;1.MySQL主从复制原理。 MySQL主从复制是一个异步的复制过程&#xff0c;底层是基于Mysql数据…

AI编程助手 Kodezi : 记录、分享一个 VS code 插件

目录0. 概述1. 安装 / 功能介绍1.1 安装1.2 功能介绍1.2.1 Debug1.2.2 代码优化1.2.3 代码转换1.2.4 逐行注释1.2.5 多行注释1.2.6 生成文档1.2.7 生成代码2. KodeziChat 聊天机器人2.1 聊天机器人功能介绍2.2 如何使用 KodeziChat &#xff1f;3. Kodezi 版本介绍3.1 免费版3.…

【2023年第十一届泰迪杯数据挖掘挑战赛】C题:泰迪内推平台招聘与求职双向推荐系统构建 建模及python代码详解 问题三

相关链接 【2023年第十一届泰迪杯数据挖掘挑战赛】C题泰迪内推平台招聘与求职双向推荐系统构建 建模及python代码详解 问题一 【2023年第十一届泰迪杯数据挖掘挑战赛】C题泰迪内推平台招聘与求职双向推荐系统构建 建模及python代码详解 问题二 【2023年第十一届泰迪杯数据挖…

【软件应用】TamperMonkey同步操作

不废话&#xff0c;记录一些关键点 要设置同步&#xff0c;先得打开高级设置模式 众所周知&#xff0c;这里可以设置同步&#xff0c;建议设置为WebDAV同步&#xff0c;这样通用性更强 至于选择啥网盘就看自己的需求了&#xff0c;支持WebDAV的网盘还是很多的&#xff0c;甚…

数据库设计案例

一个专辑可以包含多个曲目&#xff0c;一个曲目只能属于一个专辑 一对多 一个专辑可以包含多条短评&#xff0c;一条短语只能属于一个专辑 一对多 一个用户可以包含多条短评&#xff0c;一个短评只能属于一个用户 一对多 一个专辑可以属于多个用户&#xff0c;一个用户…