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
英文地址,請注明出處:http://en.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;順序循環將請求一次順序循環地連接每個服務器。當其中某個服務…