Server饭的技术分享
Server饭-用API发微信通知或用微信控制你的服务器
大家好,这是又一个向微信发通知的服务。这样的服务其实不少,一定是有新的特色我才好意思来介绍。 Server饭不仅可以给微信发通知,还能反过来用微信和服务器产生交互。大概像下面这样:
好了,还是先介绍基础功能吧。
主动发送通知
给自己发消息是最常用的功能。
拉到文末扫码关注服务号,或者在微信搜索 “LetServerRun” 这个服务号关注。
在服务号发送 token
命令查看自己的用户 token 。
使用用户 token,您就可以用 API 向公众号发警告消息了:
curl "https://api.letserver.run/message/info?token=YOUR-TOKEN&msg=hello"
这个 GET 接口是为了调试和轻量使用场景的,在程序中使用的话有 一系列接口和SDK。
反向控制服务器
Server饭的特色功能是用微信控制服务器做简单的事情。就像一开始的图里那样。 放心,不需要你提供ssh密钥,为了安全,命令能做什么完全由你定义。
实现的原理是在服务号中你发的命令会被存储在云端, Agent 每分钟向云端发起请求检查一次,如果有命令则拉回来执行它。 执行完成之后可以返回成功或者失败的结果,你就会在微信服务号上看到。
Agent 哪里来呢?有这么几种选择:
- 最自由:调用我们的API自己写
- 省事点:调用 SDK 自己写
- 够用就好:直接用我们几个开源的方案
这里我们先使用一个
开源的通用 Agent来上手。
它可以帮你在服务器执行特定的命令。后面我们可以根据需求,自己通过 API 或 SDK,集成 Agent 或自己编写。
假设你的服务器是 Debian/Ubuntu ,如果是别的请参考
安装通用Agent
如果您本身就是 root 用户,麻烦去掉所有命令中的 sudo
# 注册仓库
curl -1sLf \
'https://dl.cloudsmith.io/public/hackfan/skadi/setup.deb.sh' \
| sudo -E bash
# 更新
apt update
# 安装
apt install skadi
在安装后,因为还没有 Token,所以并没有自动启动。
Token 哪来的呢?在服务号输入命令: agent add 名字
(名字是要你给它取个简单的名字,以后每次都要用它发命令)
然后将得到的 Token 写入配置文件,像下面这样。
你也可以编辑 /etc/skadi/skadi.yml
这个文件自己写入。
# 写入 Token
sudo skadi AGENT-TOKEN
# 启动服务
sudo systemctl start
只有第一次需要配置Token后手动启动服务,服务器重启它是会依靠systemd自己启动的。
然后就可以试用这个 Agent 了。
在公众号输入 名字 help
,看看 Agent 自己的帮助。
这个官方的通用 Agent 功能由你部署它的服务器上的 /etc/skadi/skadi.yml
这个配置文件定义。
然后你可以顺次输入名字 date
,名字 lsroot
,名字 free -m
,名字 Hi Fool
,
去试用,接下来,更改配置文件就可以完成重启服务,查看状态等简单的动作了。
只是想现在看看的话,也可以看看 代码仓库的版本
更多功能
因为目前只是提供了一个舞台,更多的功能在持续的发掘中,我们会陆续更新文档中的 cookbook:
- 在 CI 服务中通知微信,甚至直接给 Agent 发任务进行持续部署
- Agent 之间链式发送任务配合完成工作
- 集成在业务系统中当作一个简单的控制台,比如清除缓存,封禁用户等操作,微信上就搞定了。
- 当成一个 延迟队列使用
- 控制家里的电脑
- 控制路由器或者 NAS
- 更多用法等待你开发脑洞~
最后,二维码在这里哦,暂时用不到也可以扫扫留着备用,只有重大更新才会推送通知,平时0骚扰哦。
使用 Go 1.16 的 signal.NotifyContext 让你的服务重启更优雅
在 Go 1.16 的更新中,signal
包增加了一个函数
NotifyContext,
这让我们优雅的重启服务(Graceful Restart)可以写的更加优雅。
一个服务想要优雅的重启主要包含两个方面:
- 退出的旧服务需要
Graceful Shutdown
,不强制杀进程,不泄漏系统资源。 - 在一个集群内轮流重启服务实例,保证服务不中断。
第二个问题跟部署方式相关,改天专门写一篇讨论,今天我们主要谈怎么样优雅的退出。
首先在代码里,用了外部资源,一定要使用defer
去调用Close()
方法关闭。
然后我们就要拦截系统的中断信号,保证程序收到中断信号之后,主动有序退出,这样所有的 defer 才会被执行。
在以前,大概是这么写:
func everLoop(ctx context.Context) {
LOOP:
for {
select {
case <-ctx.Done():
// 收到信号退出无限循环
break LOOP
default:
// 用一个 sleep 模拟业务逻辑
time.Sleep(time.Second * 10)
}
}
}
func main() {
// 建立一个可以手动取消的 Context
ctx, cancel := context.WithCancel(context.Background())
// 监控系统信号,这里只监控了 SIGINT(Ctrl+c),SIGTERM
// 在 systemd 和 docker 中,都是先发 SIGTERM,过一段时间没退出再发 SIGKILL
// 所以这里没捕获 SIGKILL
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sig
cancel()
}()
// 开始无限循环,收到信号就会退出
everLoop(ctx)
fmt.Println("graceful shuwdown")
}
现在有了新的函数,这一段变得更简单了:
func main() {
// 监控系统信号和创建 Context 现在一步搞定
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
// 在收到信号的时候,会自动触发 ctx 的 Done ,这个 stop 是不再捕获注册的信号的意思,算是一种释放资源。
defer stop()
// 开始无限循环,收到信号就会退出
everLoop(ctx)
fmt.Println("graceful shuwdown")
}
最后,还有一个值得注意的问题:如果有多个 goroutine 利用 context 做取消信号,我们需要保证全部退出了才能退出主进程。
目前还没有特别优雅的办法,需要为每个 goroutine 传递一个 sync.WaitGroup 的指针,最后在退出前 Wait 一下。
感谢 Golang
,当年用别的语言需要写一大堆代码的功能,现在几行就可以轻松实现了。
让它成为你服务程序的标配吧。
最后,我是写最新的项目 Server饭的时候,发现这种最新的写法的。 Server饭 可以让你把微信公众号当作随身的 Terminal 控制你的服务端。 在它的 Agent 的 main 函数中就有上述用法的示例,欢迎参考。
附上 Server饭 的服务号二维码,感兴趣的同学可以关注一下: