欢迎关注「全栈工程师修炼指南」公众号
点击 下方卡片 即可关注我哟!
设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习!
专注 企业运维实践、网络安全、系统运维、应用开发、物联网实战、全栈文章 等知识分享
“ 花开堪折直须折,莫待无花空折枝。 ”
作者主页:[ https://www.weiyigeek.top ]
博客:[ https://blog.weiyigeek.top ]
作者<安全开发运维>学习交流群,回复【学习交流群】即可加入
文章目录:
0x00 如何自定义Gin服务配置及其启动多个服务?
0x01 如何优雅的关闭或者重启Gin应用程序?
1.使用 chan 通道监听中断信号(SIGINT和SIGTERM)
2.使用 os/exec 包来执行Gin平滑重启
3.使用 fvbock/endless 包实现访问指定路由平滑重启Gin服务
描述: 在Gin的生产环境中通常会自定义HTTP配置以达到最优性能,此处我们简单一下 Server 结构体中可配置的参数项。
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
// 配置监听地址:端口,默认是:8080
Addr string
// 要调用的处理程序,http.DefaultServeMux如果为nil
Handler Handler
// 如果为true,则将“OPTIONS*”请求传递给Handler
DisableGeneralOptionsHandler bool
// 提供TLS配置
TLSConfig *tls.Config
//读取整个请求(包括正文)的最长持续时间。
ReadTimeout time.Duration
// 读取整请求(Header)的最长持续时间。
ReadHeaderTimeout time.Duration
// 超时写入响应之前的最长持续时间
WriteTimeout time.Duration
// 启用保持活动时等待下一个请求的最长时间
IdleTimeout time.Duration
// 控制服务器解析请求标头的键和值(包括请求行)时读取的最大字节数 (通常情况下不进行设置)
MaxHeaderBytes int
// 在发生ALPN协议升级时接管所提供TLS连接的所有权。
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
// 指定了一个可选的回调函数,当客户端连接更改状态时调用该函数
ConnState func(net.Conn, ConnState)
// 为接受连接的错误、处理程序的意外行为以及潜在的FileSystem错误指定了一个可选的记录器
ErrorLog *log.Logger
// 返回/此服务器上传入请求的基本上下文
BaseContext func(net.Listener) context.Context
// 指定一个函数来修改用于新连接c的上下
ConnContext func(ctx context.Context, c net.Conn) context.Context
// 当服务器处于关闭状态时为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"
)
// 处理属于同一总体任务的子任务的goroutine的集合
var (
g errgroup.Group
)
// s2 Gin 服务的 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返回一个Engine实例,该实例已连接Logger和Recovery中间件。
router := gin.Default()
// Gin 服务s1.用于运行HTTP服务器的参数 (常规参数)
s1 := &http.Server{
// Gin运行的监听端口
Addr: ":8080",
// 要调用的处理程序,http.DefaultServeMux如果为nil
Handler: router,
// ReadTimeout是读取整个请求(包括正文)的最长持续时间。
ReadTimeout: 5 * time.Second,
// WriteTimeout是超时写入响应之前的最长持续时间
WriteTimeout: 10 * time.Second,
// MaxHeaderBytes控制服务器解析请求标头的键和值(包括请求行)时读取的最大字节数 (通常情况下不进行设置)
MaxHeaderBytes: 1 << 20,
}
// Go在一个新的goroutine中调用给定的函数,此处将Go语言的并发体现的淋漓尽致。
g.Go(func() error {
return s1.ListenAndServe()
})
// 配置Gin中间件
// Recovery返回一个中间件,该中间件可以从任何exception中恢复,并在出现exception时写入500。
router.Use(gin.Recovery())
// 服务s1的路由
router.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"msg": "Welcome server 01 www.weiyigeek.top",
},
)
})
// Gin 服务s1.定义了不同的监听端口以及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)
}
}
偷偷的告诉你哟?【极客全栈修炼】微信小程序已经上线了,
可直接在微信里面直接浏览博主博客了哟,后续将上线更多有趣的小工具。
执行结果:
0x01 如何优雅的关闭或者重启Gin应用程序?
1.使用 chan 通道监听中断信号(SIGINT和SIGTERM)
描述: 在Go Gin中,可以使用以下代码实现优雅地重启或停止, 确保所有连接都被正确关闭,避免数据丢失或损坏。
代码示例:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
)
func main() {
// 创建 Gin 实例
router := gin.Default()
// 添加路由
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, World! weiyigeek.top")
})
// 创建 HTTP Server
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
// 开启一个goroutine启动服务 启动 HTTP Server
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal)
// kill 默认会发送 syscall.SIGTERM 信号
// kill -2 发送 syscall.SIGINT 信号,我们常用的Ctrl+C就是触发系统SIGINT信号
// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它
// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
<-quit // 阻塞在此,当接收到上述两种信号时才会往下执行
log.Println("Shutdown Server ...")
// 创建一个 5 秒的超时上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 关闭 HTTP Server
// // 5秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超过5秒就超时退出
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
}
代码解析:
首先创建了一个Gin实例和一个HTTP Server,然后启动HTTP Server。接下来,使用signal.Notify()
函数监听中断信号(SIGINT和SIGTERM),当接收到中断信号时,服务器会进入优雅关闭流程,即先关闭HTTP Server
,然后等待5秒钟,最后退出程序。
在关闭HTTP Server时,我们使用了srv.Shutdown()
函数,它会优雅地关闭HTTP Server并等待所有连接关闭。如果在5秒钟内没有关闭完所有连接,函数会返回错误。
知识补充: 使用os/signal包实现对信号的处理, 最常见的信号列表。
2.使用 os/exec 包来执行Gin平滑重启
描述: 在Linux的Go-gin环境中我们可以使用 os/exec 包来执行重启命令,然后在 Gin 中定义一个路由,使得访问该路由时会执行重启命令。
代码示例:
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")
}
编译执行:
go build ./main.go -o appweiyigeek
./appweiyigeek
在上面的例子中,我们定义了一个路由 /restart,当访问该路由时,它会执行 killall -HUP appweiyigeek
命令来重启 Gin 服务, 这里的appweiyigeek
应该替换为你实际的 Gin 应用程序的名称。
温馨提示: 此种重启方式可能会导致请求失败或者超时,因为它会强制关闭正在处理的连接, 如果你需要更加优雅的重启方式,可以考虑使用优雅重启的方式。
描述: 由于endless在windows环境是不支持,所以博主针对下述代码在Linux环境下载并编译成二进制文件打包到Linux环境运行进行验证。
依赖下载:
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.默认的Gin引擎
router := gin.Default()
// 传统方式
// 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 服务
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侦听TCP网络地址addr,然后使用处理程序调用Serve来处理传入连接上的请求
err := endless.ListenAndServe(":8080", router)
if err != nil || err != http.ErrServerClosed {
log.Println("err:", err)
}
// 5.引入了endless扩展,将原本的Run方式启动项目改成了ListenAndServe方式所有此处主席掉
// router.Run(":8080")
}
编译构建:
# 切换编译在Linux平台的64位可执行程序环境
go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
# 编译
go build -o endless-test-1 .\main.go
# 执行验证
chmod +x endless-test-1
nohup ./endless-test-1 &
[1] 1147978
执行效果:
# GET 请求 10.20.176.101:8080/pid
# POST 请求 10.20.176.101:8080/restart
请求restart
后可以看见go-gin已经平滑重启了是不是很方便,效果如下。
亲,文章就要看完了,不关注一下【全栈工程师修炼指南】吗?
知识扩展:
3、Windows下编译Mac, Linux平台的64位可执行程序:
$ go env -w CGO_ENABLED=0 GOOS=darwin3 GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
2、Linux下编译Mac, Windows平台的64位可执行程序:
$ go env -w CGO_ENABLED=0 GOOS=darwin GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=windows GOARCH=amd64
1、Mac下编译Linux, Windows平台的64位可执行程序:
$ go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64
$ go env -w CGO_ENABLED=0 GOOS=windows GOARCH=amd64
本文至此完毕,更多技术文章,尽情等待下篇好文!
原文地址: https://blog.weiyigeek.top/2023/6-2-745.html
如果此篇文章对你有帮助,请你将它分享给更多的人!
学习书籍推荐 往期发布文章
公众号回复【0008】获取【Ubuntu22.04安装与加固脚本】
公众号回复【10001】获取【WinServer安全加固脚本】
公众号回复【10002】获取【KylinOS银河麒麟安全加固脚本】
公众号回复【0011】获取【k8S二进制安装部署教程】
公众号回复【0014】获取【Nginx学习之路汇总】
公众号回复【0015】获取【Jenkins学习之路汇总】
公众号回复【10005】获取【adb工具刷抖音赚米】
热文推荐
Golang | Web开发之Gin框架快速入门基础实践
Go开发学习 | 如何快速读取json/yaml/ini等格式的配置文件使用示例
Go开发学习 | 如何使用Gomail.v2模块包发送邮箱验证码消息及附件学习记录
Go开发学习 | 如何使用日志记录模块包针对日志按天数、按大小分隔文件示例
开发基础 | Golang语言的RESTfulAPI接口设计规范快速入门
欢迎长按(扫描)二维码 ,获取更多渠道哟!
欢迎关注 【全栈工程师修炼指南】(^U^)ノ~YO
添加作者微信【weiyigeeker 】 一起学习交流吧!
关注回复【学习交流群】即可加入【安全运维沟通交流小群】
温馨提示: 由于作者水平有限,本章错漏缺点在所难免,希望读者批评指正,若有问题或建议请在文章末尾留下您宝贵的经验知识,或联系邮箱地址
[email protected] 或 关注公众号 [全栈工程师修炼指南] 留言。
点个【赞 + 在看】吧!
点击【"阅读原文"】获取更多有趣的知识!