golang API开发过程的中的自动重启方式(基于gin框架)
作者:返回主页千里之行,始于足下 发布时间:2024-02-03 02:56:48
概要
基于 golang Gin 框架开发 web 服务时, 需要时不时的 go build , 然后重启服务查看运行结果.
go build 的过程集成在编辑器中(emacs), 可以通过快捷键迅速完成, 但是每次重启服务都切换到命令行中操作.
因此, 希望能够编译通过之后自动重启服务.
这里并不是部署阶段的服务重启, 所以不用过多考虑是否正常退出其中的协程.
实现方式
在开源的 illuminant 项目中, 已经将相应的代码集成到 gin 的 debug mode 中.
代码文件: https://gitee.com/wangyubin/illuminant/blob/dev/server_cmd.go
func setupWatcher() (chan struct{}, error) {
file, err := osext.Executable()
if err != nil {
return nil, err
}
log.Printf("watching %q\n", file)
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
done := make(chan struct{})
go func() {
select {
case e := <-w.Events:
log.Printf("watcher received: %+v", e)
err := syscall.Exec(file, os.Args, os.Environ())
if err != nil {
log.Fatal(err)
}
case err := <-w.Errors:
log.Printf("watcher error: %+v", err)
case <-done:
log.Print("watcher shutting down")
return
}
}()
err = w.Add(file)
if err != nil {
return nil, err
}
return done, nil
}
在 gin debug mode 下, 使用此方法自动重启服务
if c.Bool("prod") {
gin.SetMode(gin.ReleaseMode)
// start route
return routes.Routes(cnf.Server.Port)
} else {
gin.SetMode(gin.DebugMode)
watcher, err := setupWatcher()
if err != nil {
// do something sensible
log.Fatal(err)
}
defer close(watcher)
return routes.Routes(cnf.Server.Port)
}
补充
上面函数的核心有以下两点:
w, err := fsnotify.NewWatcher(): 创建监控文件变化的 watcher, err = w.Add(file) 并将当前二进制文件加入到监控文件列表中
err := syscall.Exec(file, os.Args, os.Environ()) 接受到文件变化的事件时, 重新调用一次自己, 使用上次一样的参数和环境变量
syscall.Exec
对于这个函数, 一般可能用的比较少, 这里稍微介绍下. 它有 3 个参数:
args[0]: 可执行文件的路径(相对路径, 绝对路径或者 PATH 中的路径都可以)
args[1]: 命令的参数
args[2]: 命令的执行的环境变量, os.Environ() 表示继承 caller 的环境变量
当 syscall.Exec 执行时, 在它之前的所有未执行完的程序都会被中止(包括在 go routine 中执行的程序),
然后执行 syscall.Exec 调用的命令, 该命令还保持在之前程序的 PID 下执行.
syscall.Exec 是最后一条执行的代码, 重启时在它之后可以有代码, 但是都不会被执行到, 包括 defer 中的代码.
下面是个小例子(通过这个例子可以验证上面的结论):
package main
import (
"fmt"
"log"
"os"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
"github.com/kardianos/osext"
)
func syscallExec() {
watcher, err := setupWatcher()
if err != nil {
log.Fatal(err)
}
defer finally(watcher)
fmt.Printf("current pid: %d\n", os.Getpid())
var count = 0
go func(count int) {
for {
fmt.Printf(">>> count in GO ROUTINE: %d\n", count)
count++
time.Sleep(1 * time.Second)
}
}(count)
for {
fmt.Printf(">>> count in MAIN: %d\n", count)
count++
time.Sleep(1 * time.Second)
}
}
func finally(watcher chan struct{}) {
// 重启时没有执行此函数
fmt.Println("exit original exec")
close(watcher)
}
func setupWatcher() (chan struct{}, error) {
file, err := osext.Executable()
if err != nil {
return nil, err
}
log.Printf("watching %q\n", file)
w, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
done := make(chan struct{})
go func() {
select {
case e := <-w.Events:
log.Printf("watcher received: %v", e)
err := syscall.Exec(file, os.Args, os.Environ())
if err != nil {
log.Fatal(err)
}
case err := <-w.Errors:
log.Printf("watcher error: %+v", err)
case <-done:
log.Print("watcher shutting down")
return
}
}()
err = w.Add(file)
if err != nil {
return nil, err
}
return done, nil
}
来源:https://www.cnblogs.com/wang_yb/archive/2020/12/14/14132498.html
猜你喜欢
- 本文实例分析了CI框架出现mysql数据库连接资源无法释放的解决方法。分享给大家供大家参考,具体如下:使用ci框架提供的类查询数据:$thi
- 初次安装完PyCharm后,新建项目时,遇到了No Python interpreter selected的问题。意思是说没有找到Pytho
- 前言在使用MySQL数据库的时候,有时候需要客户机直接远程登陆服务器的数据库 ,而不是将请求发给数据库服务器。这时候就需要配置下MySQL的
- 说明:本文内容都是从Google上搜索来的,本想上http://www.alexa.com/查官方数据,访问非常慢暂且没查。使用本接口将返回
- 鉴于最近复习线性代数计算量较大,且1800答案常常忽略一些逆阵、行列式的计算答案,故用Python写出矩阵的简单计算程序,便于检查出错的步骤
- 本文实例为大家分享了python遍历文件目录、批量处理同类文件的具体代码,供大家参考,具体内容如下目录操作1、获取当前目录import os
- function f(x){ alert(x); return
- 一、Cookie的使用1、什么是Cookie我们都知道,HTTP 协议是无状态的,也就是说,在一次请求响应结束后,服务端不会保留任何对方状态
- 如何在服务器端调用winzip命令行对上传的多个文件打包压缩?要解决这个问题,首先要了解一下Windows Scripting Host,简
- 要做到这一点,就要尽量地使用缓存,经常地从缓存中获得以前的消息。幸运的是目前大多数WAP设备都有一定级别的缓存,在默认情况下,会尝试最大化的
- 一个小项目自动登录淘宝联盟抓取数据,由于之前在Github上看过类似用Python写的代码因此选择用Python来写,第一次用Python正
- 在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当 * 作对象数目不大时,
- Python使用 continue 语句跳出循环,而break跳出整个循环。continue 语句用来告诉Python跳过当前循环的剩余语句
- 匿名函数lambdaPython使用lambda关键字创造匿名函数。所谓匿名,意即不再使用def语句这样标准的形式定义一个函数。这种语句的目
- 发现问题图片地址如下http://qximg.lightplan.cc/2016/09/11/1473574486942944.jpeg需要
- 一、判断数据库表是否存在: 首先要拿到数据库连接conn,调用DatabaseMetaData dbmd = conn.getDataMet
- 跨文件使用全局变量有多个py文件同时使用一个全局变量时,那应该如何使跨文件,用这个全局变量呢?1. 错误示范file1.py代码如下:num
- 适配器模式说明说明: 适配器模式,一般是为要使用的接口,不符本应用或本系统使用,而需引入的中间适配层类或对象的情况;场景: 就好比我们买了台
- 一 时间元组1. 时间元组和时间戳的互化import time,datetime# 获取当前时间的时间元组t = time.localtim
- 目录一. 加载数据1. 继承Dataset类并重写关键方法2. 使用Dataloader加载数据二. 模型设计三. 训练四. 测试结语pyt