Mongo 副本集 选择副本后 连不上 server selection error

failed: server selection error: context deadline exceeded, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: 127.0.0.1:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp 127.0.0.1:27017: connect: connection refused

Mongo 副本集 选主错误 server selection error

问题

failed: server selection error: context deadline exceeded, current topology: { Type: ReplicaSetNoPrimary, Servers: [{ Addr: 127.0.0.1:27017, Type: Unknown, Last error: connection() error occurred during connection handshake: dial tcp 127.0.0.1:27017: connect: connection refused

解决否

已解决

方案

登陆 mongo server 输入命令

rs.status()

rs.status

这里看图片里,name是: mongolab:27021, 那么应用也会用这个地址去连 mongodb,如果 dns 里没有 mongolab,那么应用是连不上这个 mongo 的,最终报错就是链接超时。

查看一下Mongo集群之间的通讯 IP,或者地址是怎么配置,是否配置的IP的应用端能访问的IP段。

例如,三个 mongo 集群配置在同一网段内—— 192.168.1.0 ,如果你也按照这个 IP 来配置副本集,那么 mongo 之间通信是没问题的,但如果应用端可能读取 replica 信息后,会那副本集配置的 IP 信息去访问其他节点,这个时候应用会拿着 192.168.1.0 这个网断的 IP 去访问 mongo,如果 mongo 和应用不在一个网段,就会导致如上问题。所以,mongoreplica 配置的IP也需是应用可访问的 IP

这也是,往往运维部署,发现,mongo 互通 OK,运维溜了。等应用来连,却连不上问题。

参考

  • https://www.mongodb.com/docs/manual/replication/
  • https://www.mongodb.com/docs/manual/reference/method/rs.initiate/

Golang copier入门到入坑

一个小小copier.Copy函数你确定是用对了么?快来看看还有哪些隐含参数,哪种类型变量会有隐含坑呢?还有个copier.CopyWithOption你用过么?

github: https://github.com/jinzhu/copier

由于 golang 没有对复杂结构体的 clone 方法,所以,就需要有 copier 这样的工具库。

它看起来很简单,但实际使用中,有些“坑”还是要注意!

本文:

入门为辅,探“坑”为主,

看完再划走,CS我没有。

安装

go get github.com/jinzhu/copier

快速入门

好的,来一段代码快速了解 copier

package main

import (
    "fmt"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}


type M1 struct {
    A int
    W string
    S *SC
}

func main() {
    var src = M1{12, "Hello", &SC{32}}
    var dst = M1{}
    fmt.Printf("before copy src %+v\tdst %+v\n", src, dst)
    copier.Copy(&dst, src)
    fmt.Printf("after  copy src %+v\tdst %+v\n", src, dst)
}

输出:

before copy src {A:12 W:Hello S:0xc00017f550}   dst {A:0 W: S:<nil>}
after  copy src {A:12 W:Hello S:0xc00017f550}   dst {A:12 W:Hello S:0xc00017f618}

好的,看到这,你就已掌握了 copier 80%的功能了。先别着急划走,接下来还是踩坑记录。

本文代码运行输出内容是基于 github.com/jinzhu/copier@v0.3.5 和 go1.16.1 darwin/amd64 环境演示的结果。

http://itjsz.com

入坑

package main

import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}

type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}

func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)
    copier.Copy(&dst1, src)
    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

以上代码运行后会输出:

before src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}          dst {M:<nil> A:<nil> C:<nil>}

befre 那一行代码如上⬆️ , after 那一行会输出什么呢?

1. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
2. after  src {M:map[C::3 d:4] A:[9 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
3. after  src {M:map[C::3 d:4] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
4. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:32}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}
5. after  src {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a1e8){C:27}}  dst {M:map[C::3 d:4 F:5 g:6] A:[7 8] C:<*>(0xc00012a348){C:27}}

答案是: var a = int(759 / 6 / 31.5)

为了避免不小心看了答案,请计算 759 / 6 / 31.5 得出的值四舍五入便是。

再探坑

出坑

我看其他同学使用 copier 也是像上面那样——copier.Copy($dst, src), 当然啦,也不排除我!它仿佛就是一把小巧精悍的小刀。一个简单的函数调用,就完成了它的使命。

然而,它其实是把多功能刀,我都还没有打开它的—— option

上面的问题就是,我 Copy 后,对值的改动,影响了另一个值的 map,那么这个时候,就需要进行深 copy。接下来引入 copieroption

package main

import (
    "fmt"
    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type SC struct {
    C uint8
}

type Map1 struct {
    M map[string]int32
    A []int32
    C *SC
}

func main() {
    var src = Map1{map[string]int32{"C:": 3, "d": 4}, []int32{9, 8}, &SC{32}}
    var dst1 = Map1{}
    spew.Printf("before src %+v\t\tdst %+v\n", src, dst1)

    copier.CopyWithOption(&dst1, src, copier.Option{DeepCopy: true})   // 这里!

    dst1.M["F"] = 5
    dst1.M["g"] = 6
    dst1.A[0] = 7
    dst1.C.C = 27
    spew.Printf("after  src %+v\tdst %+v\n", src, dst1)
}

好的,这样copy之后,对新变量的改动,不会传递会原变量改动了。

再盘一盘坑

package main

import (
    "fmt"

    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/copier"
)

type ArrTC struct {
    Name [2]string
    C    *ArrTC
}

type ArrT struct {
    A  [3]int32
    S  []int32
    E  []int32
    C  string
    V  string
    M map[string]int32
    AC ArrTC
    s bool
}

func main() {
    var src = ArrT{
        [3]int32{9, 10, 0},
        []int32{12, 0},
        []int32{},
        "",
        "val",
        map[string]int32{"A:": 1, "b": 0},
        ArrTC{},
        true,
    }
    var dst = ArrT{
        [3]int32{1, 2, 3},
        []int32{4, 5, 6, 7},
        []int32{9, 10},
        "char",
        "ha",
        map[string]int32{"C:": 3, "b": 4, ".": 0},
        ArrTC{[2]string{"Y", "Z"}, nil},
        false,
    }
    spew.Printf("before src %+v\tdst %+v\n", src, dst)
    copier.CopyWithOption(&dst, src, copier.Option{IgnoreEmpty: true, DeepCopy: true})
    spew.Printf("after  src %+v\tdst %+v\n", src, dst)
    src.M["b"] = 99
    src.S[1] = 1
    dst.S[0] = 2
    spew.Printf("last  src %+v\tdst %+v\n\n", src, dst)
}

输出:

before src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true} dst {A:[1 2 3] S:[4 5 6 7] E:[9 10] C:char V:ha M:map[C::3 b:4 .:0] AC:{Name:[Y Z] C:<nil>} s:false}
after  src {A:[9 10 0] S:[12 0] E:[] C: V:val M:map[A::1 b:0] AC:{Name:[ ] C:<nil>} s:true} dst {A:[9 10 0] S:[12 0 6 7] E:[9 10] C:char V:val M:map[A::1 C::3 b:0 .:0] AC:{Name:[Y Z] C:<nil>} s:true}
last  src {A:[9 10 0] S:[12 1] E:[] C: V:val M:map[A::1 b:99] AC:{Name:[ ] C:<nil>} s:true} dst {A:[9 10 0] S:[2 0 6 7] E:[9 10] C:char V:val M:map[C::3 b:0 .:0 A::1] AC:{Name:[Y Z] C:<nil>} s:true}

这次的代码我加上了 IgnoreEmpty: true, 也就是复制时忽略空的值。 也就说可以当作值 merge 用。

然后,又测试了一下变量独立性。复制之后,src, dst 两个变量再无瓜葛,对其中一个值的任意改动都不会同步到另一个值。

但是,这个 merge 的表现,可能不是你想的那样,

src.S = []int32{12, 0}
dst.S = []int32{4, 5, 6, 7}
## 调用 copy 后, 你预期的结果是什么?[6/7]
6. dst.S = []int32{12, 0}
7. dst.S = []int32{12, 0, 6, 7}
  • 选项6: 嗯,原来是 {12, 0} 复制给 dst 就是 {12, 0}
  • 选项7: 这个是切片,你只给我 0,1 位的值,copier把 0,1 位置的值 copy 了,dst后面2,3位的值,src没给出,那就不管。所以就是 {12, 0, 6, 7}

这块的表现,我觉得是有争议的,大佬们在评论区留下你预期选项,看看大家是不是都这样想的。

实际运行结果,见上面的代码输出就能找到答案。

结语

copier 本来是一个短小精悍的工具库,也没想要水一篇,最近使用时,突然踩坑,就特开一篇,和大家分享一下踩坑经验。

在使用外部库的时候,还是建议去 github 上看看详细说明, 或者上 pkg.go.dev 看看它暴露出来出的接口以及说明。更或者进行完整的测试,充分了解它之后,再使用。

PS: 最近项目中使用这个库,发现问题后,改疯了~

qrcode

Ubuntu 安装 MongoDB

Ubuntu 下如何用 apt-get 安装最新的 MongoDB

Ubuntu 安装 MongoDB

官网:https://www.mongodb.com/

社区版下载地址: https://www.mongodb.com/try/download/community

安装命令

## 安装依赖
curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -

## 查看添加成功没
apt-key list

echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list

## 更新 apt
sudo apt update

## 安装 mongo
sudo apt install mongodb-org

## 启动服务
sudo systemctl start mongod.service

## 查看服务状态
sudo systemctl status mongod

## 启用状态
sudo systemctl enable mongod

## 停用状态
sudo systemctl stop mongod

初始化配置

vim /etc/mongod.conf
# network interfaces
net:
  port: 27017
#  bindIp: 127.0.0.1
  bindIp: 0.0.0.0

编辑了用户记得重启

sudo systemctl restart mongod

登陆 mongo

在安装了mongod的服务本机,mongo是无密码的,可输入如下命令直接登陆

$ mongo

远端通过如下命令登陆,未设置密码情况下,绑定了外网地址情况下,也能登陆

$ mongo "mongodb://192.168.0.x:27017"

创建用户

  • 产看所用用户
> use admin
switched to db admin
> db.system.users.find().pretty()

注意:用户相关的操作都需要 在admin db下操作!

创建 root 用户

> db.createUser(
    {
        user:"root",
        pwd:"pwd",
        roles:["root"]
    }
)

注意: 密码最好不要使用 & ? # : $ ; / . @ 等符号,因为 mongo DSN 格式是个 url,url 相关的关键词都不要用哦!

创建 admin 用户

> db.createUser(  
  { user: "admin",  
    customData:{description:"superuser"},
    pwd: "admin",  
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]  
  }  
)  

创建业务用户

> db.createUser({
    user:"user001",
    pwd:"123456",
    customData:{
        name:'jim',
        email:'jim@qq.com',
        age:18,
    },
    roles:[
        {role:"readWrite",db:"db001"},
        {role:"readWrite",db:"db002"},
        'read'// 对其他数据库有只读权限,对db001、db002是读写权限
    ]
})

用户管理

  • 改密码
use admin
db.changeUserPassword("username", "xxx")
  • 为用户追加权限
db.grantRolesToUser( "<username>", [ <roles> ], { <writeConcern> } )
  • 删除用户
use admin
db.dropUser('user001')

安全设置

访问mongo认证配置

在无密码的mongo上配置好用户后,就可以启用安全认证了。

vim /etc/mongod.conf
#security:

修改为:

security:
  authorization: enabled

如果是 mongo 低于 2.6 那么在配置文件最后一行加上:

auth = true

参考

  • https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-ubuntu-20-04
  • https://segmentfault.com/a/1190000015603831
  • https://stackoverflow.com/questions/25325142/how-to-set-authorization-in-mongodb-config-file

float64 can only be truncated to an integer type when truncation is enabled

使用 mongo-go-dirver 反序列化到 float32 报错

error decoding key weight: float64 can only be truncated to an integer type when truncation is enabled

问题描述

通过 mongo-go-dirver findOne 查数据并反序列化到 结构体时,有个字段:

type User struct {
    Name      string    `bson:"name"`
    Age       uint32    `bson:"age"`
    Weight    float32   `bson:"weight"`
    Studying  bool      `bson:"studying"`
    Tag       []string  `bson:"tag"`
    CreatedAt time.Time `bson:"created_at"`
}

weight 字段类型为 float32,就提示:

float64 类型的值只能在启用截断时截断为整数类型

问题如何产生的

存入数据时,手写 68.1,在 Golang 中手写的 float 值默认为 float64, 所以就当 float64 存入mongo了,待你想反序列化到 float32的值时,就报错了。

在 mgo 中,程序会自动帮你截断转换了,但是在 mongo-go-driver 中需要手动处理,或者显示得配置解决方案,不然会导致报错,不输出数据!

解决否

已解决

方案

问题原因时 浮点数 decode时,超出了精度,mongo-go-driver就把这个问题抛了个error出来。那么针对这个报错,我们可以从两方面去解决。要么提高精度,要么高速golang,多的截断,咋不要了。

  1. 把float32改为 float64
  2. 在bson tag 追加 ,truncate
    Weight    float32   `bson:"weight,truncate"`

参考

  • https://segmentfault.com/a/1190000038451313

Golang 轮子之 Supervisor

golang 与Supervisor进程管理工具之间会擦出怎样的火花呢,来看看新轮子go-supervisor

Supervisor 是一个强大的 进程管理工具。

在非容器化管理的服务器上, Supervisor 是有非常广泛的使用场景的。

例如:

服务批量重启,多服务按顺序启动,服务oom后自动拉起,服务std日志收集等,甚至服务健康检查它都能做。

原 Supervisor (Python)

git: https://github.com/Supervisor/supervisor

doc: http://supervisord.org/

新轮子 Supervisor (Golang)

git: https://github.com/ochinchina/supervisord

对比

两个 Supervisor 对比

指标\语言 Python Golang
起源 2004 2017
当前版本 4.2.4 0.7.3
语言版本要求 2.7+ 或 3.4+ 1.11+
*unix 支持 支持
MacOS 支持 支持
Widnows 不支持 能跑
安装包大小 Pyton环境(40MB) + 脚本(490KB) 4.2MB
Web GUI 支持 支持

功能支持情况

共呢个\语言 Python Golang
分组 支持 支持
挂了自动拉起 支持 支持
定时重启 支持 支持
web端管理 支持 支持
监控文件自动重启 支持 支持
依赖顺序启动 支持 支持

这里只是列举了常用的功能,基本都实现了的,依靠golang按需runtime+可执行代码打包后,二进制部署相较 python 是更为方便和小巧的。

安装

gihub 上没有二进制包,需要clone代码,手动编译。

$ git clone https://github.com/ochinchina/supervisord
$ cd supervisord
$ go generate

# 以下代码会编译出 linux 平台二进制可执行文件
$ GOOS=linux go build -tags release -a -ldflags "-linkmode external -extldflags -static" -o supervisord
# mac 下
$ go build -tags release -o supervisord

试试

$ ./supervisord --help
Usage:
  supervisord [OPTIONS] <command>

Application Options:
  -c, --configuration= the configuration file
  -d, --daemon         run as daemon
      --env-file=      the environment file

Help Options:
  -h, --help           Show this help message

Available commands:
  ctl      Control a running daemon
  init     initialize a template
  service  install/uninstall/start/stop service
  version  show the version of supervisor

使用

  • 先创建一个配置文件
$ vi supervisor.conf
[program:test]
command = watch -n 5 "echo Hello!"
  • 启动
$ supervisord -c supervisor.conf
INFO[2022-10-15T17:31:24+08:00] load configuration from file                  file=./supervisor.conf
INFO[2022-10-15T17:31:24+08:00] create process:test
INFO[2022-10-15T17:31:24+08:00] stop listening
INFO[2022-10-15T17:31:24+08:00] try to start program                          program=test
DEBU[2022-10-15T17:31:24+08:00] wait program exit                             program=test
INFO[2022-10-15T17:31:25+08:00] success to start program                      program=test

## 此时该 supervisord 会前台运行,退出终端,或者 Ctrl+C 都会推出,会结束所有的程序。
^CINFO[2022-10-15T17:32:39+08:00] receive a signal to stop all process & exit   signal=interrupt
INFO[2022-10-15T17:32:39+08:00] stop the program                              program=test
INFO[2022-10-15T17:32:39+08:00] force to kill the program                     program=test
INFO[2022-10-15T17:32:39+08:00] Send signal to program                        program=test signal=killed
INFO[2022-10-15T17:32:39+08:00] program stopped with status:signal: killed    program=test
INFO[2022-10-15T17:32:39+08:00] program exited                                program=test
INFO[2022-10-15T17:32:39+08:00] Stopped by user, don't start it again         program=test
  • 启动并运行到后台
$ supervisord -c supervisor.conf -d

这样就启动了

http 管理

supervior 同样提供了 Web GUI 管理入口,我们来启用配置试试

[program:test]
command = watch -n 5 "echo Hello"

[inet_http_server]
port=127.0.0.1:9001

访问: http://127.0.0.1:9001

web_gui

同样支持 http Auth, 按照如下配置

[inet_http_server]
port=127.0.0.1:9001
username=test1
password=thepassword

注意: Shutdown 是停掉 supervisor 服务本身,包括 Web 入口,需要登陆到服务器,手动启动后,才能继续使用。要停掉所有自程序,选择全部然后点击 Stop Select

文件监控

当我们部署,或更新程序时,希望 supervisor 能自动关闭,并运行新的可执行文件,那么 文件监控 功能就派上用场了。

go-supervisor 支持多种文件监控模式:

  1. 执行的程序本身监控
  2. 某个文件夹内监控
  3. 文件监控
  • 配置方式
[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true

这里的测试代码我放到文章最后了

INFO[2022-10-15T20:33:20+08:00] program is changed, restart it                program=golang
INFO[2022-10-15T20:33:20+08:00] stop the program                              program=golang
INFO[2022-10-15T20:33:20+08:00] force to kill the program                     program=golang
INFO[2022-10-15T20:33:20+08:00] Send signal to program                        program=golang signal=killed
INFO[2022-10-15T20:33:20+08:00] program stopped with status:signal: killed    program=golang
INFO[2022-10-15T20:33:20+08:00] program exited                                program=golang
INFO[2022-10-15T20:33:20+08:00] Stopped by user, don't start it again         program=golang
INFO[2022-10-15T20:33:21+08:00] try to start program                          program=golang
DEBU[2022-10-15T20:33:21+08:00] wait program exit                             program=golang
INFO[2022-10-15T20:33:22+08:00] success to start program                      program=golang

监控到变化后,重启方式也有两种,一种是:直接kill。另一种是发送信号量给程序,让程序自行处理。

注意: 如果 supervisor 本身发了 kill 信号给程序,程序自己结束了,superviosr 默认也不会帮你在重启程序,它的设计逻辑时,我只负责发信号,其他程序自理。这里你可以手动新增一条配置:

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
autostart=true #  这行配置

如果管理了在线的大流量服务,推荐使用第二种,平滑重启,因为直接kill程序,会导致请求处理一半,或事务进行到一半中止,进而数据不一致。

好的,我们再次调整配置

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=9 # SIGKILL

来看下日志:

INFO[2022-10-15T20:37:58+08:00] program is changed, restart it                program=golang
INFO[2022-10-15T20:37:58+08:00] Send signal to program                        program=golang signal=terminated
INFO[2022-10-15T20:37:58+08:00] program stopped with status:exit status 0     program=golang
INFO[2022-10-15T20:37:58+08:00] program exited                                program=golang
INFO[2022-10-15T20:37:58+08:00] Don't start the stopped program because its autorestart flag is false  program=golang

注意这里的日志,说的是,supersivor 给程序发了 信号,但是程序退出了,由于,你启用自动重启配置,所有,没有启动该程序。

这里是信号发错了,调整一下:

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf ./Users/paulxu/golang/go-learn/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=SIGHUP # 1 这里填数字字符都行

这下重新启动supervisor,看下效果。

程序运行日志:

2022-10-16 11:00:27.754 [INFO] main.go:13: start
2022-10-16 11:00:27.754 [INFO] main.go:21: waiting signal~
2022-10-16 11:00:28.755 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:29.757 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:30.761 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:31.765 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:32.768 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:33.771 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:34.774 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:35.779 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:36.783 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:37.659 [INFO] main.go:32: golang get signal hangup [sighup]
2022-10-16 11:00:37.788 [INFO] main.go:17: golang program is running~
2022-10-16 11:00:38.790 [INFO] main.go:17: golang program is running~

这样之后,我们就能实现部署新的程序后,自动平滑重启程序了。

注意:在 Web 端,手动 stop/start 程序,不会发信号量到程序!

监控文件夹

刚刚展示目标程序变更,自动重启。那么配置文件更新了,自动重启如何配置呢?

注意:如果程序内自动监控了文件变化并更新配置(推荐这样做),则不需要 supervisor 来发信号给程序本身了。

这里新增了两行配置,1.配置监控存放配置文件的文件夹,2. 配置文件夹内文件变化时,发什么信号通知程序。

[program:golang]
command = /Users/paulxu/golang/go-learn/main -conf /Users/paulxu/golang/go-learn/config/config.toml
restart_when_binary_changed=true
restart_signal_when_binary_changed=SIGHUP
restart_directory_monitor=/Users/paulxu/golang/go-learn/config/
restart_signal_when_file_changed=SIGHUP

测试代码

package main

import (
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gogf/gf/frame/g"
)

func main() {
    g.Log().Line().Info("start!!")
    go func() {
        for {
            time.Sleep(time.Second)
            g.Log().Line().Info("golang program is running~")
        }
    }()

    g.Log().Line().Info("waiting signal~")
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
    for {
        select {
        case s := <-c:
            switch s {
            case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
                g.Log().Line().Infof("golang get signal %+v", s)
                return
            case syscall.SIGHUP:
                g.Log().Line().Infof("golang get signal %+v [sighup]", s)
            default:
                g.Log().Line().Infof("golang get other signal %+v", s)
                return
            }
        }
    }
}

最后

好的,今天给大家介绍了一款 superviorgolang轮子,以及基本使用方法。可以看到一些常用的基础和golang碰撞后,擦出了不一样的火花。由于 Go 语言的编译工具链会全静态链接构建二进制文件,All in One 的设计理念,对运维部署时非常友好的。期待更多这样的轮子。

虽然,在当前容器化时代,它的使用场景被进一步挤压,但是在小型站点,实体机上使用还是很方便的。

关注我,了解更多golang知识~

qrcode