歡迎關(guān)注「全棧工程師修煉指南」公眾號(hào)
點(diǎn)擊 ???下方卡片?即可關(guān)注我喲!
設(shè)為「星標(biāo)?」每天帶你?基礎(chǔ)入門(mén)?到?進(jìn)階實(shí)踐?再到?放棄學(xué)習(xí)!
專注?企業(yè)運(yùn)維實(shí)踐、網(wǎng)絡(luò)安全、系統(tǒng)運(yùn)維、應(yīng)用開(kāi)發(fā)、物聯(lián)網(wǎng)實(shí)戰(zhàn)、全棧文章?等知識(shí)分享
“??花開(kāi)堪折直須折,莫待無(wú)花空折枝。?”
作者主頁(yè):[ https://www.weiyigeek.top ]??
博客:[?https://blog.weiyigeek.top?]
作者<安全開(kāi)發(fā)運(yùn)維>學(xué)習(xí)交流群,回復(fù)【學(xué)習(xí)交流群】即可加入
文章目錄:
0x00 如何自定義Gin服務(wù)配置及其啟動(dòng)多個(gè)服務(wù)?
0x01 如何優(yōu)雅的關(guān)閉或者重啟Gin應(yīng)用程序?
1.使用 chan 通道監(jiān)聽(tīng)中斷信號(hào)(SIGINT和SIGTERM)
2.使用 os/exec 包來(lái)執(zhí)行Gin平滑重啟
3.使用 fvbock/endless 包實(shí)現(xiàn)訪問(wèn)指定路由平滑重啟Gin服務(wù)
0x00 如何自定義Gin服務(wù)配置及其啟動(dòng)多個(gè)服務(wù)?
描述: 在Gin的生產(chǎn)環(huán)境中通常會(huì)自定義HTTP配置以達(dá)到最優(yōu)性能,此處我們簡(jiǎn)單一下 Server 結(jié)構(gòu)體中可配置的參數(shù)項(xiàng)。
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
// 配置監(jiān)聽(tīng)地址:端口,默認(rèn)是:8080
Addr string
// 要調(diào)用的處理程序,http.DefaultServeMux如果為nil
Handler Handler
// 如果為true,則將“OPTIONS*”請(qǐng)求傳遞給Handler
DisableGeneralOptionsHandler bool
// 提供TLS配置
TLSConfig *tls.Config
//讀取整個(gè)請(qǐng)求(包括正文)的最長(zhǎng)持續(xù)時(shí)間。
ReadTimeout time.Duration
// 讀取整請(qǐng)求(Header)的最長(zhǎng)持續(xù)時(shí)間。
ReadHeaderTimeout time.Duration
// 超時(shí)寫(xiě)入響應(yīng)之前的最長(zhǎng)持續(xù)時(shí)間
WriteTimeout time.Duration
// 啟用保持活動(dòng)時(shí)等待下一個(gè)請(qǐng)求的最長(zhǎng)時(shí)間
IdleTimeout time.Duration
// 控制服務(wù)器解析請(qǐng)求標(biāo)頭的鍵和值(包括請(qǐng)求行)時(shí)讀取的最大字節(jié)數(shù) (通常情況下不進(jìn)行設(shè)置)
MaxHeaderBytes int
// 在發(fā)生ALPN協(xié)議升級(jí)時(shí)接管所提供TLS連接的所有權(quán)。
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
// 指定了一個(gè)可選的回調(diào)函數(shù),當(dāng)客戶端連接更改狀態(tài)時(shí)調(diào)用該函數(shù)
ConnState func(net.Conn, ConnState)
// 為接受連接的錯(cuò)誤、處理程序的意外行為以及潛在的FileSystem錯(cuò)誤指定了一個(gè)可選的記錄器
ErrorLog *log.Logger
// 返回/此服務(wù)器上傳入請(qǐng)求的基本上下文
BaseContext func(net.Listener) context.Context
// 指定一個(gè)函數(shù)來(lái)修改用于新連接c的上下
ConnContext func(ctx context.Context, c net.Conn) context.Context
// 當(dāng)服務(wù)器處于關(guān)閉狀態(tài)時(shí)為true
inShutdown atomic.Bool
disableKeepAlives atomic.Bool
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
onShutdown []func()
listenerGroup sync.WaitGroup
}
模塊更新
go get -u golang.org/x/sync/errgroup
go mod tidy
示例代碼:
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
// 處理屬于同一總體任務(wù)的子任務(wù)的goroutine的集合
var (
g errgroup.Group
)
// s2 Gin 服務(wù)的 Handler
func router02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"msg": "Welcome server 02 blog.weiyigeek.top",
},
)
})
return e
}
func main() {
// Default返回一個(gè)Engine實(shí)例,該實(shí)例已連接Logger和Recovery中間件。
router := gin.Default()
// Gin 服務(wù)s1.用于運(yùn)行HTTP服務(wù)器的參數(shù) (常規(guī)參數(shù))
s1 := &http.Server{
// Gin運(yùn)行的監(jiān)聽(tīng)端口
Addr: ":8080",
// 要調(diào)用的處理程序,http.DefaultServeMux如果為nil
Handler: router,
// ReadTimeout是讀取整個(gè)請(qǐng)求(包括正文)的最長(zhǎng)持續(xù)時(shí)間。
ReadTimeout: 5 * time.Second,
// WriteTimeout是超時(shí)寫(xiě)入響應(yīng)之前的最長(zhǎng)持續(xù)時(shí)間
WriteTimeout: 10 * time.Second,
// MaxHeaderBytes控制服務(wù)器解析請(qǐng)求標(biāo)頭的鍵和值(包括請(qǐng)求行)時(shí)讀取的最大字節(jié)數(shù) (通常情況下不進(jìn)行設(shè)置)
MaxHeaderBytes: 1 << 20,
}
// Go在一個(gè)新的goroutine中調(diào)用給定的函數(shù),此處將Go語(yǔ)言的并發(fā)體現(xiàn)的淋漓盡致。
g.Go(func() error {
return s1.ListenAndServe()
})
// 配置Gin中間件
// Recovery返回一個(gè)中間件,該中間件可以從任何exception中恢復(fù),并在出現(xiàn)exception時(shí)寫(xiě)入500。
router.Use(gin.Recovery())
// 服務(wù)s1的路由
router.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"msg": "Welcome server 01 www.weiyigeek.top",
},
)
})
// Gin 服務(wù)s1.定義了不同的監(jiān)聽(tīng)端口以及Handler
s2 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
return s2.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
偷偷的告訴你喲?【極客全棧修煉】微信小程序已經(jīng)上線了,
可直接在微信里面直接瀏覽博主博客了喲,后續(xù)將上線更多有趣的小工具。
執(zhí)行結(jié)果:
0x01 如何優(yōu)雅的關(guān)閉或者重啟Gin應(yīng)用程序?
1.使用 chan 通道監(jiān)聽(tīng)中斷信號(hào)(SIGINT和SIGTERM)
描述: 在Go Gin中,可以使用以下代碼實(shí)現(xiàn)優(yōu)雅地重啟或停止, 確保所有連接都被正確關(guān)閉,避免數(shù)據(jù)丟失或損壞。
代碼示例:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
)
func main() {
// 創(chuàng)建 Gin 實(shí)例
router := gin.Default()
// 添加路由
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World! weiyigeek.top")
})
// 創(chuàng)建 HTTP Server
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
// 開(kāi)啟一個(gè)goroutine啟動(dòng)服務(wù) 啟動(dòng) HTTP Server
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中斷信號(hào)
quit := make(chan os.Signal)
// kill 默認(rèn)會(huì)發(fā)送 syscall.SIGTERM 信號(hào)
// kill -2 發(fā)送 syscall.SIGINT 信號(hào),我們常用的Ctrl+C就是觸發(fā)系統(tǒng)SIGINT信號(hào)
// kill -9 發(fā)送 syscall.SIGKILL 信號(hào),但是不能被捕獲,所以不需要添加它
// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信號(hào)轉(zhuǎn)發(fā)給quit
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此處不會(huì)阻塞
<-quit // 阻塞在此,當(dāng)接收到上述兩種信號(hào)時(shí)才會(huì)往下執(zhí)行
log.Println("Shutdown Server ...")
// 創(chuàng)建一個(gè) 5 秒的超時(shí)上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 關(guān)閉 HTTP Server
// // 5秒內(nèi)優(yōu)雅關(guān)閉服務(wù)(將未處理完的請(qǐng)求處理完再關(guān)閉服務(wù)),超過(guò)5秒就超時(shí)退出
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
}
代碼解析:
首先創(chuàng)建了一個(gè)Gin實(shí)例和一個(gè)HTTP Server,然后啟動(dòng)HTTP Server。接下來(lái),使用signal.Notify()
函數(shù)監(jiān)聽(tīng)中斷信號(hào)(SIGINT和SIGTERM),當(dāng)接收到中斷信號(hào)時(shí),服務(wù)器會(huì)進(jìn)入優(yōu)雅關(guān)閉流程,即先關(guān)閉HTTP Server
,然后等待5秒鐘,最后退出程序。
在關(guān)閉HTTP Server時(shí),我們使用了srv.Shutdown()
函數(shù),它會(huì)優(yōu)雅地關(guān)閉HTTP Server并等待所有連接關(guān)閉。如果在5秒鐘內(nèi)沒(méi)有關(guān)閉完所有連接,函數(shù)會(huì)返回錯(cuò)誤。
知識(shí)補(bǔ)充:?使用os/signal包實(shí)現(xiàn)對(duì)信號(hào)的處理, 最常見(jiàn)的信號(hào)列表。
2.使用 os/exec 包來(lái)執(zhí)行Gin平滑重啟
描述: 在Linux的Go-gin環(huán)境中我們可以使用 os/exec 包來(lái)執(zhí)行重啟命令,然后在 Gin 中定義一個(gè)路由,使得訪問(wèn)該路由時(shí)會(huì)執(zhí)行重啟命令。
代碼示例:
package main
import (
"fmt"
"net/http"
"os"
"os/exec"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 重啟的路由 /restart
r.GET("/restart", func(c *gin.Context) {
cmd := exec.Command("killall", "-HUP", "appweiyigeek")
err := cmd.Run()
if err != nil {
fmt.Println("Error executing restart command:", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to restart Gin server."})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Gin server restarted successfully."})
})
r.Run(":8080")
}
編譯執(zhí)行:
go build ./main.go -o appweiyigeek
./appweiyigeek
在上面的例子中,我們定義了一個(gè)路由 /restart,當(dāng)訪問(wèn)該路由時(shí),它會(huì)執(zhí)行?killall -HUP appweiyigeek
?命令來(lái)重啟 Gin 服務(wù), 這里的appweiyigeek
應(yīng)該替換為你實(shí)際的 Gin 應(yīng)用程序的名稱。
溫馨提示: 此種重啟方式可能會(huì)導(dǎo)致請(qǐng)求失敗或者超時(shí),因?yàn)樗鼤?huì)強(qiáng)制關(guān)閉正在處理的連接, 如果你需要更加優(yōu)雅的重啟方式,可以考慮使用優(yōu)雅重啟的方式。
3.使用 fvbock/endless 包實(shí)現(xiàn)訪問(wèn)指定路由平滑重啟Gin服務(wù)
描述: 由于endless在windows環(huán)境是不支持,所以博主針對(duì)下述代碼在Linux環(huán)境下載并編譯成二進(jìn)制文件打包到Linux環(huán)境運(yùn)行進(jìn)行驗(yàn)證。
依賴下載:
go get -u github.com/fvbock/endless
go mod tidy
代碼示例:
package main
import (
"fmt"
"log"
"net/http"
"os/exec"
"strconv"
"syscall"
"github.com/fvbock/endless"
"github.com/gin-gonic/gin"
)
func main() {
pid := syscall.Getpid()
// 1.默認(rèn)的Gin引擎
router := gin.Default()
// 傳統(tǒng)方式
// server := &http.Server{
// Addr: ":8080",
// Handler: router,
// ReadTimeout: 5 * time.Second,
// WriteTimeout: 10 * time.Second,
// }
// 2.獲取 Pid
router.GET("/pid", func(c *gin.Context) {
pid = syscall.Getpid()
fmt.Println("Pid:", pid)
c.JSON(http.StatusOK,
gin.H{
"code": http.StatusOK,
"msg": fmt.Sprintf("Gin Server Pid -> %d.", pid),
})
})
// 3.重啟 Gin 服務(wù)
router.POST("/restart", func(c *gin.Context) {
pid = syscall.Getpid()
fmt.Println("Restarting Gin Server.......", pid)
err := exec.Command("kill", "-1", strconv.Itoa(pid)).Run()
if err != nil {
fmt.Println("Error executing restart command:", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to restart Gin server."})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Gin server restarted successfully.", "pid": pid})
})
// 4.使用endless偵聽(tīng)TCP網(wǎng)絡(luò)地址addr,然后使用處理程序調(diào)用Serve來(lái)處理傳入連接上的請(qǐng)求
err := endless.ListenAndServe(":8080", router)
if err != nil || err != http.ErrServerClosed {
log.Println("err:", err)
}
// 5.引入了endless擴(kuò)展,將原本的Run方式啟動(dòng)項(xiàng)目改成了ListenAndServe方式所有此處主席掉
// router.Run(":8080")
}
編譯構(gòu)建:
# 切換編譯在Linux平臺(tái)的64位可執(zhí)行程序環(huán)境
go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
# 編譯
go build -o endless-test-1 .\main.go
# 執(zhí)行驗(yàn)證
chmod +x endless-test-1
nohup ./endless-test-1 &
[1] 1147978
執(zhí)行效果:
# GET 請(qǐng)求 10.20.176.101:8080/pid
# POST 請(qǐng)求 10.20.176.101:8080/restart
請(qǐng)求restart
后可以看見(jiàn)go-gin已經(jīng)平滑重啟了是不是很方便,效果如下。
親,文章就要看完了,不關(guān)注一下【全棧工程師修煉指南】嗎?
知識(shí)擴(kuò)展:
-
3、Windows下編譯Mac, Linux平臺(tái)的64位可執(zhí)行程序:
$ go env -w CGO_ENABLED=0 GOOS=darwin3 GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
-
2、Linux下編譯Mac, Windows平臺(tái)的64位可執(zhí)行程序:
$ go env -w CGO_ENABLED=0 GOOS=darwin GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=windows GOARCH=amd64
-
1、Mac下編譯Linux, Windows平臺(tái)的64位可執(zhí)行程序:
$ go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=windows GOARCH=amd64
本文至此完畢,更多技術(shù)文章,盡情等待下篇好文!
原文地址:?https://blog.weiyigeek.top/2023/6-2-745.html
如果此篇文章對(duì)你有幫助,請(qǐng)你將它分享給更多的人!?
?學(xué)習(xí)書(shū)籍推薦?往期發(fā)布文章?
公眾號(hào)回復(fù)【0008】獲取【Ubuntu22.04安裝與加固腳本】
公眾號(hào)回復(fù)【10001】獲取【W(wǎng)inServer安全加固腳本】
公眾號(hào)回復(fù)【10002】獲取【KylinOS銀河麒麟安全加固腳本】
公眾號(hào)回復(fù)【0011】獲取【k8S二進(jìn)制安裝部署教程】
公眾號(hào)回復(fù)【0014】獲取【Nginx學(xué)習(xí)之路匯總】
公眾號(hào)回復(fù)【0015】獲取【Jenkins學(xué)習(xí)之路匯總】
公眾號(hào)回復(fù)【10005】獲取【adb工具刷抖音賺米】
?熱文推薦??
-
Golang | Web開(kāi)發(fā)之Gin框架快速入門(mén)基礎(chǔ)實(shí)踐
-
Go開(kāi)發(fā)學(xué)習(xí) | 如何快速讀取json/yaml/ini等格式的配置文件使用示例
-
Go開(kāi)發(fā)學(xué)習(xí) | 如何使用Gomail.v2模塊包發(fā)送郵箱驗(yàn)證碼消息及附件學(xué)習(xí)記錄
-
Go開(kāi)發(fā)學(xué)習(xí) | 如何使用日志記錄模塊包針對(duì)日志按天數(shù)、按大小分隔文件示例
-
開(kāi)發(fā)基礎(chǔ) | Golang語(yǔ)言的RESTfulAPI接口設(shè)計(jì)規(guī)范快速入門(mén)
歡迎長(zhǎng)按(掃描)二維碼?,獲取更多渠道喲!
歡迎關(guān)注 【全棧工程師修煉指南】(^U^)ノ~YO
添加作者微信【weiyigeeker?】 一起學(xué)習(xí)交流吧!
關(guān)注回復(fù)【學(xué)習(xí)交流群】即可加入【安全運(yùn)維溝通交流小群】
溫馨提示:?由于作者水平有限,本章錯(cuò)漏缺點(diǎn)在所難免,希望讀者批評(píng)指正,若有問(wèn)題或建議請(qǐng)?jiān)谖恼履┪擦粝履鷮氋F的經(jīng)驗(yàn)知識(shí),或聯(lián)系郵箱地址
master@weiyigeek.top?或 關(guān)注公眾號(hào)?[全棧工程師修煉指南]?留言。
點(diǎn)個(gè)【贊 +?在看】吧!文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-481061.html
點(diǎn)擊【"閱讀原文"】獲取更多有趣的知識(shí)!? ?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-481061.html
到了這里,關(guān)于Golang | Web開(kāi)發(fā)之Gin多服務(wù)配置及優(yōu)雅關(guān)閉平滑重啟的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!