Go语言的web框架--gin

本章内容,会介绍一下gin的运用,以及gin框架底层的内容,话不多说,开始进入今天的主题吧!

一.基本使用

gin框架支持前后端不分离的形式,也就是直接使用模板的形式。

模板是什么?

这里可能有同学不太了解,其实就是指的html,直接调用html,在后续的介绍中会有所了解。

1.1 模板搭建

首先简单看一下引用模板的样子吧

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()//解析模板r.LoadHTMLFiles("templates/hello.html")r.GET("/hello", func(c *gin.Context) {//Http请求c.HTML(http.StatusOK, "hello.html", gin.H{ //模板渲染"title": "Hello World",})})r.Run() //启动server
}
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body>{{ .title }}<h1>haha</h1></body>
</html>

对应的文件就是hello.html,然后运行go代码,去默认端口查看即可。

上述对应的函数会在后续介绍,接下来看看静态文件(css,js)的引入

1.2 静态文件的引入

  • 静态文件就是指的是css,js那一类的文件

r.Static("/static", "./static")

就是如果以static开头的文件,会去static下面去找

r.Static("/xxx", "./static")

也就是xxx开头的文件去static下面去找

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()//静态文件渲染r.Static("/static", "./static")//解析模板r.LoadHTMLFiles("templates/hello.html")r.GET("/hello", func(c *gin.Context) {//Http请求c.HTML(http.StatusOK, "hello.html", gin.H{ //模板渲染"title": "Hello World",})})r.Run() //启动server
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="/static/css/hello.css"><!-- 这里其实就是去找的static开头的文件都去 /static下面取找把static改为xxx也是可以实现的 -->
</head>
<body>{{ .title }}<h1>haha</h1><script src="/static/js/hello.js"></script>
</body>
</html>
alert(123);
body{background-color: hotpink;
}

1.3 前后端分离的搭建

主要还是前后端分离的搭建,说一下流程

  1. 首先设置开发模式
  2. 创建实例
  3. 设置中间件
  4. 加载路由
  5. 最后启动
func InitRouter(cfg *config.Configuration) {gin.SetMode(gin.DebugMode)//r := gin.New()	//获取一个gin的引擎实例r := gin.Default() //这个是创建了一个默认的gin实例对象r.Use(cor.Cors()) // 跨域中间件repo := repositories.NewUrlMapRepoUse()service := service2.NewUrlService(repo)UrlHandler := shortURL.NewURLHandler(service)r.GET("/:code", UrlHandler.RedirectURL)apiv1 := r.Group("/api/v1"){apiv1.POST("/shorturl", UrlHandler.CreateURL)}r.Run(":8080")
}

这个例子无法直接运行,他是我的一个案例

二.具体函数和步骤介绍

目前来看,大部分项目都是前后端分离的项目,所以我们采用的也是前后端分离的架构,下面会按照之前介绍的步骤,一一介绍对应的函数。

2.1 开发模式

gin.SetMode(gin.DebugMode)

在gin包下,它里面有默认的常量表示开发模式,就像上面的gin.DebugMode

也可以改为"debug"也是可以的,两者等价

除了上面的debug模式还有release模式

debug模式会展示更多细节内容,但是release模式就是一个运行,而不是测试

具体介绍一下:

  1. DebugMode:这是默认模式,适用于开发和调试阶段。在这种模式下,Gin 会输出详细的调试信息,帮助开发者快速定位问题。例如: gin.SetMode(gin.DebugMode)
  2. ReleaseMode:适用于生产环境。在这种模式下,Gin 会减少日志输出,提升性能和安全性。例如: gin.SetMode(gin.ReleaseMode)
  3. TestMode:主要用于单元测试,Gin 自身的测试会使用这种模式。对于一般开发者来说,这种模式并不常用。例如: gin.SetMode(gin.TestMode)

2.2 实例对象

r := gin.Default() //这个是创建了一个默认的gin实例对象或者r:= gin.New()

两者的主要区别就是

gin.default默认提供了两个中间件

但是gin.New()获得实例没有任何中间件

r.Use(gin.Logger()) 	// 日志中间件
r.Use(gin.Recovery())	// 

gin.Recovery()中间件

gin.Recovery()是一个内置的Gin中间件,它的主要目的是捕获和处理在请求处理过程中发生的任何panic。当一个panic发生时,如果没有适当的恢复机制,程序将会崩溃并终止运行。gin.Recovery()中间件提供了一个安全网,它能够捕获这些panic,防止程序崩溃,并且返回一个500内部服务器错误的响应给客户端。

2.3 中间件的加入

r.Use(cor.Cors()) // 跨域中间件

主要就是通过r.Use来添加中间件,除了自带的中间件,我们也可以自己编写中间件,会在后续聊到中间件的时候具体介绍一下。

2.4 路由的加入

	r.GET("/:code", UrlHandler.RedirectURL)apiv1 := r.Group("/api/v1"){apiv1.POST("/shorturl", UrlHandler.CreateURL)}

所谓的路由就是url对应的处理器,处理前端传送过来的请求,上述的写法是路由组的形式,很容易看出来,前缀都是api/v1

2.5 启动gin引擎

r.Run(":8080")

就是在8080端口启动这个程序

三.请求和响应

所谓的请求和响应就是客户端和服务器之间的一个信息沟通,他们之间交流的信息格式有很多,比如json,xml等,一般来说都是json比较多。

json就是一个前端和后端交互的一个媒介,换句话说就是一种数据格式。

3.0 前置补充

如果大家有学过http协议,应该对请求方法有所了解,比如Get,Post等等方法,在gin框架中大家不难发现它已经为这些不同的方法做好了封装处理。

package mainimport ("github.com/gin-gonic/gin""net/http"
)func Sayhello(c *gin.Context) {c.JSON(200, gin.H{"message": "hello world",})
}func main() {//创建一个默认的路由引擎r := gin.Default() //返回一个默认的路由引擎//指定用户使用GET请求访问r.GET("/ping", Sayhello)r.GET("/books", func(c *gin.Context) {c.JSON(200, gin.H{"message": "Get",})})r.POST("/books", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "post",})})r.PUT("/books", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Put",})})r.DELETE("/books", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "DELETE",})})//启动服务器r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

其中的func(c *gin.Context) { 函数体 }就是http里面的处理器函数

在这个案例中,也可以直接引用处理器函数,要求和http的处理器是一样的,必须是func(c *gin.Context)类型,这样写让路由更加简单,也可以更好的实现分层。

//指定用户使用GET请求访问
r.GET("/ping", Sayhello)

c.JSON(http.StatusOK, gin.H{ (json格式 ) })

说一下JSON这个函数,它的作用就是处理器处理完成之后,给前端的一个响应,第一个参数就是状态码,如果有同学不了解的,可以自行上网搜索一下。

第二个参数就是后续要将的响应数据,采用的是json格式。

3.1 响应

响应,其实就是服务器给客户端发送数据的操作,在gin框架中,对响应的渲染主要有两种方式:

一个是map,另一个就是结构体。

3.1.1 gin.H

map方式 (也就是gin.H),为了处理不同类型的值,一般都会采用接口作为value

但是定义map[]interface比较麻烦,所以gin框架就对其做了一个封装,改为了gin.H。

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("json", func(c *gin.Context) {//方法一使用map//data := map[string]interface{}{//	"name":    "小王子",//	"age":     18,//	"message": "hello world",//	}//	c.JSON(http.StatusOK, data)//})//简化的方式//但是gin的作者考虑到了这个问题,如果一直写map麻烦,就有了gin.Hdata := gin.H{"name":    "小王子","age":     18,"message": "hello world",}c.JSON(http.StatusOK, data)})r.Run(":9090") //启动server
}

3.1.2 *结构体方式(这一块的内容可以了解)

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("json", func(c *gin.Context) {type msg struct {Name    stringAge     intMessage string}m := msg{"小王子",18,"hello world",}c.JSON(http.StatusOK, m)})r.Run(":9090") //启动server
}

这里注意结构体的访问问题,大小写是否可以访问呢??

如果就想他是小写的name呢??查看内容即可,我做一个简单的案例。

通过tag(标签)来实现一个这样的效果

type msg struct {Name    string `json:"name"`Age     int    `json:"age"`Message string `json:"message"`}

3.2 请求

类比http包,如果大家对http有了解的话,一个知道,http可以对请求做处理,比如路径参数,表格参数等等。

接下来就是介绍一下gin框架中是如何处理这些内容的。

3.2.1 querystring参数

这里还是介绍一下query函数的作用:就是获取参数的作用

name := c.Query("query") //通过query获取请求中的query string参数

这里获取的是url上的参数,是在路由之后/web?参数

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/web", func(c *gin.Context) {//获取浏览器那边发请求携带的querystring参数name := c.Query("query") //通过query获取请求中的querystring参数c.JSON(http.StatusOK, gin.H{"name": name,})})r.Run(":9090") //启动server
}

http://localhost:9090/web?query=程潇,去浏览器搜索这个路径

除了Quety函数,还有DefaultQuery和GetQuery两个函数

DefaultQuery()则是如果没用该参数,返回一个设置的默认值

GetQuery()则会返回一个布尔值,来判断是否返回了一个值

除此之外还可以传递多个参数,不是只有一个参数哦

多个key-value使用&来连接

3.2.2.form参数

这个一般是使用在获取信息的界面。

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.LoadHTMLFiles("templates/hello.html", "templates/index.html")r.GET("/hello", func(c *gin.Context) {c.HTML(http.StatusOK, "hello.html", nil)})//接受请求r.POST("/hello", func(c *gin.Context) {user := c.PostForm("username")mm := c.PostForm("password")c.HTML(http.StatusOK, "index.html", gin.H{"Name":     user,"Password": mm,})})r.Run(":9090") //启动server
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="/static/css/hello.css"><!-- 这里其实就是去找的static开头的文件都去 /static下面取找把static改为xxx也是可以实现的 -->
</head>
<body><form action="/hello" method="post"><div><label for="username">username:</label><input type="text" name="username" id="username"></div><div><label for="password">password:</label><input type="text" name="password" id="password"></div><div><input type="submit" value="登录"></div></form><script src="/static/js/hello.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>hello,{{ .Name}}</h1><h1>你的密码是{{.Password}}</h1>
</body>
</html>

这里我们可以发现,就是一个/hello可以对于不同的请求的界面,一个是get,一个是post

都是可以实现的

还要该清楚这里的逻辑是怎么实现的

他的逻辑就是你进入get的hello网址(不过一般都是/开头的),然后在登入的html里面把内容通过form表单在传递给他参数action规定的处理器上,然后看看他是什么方法,由于是post方法,所以会传给post方法对应的处理器上面,从而进行不同的操作啦。

还存在第二种方法

他和上面的query string差不多,他就是类似一个,如果没有的话就返回一个默认值,你应该是传入input标签里面的name,但是没有传入,就会取默认值。

还有第三种,和上面的大差不差

3.2.3.path参数的获取

path参数也被称为路径参数,主要通过/:key 来表示,获取这个key的值

package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/:name/:age", func(c *gin.Context) {name := c.Param("name")age := c.Param("age")c.JSON(200, gin.H{"name": name,"age":  age,})})r.Run(":9090")
}

看他的URL上,就可以将他传给我们的get,去处理

package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/:name/:age", func(c *gin.Context) {name := c.Param("name")age := c.Param("age")c.JSON(200, gin.H{"name": name,"age":  age,})})r.GET("/blog/:year/:month", func(c *gin.Context) {year := c.Param("year")month := c.Param("month")c.JSON(200, gin.H{"year":  year,"month": month,})})r.Run(":9090")
}

可以通过加前缀的方式改变这个问题

3.2.4.参数的绑定

 c.ShouldBind(&参数)

我们可以使用这个函数直接提取上述三种方式传入的数据,并且绑定到对应的结构体里面去。

非常的好用哦

比如我们的登入注册功能就可以不在使用页面做了,直接使用api来进行测试也是非常的快的。

接下展示一下json的例子,由于是前后端分离的形式,所以要认真学习者一块的内容

json发送和form选的是不一样的啦

package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)type User struct {Username string `json:"username" form:"username"`Password string `json:"password" form:"password"`
}func main() {r := gin.Default()r.LoadHTMLFiles("templates/hello.html")r.GET("/", func(c *gin.Context) {//username := c.Query("username")//password := c.Query("password")//u1 := User{//	Username: username,//	Password: password,//}var u1 Usererr := c.ShouldBind(&u1) //这里为什么要传&u1引用,因为你要去改变他的值/*这里就会还有一个问题,你怎么判断传入的字段和你的结构体字段符合呢?在结构体体后面加上form,就是tag*/if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})} else {fmt.Println(u1)c.JSON(http.StatusOK, gin.H{"status": "ok",})}})r.GET("/hello", func(c *gin.Context) {c.HTML(http.StatusOK, "hello.html", nil)})r.POST("/form", func(c *gin.Context) {var u1 Usererr := c.ShouldBind(&u1)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})} else {fmt.Println(u1)c.JSON(http.StatusOK, gin.H{"status": "ok",})}})r.POST("/json", func(c *gin.Context) {var u1 Usererr := c.ShouldBind(&u1)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})} else {fmt.Println(u1)c.JSON(http.StatusOK, gin.H{"status": "ok",})}})r.Run(":9090")
}

通过观察,我们会发现不管是什么方式去获取,他们的大致代码都是差不多哟,你应该可以发现吧。

除了请求和URL的不同,其他大致都可以,

由此可见ShouldBind(&参数)的厉害,不管你是什么格式,都可以调用

其实这里一般只用来处理表单传入的数据,一般前端form表单的数据转化为json格式或者其他格式传给后端来解析。

除了ShouldBind,还有ShouldBindJSON专门用来解析josn数据。

3.2.5.获取Host

直接通过c.Host就可以获取路径的主机名

这些函数其实本质上都是对http的一个封装,简单看一下源码,大家应该就知道如何使用了。

四.文件的传输

4.1 介绍

4.2 操作案例

f, err := c.FormFile("f1") 首先获取文件

dst := fmt.Sprintf("%d", f.Filename)

dst := path.Join("./", f.Filename)

上述两个方法都是将个获取当前目录的方法

c.SaveUploadedFile(f, dst)

然后这里就是将这文件保存到指定目录下面

package mainimport ("github.com/gin-gonic/gin""net/http""path"
)func main() {r := gin.Default()r.LoadHTMLFiles("templates/index.html")r.GET("/", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", nil)})r.POST("/upload", func(c *gin.Context) {//从请求中读取文件f, err := c.FormFile("f1")if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})} else {// 将读取的文件保存在本地// dst := fmt.Sprintf("%d", f.Filename)dst := path.Join("./", f.Filename)c.SaveUploadedFile(f, dst)c.JSON(http.StatusOK, gin.H{"message": "ok"})}})r.Run(":9090")
}
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><form action="/upload" method="post" enctype="multipart/form-data"><!-- 插入文件或者照片必须带上enctype="multipart/form-data"--><input type="file" name="f1"><input type="submit" value="上传"></form></body>
</html>

上面是单个文件进行的一个读取操作

下面介绍一下多个文件的读取操作

c.MultipartForm 获取多个文件

然后存入到这个数组里面去

再用一个for range循环上传到指定的文件去即可

五.请求重定向

5.1 概念介绍

关于重定向,在http的内容里面已经涉及过了,就不在过多介绍了。

5.2 案例

5.2.1 http 重定向

下面就是一个重定向,到百度的网址

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/", func(c *gin.Context) {//c.JSON(http.StatusOK, gin.H{//	"status": "ok",//})c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")})r.Run(":9090")
}

5.2.2 路由重定向

也就是你虽然进入的是a的路由,但是他最后却是指向b的路由,这就是所谓的路由重定向

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/a", func(c *gin.Context) {//跳转到bc.Request.URL.Path = "/b"  //把请求的URL修改r.HandleContext(c)//继续后续的处理,跳转到b路由})r.GET("/b", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"massage": "ok",})})r.Run(":9090")
}

六.路由和路由组

6.1 基础介绍

any请求里面包含所有的请求哦,可以用switch来设置

NoRoute处理函数允许开发者定义当请求没有匹配到任何路由时应该执行的操作。默认情况下,Gin框架会为NoRoute情况返回一个404状态码

6.2 案例

6.2.1普通路由

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()//访问/index的r.GET("/test", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"method": "GET",})})r.POST("/test", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"method": "POST",})})r.DELETE("/test", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"method": "DELETE",})})r.PUT("/test", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"method": "PUT",})})//这个会处理所以的请求,懒得话就可以写这个anyr.Any("/user", func(c *gin.Context) {switch c.Request.Method {case "GET":c.JSON(http.StatusOK, gin.H{"method": "GET"})case http.MethodPost:c.JSON(http.StatusOK, gin.H{"method": "POST"})}c.JSON(http.StatusOK, gin.H{"method": "ANY",})})r.Run(":9090")
}

但是一般来说,我们设置的URL是有限制的,但是用户可能会随便输入各种各样的路径,为了解决如果是不存在的路径,我们就设置一个页面进行跳转

我们可以使用NoRoute来实现这个功能

	r.NoRoute(func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"信息": "404,查找无果",})})

6.2.2路由组

如果当我们去写项目的时候,发现有很多的来自同一个URL下的多个不同的页面,如果一个一个写r.GET或者什么,写起来是相当麻烦的。

比如这样:

我们该如何去修改呢?

这个时候我们就可以使用路由组去解决这个问题

这样就可以了

6.3路由组的嵌套

七.中间件

7.1 概念介绍

除此之外,还有跨域中间件,在gin框架中提供的有跨域的库cor。

7.2 案例

要明白Next函数和Abort函数

下面展示一个案例

package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)func indexhandler(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "index",})
}// 定义一个中间件:统计耗时
func m1(c *gin.Context) {fmt.Println("m1进入")//计时start := time.Now()c.Next() //调用后续的处理函数//c.Abort() //阻止调用后续的处理函数cost := time.Since(start)fmt.Printf("m1 cost %v\n", cost)
}func main() {r := gin.Default()r.GET("/test", m1, indexhandler)//这里请求进来之后先走m1,之后才走index,这里的m1就相当于是一个中中间件r.Run(":9090")
}

如果存在一个情况就是多个请求都需要使用m1这个中间件,有没有什么快捷的方式呢?

当然有了:

就是Use这个函数

加上之后,后续的函数都会有这个m1中间件

如果存在多个中间件:

要明白他在执行的顺序:他不是一个一个调用,而是类似一个嵌套的那种方式

package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)func indexhandler(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "test",})
}// 定义一个中间件:统计耗时
func m1(c *gin.Context) {fmt.Println("m1进入")//计时start := time.Now()c.Next() //调用后续的处理函数,就是相当于是调用后面的indexhandler//c.Abort() //阻止调用后续的处理函数cost := time.Since(start)fmt.Printf("m1 cost %v\n", cost)
}func m2(c *gin.Context) {fmt.Println("m2 in")c.Next() //调用后续的处理函数fmt.Println("m2 out")
}func main() {r := gin.Default()r.Use(m1, m2)r.GET("/test", indexhandler)//这里请求进来之后先走m1,之后才走index,这里的m1就相当于是一个中中间件r.Run(":9090")
}

如果把m2里面的Next改为Abort,这样就会断掉,这样网页就会没有内容

如果想后面的m2 out 都没有

直接return即可

7.3 真正的使用案例

一般情况下是和闭包一起使用的

为路由准备中间件啦

7.4 如何实现传递数据(m2->indexhandler)

也就是使用函数GET和SET

package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)func indexhandler(c *gin.Context) {name, ok := c.Get("name")if !ok {name = "匿名用户"}c.JSON(http.StatusOK, gin.H{"message": name,})
}// 定义一个中间件:统计耗时
func m1(c *gin.Context) {fmt.Println("m1进入")//计时start := time.Now()c.Next() //调用后续的处理函数,就是相当于是调用后面的indexhandler//c.Abort() //阻止调用后续的处理函数cost := time.Since(start)fmt.Printf("m1 cost %v\n", cost)
}func m2(c *gin.Context) {fmt.Println("m2 in")c.Set("name", "qimi")c.Next() //调用后续的处理函数//c.Abort() //阻止调用后续的处理函数fmt.Println("m2 out")
}func main() {r := gin.Default()r.Use(m1, m2)r.GET("/test", indexhandler)//这里请求进来之后先走m1,之后才走index,这里的m1就相当于是一个中中间件r.Run(":9090")
}

7.5.注意事项

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

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

相关文章

企业为什么需要双因素认证?

从进入互联网时代开始&#xff0c;密码是我们个人日常的重要保护。但是单独的密码保护可能已经不再适应当前的数字化时代。密码已经不再足够安全最近发生的各种安全漏洞让我重新审视网络安全。几行代码可能就导致了全球数以百万的登录凭证被泄露。今天&#xff0c;仅仅周期性地…

Spring Boot + 本地部署大模型实现:优化与性能提升!

在Spring Boot中集成本地部署的大模型&#xff08;如LLaMA、ChatGLM等&#xff09;并进行优化&#xff0c;需要从模型选择、推理加速、资源管理和架构设计等多方面入手。以下是完整的优化方案及实现步骤&#xff1a; 一、核心优化策略 1. 模型量化 目标&#xff1a;减少显存占…

仿mudou库one thread oneloop式并发服务器

前言 我们所要实现的是一个高并发服务器的组件&#xff0c;使服务器的性能更加高效&#xff0c;是一个高并发服务器的组件&#xff0c;并不包含实际的业务。 首先需要先明确我们所要实现的目标是什么 第一点&#xff0c;实现一个高并发的服务器第二点&#xff0c;在服务器的基础…

超详细的私有化安装部署Dify服务以及安装过程中问题处理

一、什么是Dify Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员&#xff0c;也能参与到 AI 应用的定义和数据…

国产DSP,QXS320F280049,QXS320F28377D,QXS320F2800137,QXS320F28034

自定义指令集&#xff0c;自研内核架构&#xff0c;基于eclipse自研IDE&#xff0c;工具链&#xff0c;算法库。 根据自研QXS320F280049&#xff0c;做了600W和2KW数字电源方案&#xff0c;1.5KW电机方案&#xff0c;目前已在市场大量投产。 QXS320F290049应用于数字电源&#…

dotnet publish 发布后的项目,例如asp.net core mvc项目如何在ubuntu中运行,并可外部访问

复制到 Ubuntu 上的是使用 Visual Studio 或 dotnet publish 命令生成的 发布后的输出文件&#xff08;publish output&#xff09;&#xff0c;而不是原始项目源代码。在这种情况下&#xff0c;确实没有 .csproj 文件&#xff0c;所以不能直接用 dotnet run 启动。但你可以通过…

Linux多线程(十二)之【生产者消费者模型】

文章目录生产者消费者模型为何要使用生产者消费者模型生产者消费者模型优点基于BlockingQueue的生产者消费者模型BlockingQueueC queue模拟阻塞队列的生产消费模型单线程生产消费模型多线程生产消费模型生产者消费者模型 consumer/productor 321原则(便于记忆) 为何要使用生产…

MySQL表的操作(3)

文章目录前言一、创建表创建表时指定属性二、查看表查看表结构查看建表消息三、修改表修改列属性修改列名修改表名四、删除表总结前言 Hello! 那我们乘胜追击&#xff0c;开始 表的操作&#xff01; 一、创建表 首先创建一个 数据库 testForTable mysql> create database i…

从“人工智障”到“智能助手”:集成为什么能拯救AI用户体验?

几年前&#xff0c;当人们满怀期待地与AI语音助手对话时&#xff0c;常常遭遇令人啼笑皆非的回应——“抱歉&#xff0c;我不明白你在说什么”“请再说一遍”甚至答非所问。AI被戏称为“人工智障”&#xff0c;用户体验一度让人失望。然而&#xff0c;近年来&#xff0c;随着技…

Uniapp 自定义TabBar + 动态菜单实现教程(Vuex状态管理详解)

大家好&#xff0c;我是一诺。今天跟大家分享一下uniapp 封装自定义底部导航栏&#xff08;TabBar&#xff09; 过程中的思考和实践。通过本文&#xff0c;你将学会如何打造一个功能完善、可自由定制的TabBar组件&#xff01; 先看效果&#xff1a; 支持自定义图标和样式动态显…

MySQL数据库主从复制

概述1、master开启二进制日志记录2、slave开启IO进程&#xff0c;从master中读取二进制日志并写入slave的中继日志3、slave开启SQL进程&#xff0c;从中继日志中读取二进制日志并进行重放4、最终&#xff0c;达到slave与master中数据一致的状态&#xff0c;我们称作为主从复制的…

Rancher Server + Kubernets搭建云原生集群平台

目录Rancher Server Kubernets搭建云原生集群平台一、环境准备1、软件准备2、环境规划3、挂载数据盘二、虚拟机初始化基础配置&#xff08;所有节点都需要操作&#xff09;1、执行时间服务器脚本&#xff08;包括配置hostName主机名&#xff09;2、配置hosts文件3、配置各节点…

Java学习第八部分——泛型

目录 一、概述 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;作用 &#xff08;三&#xff09;引入原因 二、使用 &#xff08;一&#xff09;类 &#xff08;二&#xff09;接口 &#xff08;三&#xff09;方法 三、类型参数 &#xff08;一&#xf…

定时点击二次鼠标 定时点击鼠标

定时点击二次鼠标 定时点击鼠标 今天分享一个定时点击两次的小工具。 我们在生活中&#xff0c;可能会遇到一些定时点击的任务。比如说在晚上9点去发送一个群发&#xff0c;或者倒计时点击一个按钮。那么可以使用这个工具&#xff0c;仅适用于Windows电脑。 #定时点击鼠标 #倒计…

Linux网络配置与故障排除完全指南

1. ifconfig命令 - 网络接口配置器 ifconfig&#xff08;interface configurator&#xff09;是Linux系统中最基础的网络配置工具。该命令可以初始化网络接口、分配IP地址、启用或禁用接口&#xff0c;同时还能查看接口的详细信息。 查看网络接口信息 # ifconfig eth0 …

Python Pytest-Benchmark详解:精准性能测试的利器

在软件开发的迭代过程中&#xff0c;性能优化如同精密手术&#xff0c;需要精准的测量工具。Pytest-Benchmark作为pytest生态中的性能测试插件&#xff0c;凭借其无缝集成能力和专业统计功能&#xff0c;成为Python开发者进行基准测试的首选工具。本文将深入解析其技术特性与实…

60天python训练营打卡day51

学习目标&#xff1a; 60天python训练营打卡 学习内容&#xff1a; DAY 51 复习日 作业&#xff1a;day43的时候我们安排大家对自己找的数据集用简单cnn训练&#xff0c;现在可以尝试下借助这几天的知识来实现精度的进一步提高 学习时间&#xff1a; 2025.07.04 浙大疏锦行…

支持向量机(SVM)在肺部CT图像分类(肺癌检测)中的实现与优化

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…

YOLOv3-SPP 深度解析:引入 SPP 结构,显著提升目标检测性能!

✅ YOLOv3-SPP 技术详解 一、前言 YOLOv3-SPP 是在 YOLOv3 基础上加入 SPP&#xff08;Spatial Pyramid Pooling&#xff09;模块的一种改进版本&#xff0c;旨在提升模型对不同尺度目标的识别能力&#xff0c;尤其是在大目标检测方面表现更优。 它由 Alexey Bochkovskiy 在…

负载均衡--常见负载均衡算法

负载均衡算法可以分为两类&#xff1a;静态负载均衡算法和动态负载均衡算法。 1、静态负载均衡算法包括&#xff1a;轮询&#xff0c;比率&#xff0c;优先权 轮询&#xff08;Round Robin&#xff09;&#xff1a;顺序循环将请求一次顺序循环地连接每个服务器。当其中某个服务…