浅谈如何使用 github.com/kardianos/service - 画个一样的我
source link: https://www.cnblogs.com/huageyiyangdewo/p/17367532.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在实际开发过程中,有时候会遇到如何编写Go开机自启服务的需求,在linux中我们可以使用systemd
来进行托管,windows下可以通过注册表
来实现,mac下可以通过launchd
来实现,上面的方式对于开发者来说,并不是什么困难的事情,但是对于使用者而言,是并不希望通过这么复杂的方式来达到开机自启的功能的。这个时候,作为开发者,就需要使用其他的方式来实现开机自启的功能,下面讲一个Go中,借助这个库 github.com/kardianos/service 来简化如何实现开机自启功能。
1、github.com/kardianos/service 基础介绍
1.1 kardianos/service 简介
我们先来看一看 github.com/kardianos/service 上面的自我介绍:Run go programs as a service on major platforms.
如何理解上面这句话呢,上面这句话翻译出来的意思是:"在主要平台上将Go程序作为服务运行"。
这意味着我们可以将Go编写的程序以服务的形式在主要操作系统上运行,例如Windows、Linux、macOS等。这意味着程序可以在后台持续运行,而不需要用户手动启动或停止它们。这种方式可以提高程序的可靠性和稳定性,同时也方便了程序的管理和监控。
那该如何理解服务
呢?
服务(Service)是指在计算机系统中,为用户或其他程序提供某种功能的程序或进程。服务通常在后台运行,可以长时间运行,不需要用户交互,可以自动启动和停止。服务可以提供各种功能,如网络服务、数据库服务、文件共享服务等。在操作系统中,服务通常以服务进程的形式运行,可以通过系统管理工具进行管理和配置。
有了上面的了解过后,再来看看官方自己的描述。service will install / un-install, start / stop, and run a program as a service (daemon). Currently supports Windows XP+, Linux/(systemd | Upstart | SysV), and OSX/Launchd.
如何理解上面这句话呢,我说说自己的理解。
我们可以将编写好的代码打包成二进制文件后,通过
二进制文件名 + install / un-install, start / stop
来运行我们的服务,程序将作为服务(守护进程)运行。目前支持Windows XP+、Linux/(systemd|Upstart|SysV)和OSX/Launchd。
Windows controls services by setting up callbacks that is non-trivial. This is very different then other systems. This package provides the same API despite the substantial differences. It also can be used to detect how a program is called, from an interactive terminal or from a service manager.
下面是我的理解:
Windows 通过设置回调来控制服务,这与其他系统非常不同。这个包提供了相同的API,尽管存在很大差异。它还可以用于检测程序是从交互式终端还是从服务管理器调用的。
看到这里的时候,我其他不太理解最后一句话,什么叫从
服务管理器
调用。将在 2.2 章节中介绍。
1.2 kardianos/service 安装
安装 github.com/kardianos/service 的方式和其他方式一样。
go get github.com/kardianos/service
指定版本方式
go get github.com/kardianos/[email protected]
2、kardianos/service 使用方式
以下介绍都是基于 github.com/kardianos/[email protected] 进行讲解的。
2.1 kardianos/service 简单的使用
我们先来看一个简单的例子,代码如下:
package main
import (
"fmt"
"github.com/kardianos/service"
"os"
)
type SystemService struct {}
func (ss *SystemService) Start(s service.Service) error {
fmt.Println("coming Start.......")
go ss.run()
return nil
}
func (ss *SystemService) run() {
fmt.Println("coming run.......")
}
func (ss *SystemService) Stop(s service.Service) error {
fmt.Println("coming Stop.......")
return nil
}
func main() {
fmt.Println("service.Interactive()---->", service.Interactive())
svcConfig := &service.Config{
Name: "custom-service",
DisplayName: "custom service",
Description: "this is github.com/kardianos/service test case",
}
ss := &SystemService{}
s, err := service.New(ss, svcConfig)
if err != nil {
fmt.Printf("service New failed, err: %v\n", err)
os.Exit(1)
}
if len(os.Args) > 1 {
err = service.Control(s, os.Args[1])
if err != nil {
fmt.Printf("service Control 111 failed, err: %v\n", err)
os.Exit(1)
}
return
}
// 默认 运行 Run
err = s.Run()
if err != nil {
fmt.Printf("service Control 222 failed, err: %v\n", err)
os.Exit(1)
}
}
通过go run main.go
得到如下结果,注意:程序并不会终止,而是阻塞住了
。
service.Interactive()----> true
coming Start.......
coming run.......
实际上,kardianos/service
为我们提供了下面的参数使用,我们可以通过go build -o main main.go
编译得到二进制文件,然后使用下面的命令来运行服务。
# 生成开机自启服务所需要的文件,文件位置根据操作系统的不同而不用,linux在 /etc/systemd/system 或者 /lib/systemd/system 下
./main install
# 删除上面生成的文件
./main uninstall
# 开启服务
./main start
# 重启服务
./main restart
# 停止服务
./main stop
2.2 kardianos/service 如何做开机自启服务
接下来以 Linux 为例,进行讲解。其他系统大家可自行尝试。
具体的步骤如下:
1、第一步是编写代码,编写完成后,编译成二进制文件。
代码就以 2.1 中的为例。首先编译成二进制文件。
go build -o main main.go
2、运行 可执行文件。
# 这将在 /etc/systemd/system 或者 /lib/systemd/system 中生成 custom-service.service 文件
# 我这里测试的时候是在 /etc/systemd/system 中生成的
./main install
看到这里,用过systemd的朋友应该可以猜到 kardianos/service 背后是通过什么来实现开机自启的。就是通过
systemd
来管理的。
3、将 custom-service.service 服务设置为开机自启.
运行下面命令将我们编写的程序设置为开机自启服务。
# 设置服务开机自启动
systemctl enable test-service.service
# 启动
systemctl start test-service.service
下面是systemctl
常用的命令。
# 启动
systemctl start test-service.service
# 停止
systemctl stop test-service.service
# 设置服务开机自启动
systemctl enable test-service.service
# 查询是否自启动服务
systemctl is-enabled test-service.service
# 取消服务器开机自启动
systemctl disable test-service.service
# 列出正在运行的服务
systemctl list-units --type=service
接下来我们看看服务管理器是什么意思?
1、./main install
3、执行systemctl start custom-service.service
后,查看服务运行过程
这里就是上面
服务管理器
的作用,也就是说,如何服务是手动运行的,那么service.Interactive()
返回 true,比如:./main start。如何是系统管理器运行的,则返回 false,比如:systemctl start custom-service.service。
2.3 结合 cli 使用
通过上面的例子,我们大概知道了如何使用 github.com/kardianos/service 。实际使用中,一般的服务都可以通过-h
来查看帮助文档,但是我们我们通过./main -h
会报错,所以需要完善下代码,使我们的程序更容易使用。下面,我们一起看看,借助 github.com/urfave/cli/v2 来完成上面的需求。
package main
import (
"fmt"
"github.com/kardianos/service"
"github.com/urfave/cli/v2"
"os"
)
type SystemService struct {}
func (ss *SystemService) Start(s service.Service) error {
fmt.Println("coming Start.......")
go ss.run()
return nil
}
func (ss *SystemService) run() {
fmt.Println("coming run.......")
}
func (ss *SystemService) Stop(s service.Service) error {
fmt.Println("coming Stop.......")
return nil
}
func main() {
app := cli.NewApp()
app.Name = "custom-service"
app.Usage = "how to use custom service"
app.Commands = []*cli.Command{
{
Name: "install",
Action: ctrlAction,
},
{
Name: "uninstall",
Action: ctrlAction,
},
{
Name: "start",
Action: ctrlAction,
},
{
Name: "restart",
Action: ctrlAction,
},
{
Name: "stop",
Action: ctrlAction,
},
}
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "install",
Value: "install",
Usage: "Write the files required for startup",
},
&cli.StringFlag{
Name: "uninstall",
Value: "uninstall",
Usage: "Delete startup files",
},
&cli.StringFlag{
Name: "start",
Value: "start",
Usage: "start the service",
},
&cli.StringFlag{
Name: "stop",
Value: "stop",
Usage: "stop the service",
},
&cli.StringFlag{
Name: "restart",
Value: "restart",
Usage: "restart the service",
},
}
app.Action = startAction
app.Run(os.Args)
}
func createSystemService() (service.Service, error) {
fmt.Println("service.Interactive()---->", service.Interactive())
svcConfig := &service.Config{
Name: "custom-service",
DisplayName: "custom service",
Description: "this is github.com/kardianos/service test case",
}
ss := &SystemService{}
s, err := service.New(ss, svcConfig)
if err != nil {
return nil, fmt.Errorf("service New failed, err: %v\n", err)
}
return s, nil
}
func ctrlAction(c *cli.Context) error {
s, err := createSystemService()
if err != nil {
fmt.Printf("createSystemService failed, err: %v\n", err)
return err
}
err = service.Control(s, c.Command.Name)
if err != nil {
fmt.Printf("service Run 222 failed, err: %v\n", err)
return err
}
return nil
}
func startAction(c *cli.Context) error {
s, err := createSystemService()
if err != nil {
fmt.Printf("createSystemService failed, err: %v\n", err)
return err
}
// 默认 运行 Run
err = s.Run()
if err != nil {
fmt.Printf("service Run failed, err: %v\n", err)
return err
}
return nil
}
大家可以根据自己的需求进行开发,这里只是讲一个简单的案例而已。
go build -o main main.go
./main -h
NAME:
custom-service - how to use custom service
USAGE:
custom-service [global options] command [command options] [arguments...]
COMMANDS:
install
uninstall
start
restart
stop
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--install value Write the files required for startup (default: "install")
--uninstall value Delete startup files (default: "uninstall")
--start value start the service (default: "start")
--stop value stop the service (default: "stop")
--restart value restart the service (default: "restart")
--help, -h show help
3、浅谈 service 的执行过程
这里以 mac 为例,通过 goland 来查看调用的链路方便些。
使用 github.com/kardianos/service 的步骤大概是这样的:
1、定义 service.Config
2、通过 service.New 创建 service
3、通过 service.Control 来运行上面 生成好的 service。
第一步没啥好说的,注意 service.Config 中的 Name 是必须的,且生成的开机自启文件名就是以他命名的。
重点看看第二步,service.New
源码入下:
// New creates a new service based on a service interface and configuration.
func New(i Interface, c *Config) (Service, error) {
//这就是为啥 Name 是必填的原因
if len(c.Name) == 0 {
return nil, ErrNameFieldRequired
}
// 注意看这里,system 在使用到时候就已经初始化了,但是我们使用的时候,并没有做初始化 system 的动作。
// 那么什么时候初始化的 system 呢?
// 这个时候就会想到 init() 这个函数,这个函数在 import 时就会自动运行。
if system == nil {
return nil, ErrNoServiceSystemDetected
}
return system.New(i, c)
}
System 接口
如下:
var (
system System
systemRegistry []System
)
// System represents the service manager that is available.
type System interface {
// String returns a description of the system.
String() string
// Detect returns true if the system is available to use.
Detect() bool
// Interactive returns false if running under the system service manager
// and true otherwise.
Interactive() bool
// New creates a new service for this system.
New(i Interface, c *Config) (Service, error)
}
以 mac 的系统为例, 讲讲system.New(i, c)
type darwinSystem struct{}
func (darwinSystem) String() string {
return version
}
func (darwinSystem) Detect() bool {
return true
}
func (darwinSystem) Interactive() bool {
return interactive
}
func (darwinSystem) New(i Interface, c *Config) (Service, error) {
s := &darwinLaunchdService{
i: i,
Config: c,
userService: c.Option.bool(optionUserService, optionUserServiceDefault),
}
return s, nil
}
func init() {
//这里就是给 system 变量赋值
//这里赋值有点不同,是在编译阶段由编译器根据系统的不同,初始化不同的 结构体。
//这个我也不敢确定,希望知道的朋友不吝赐教,感谢感谢!
ChooseSystem(darwinSystem{})
}
// ChooseSystem chooses a system from the given system services.
// SystemServices are considered in the order they are suggested.
// Calling this may change what Interactive and Platform return.
func ChooseSystem(a ...System) {
systemRegistry = a
system = newSystem()
}
darwinLaunchdService
实现了 Service interface
定义的方法。这里就不复制源代码了,有兴趣可以看看源代码。
以上就是我对 github.com/kardianos/service 的理解,有不对的地方,请不吝赐教。谢谢!
__EOF__
Recommend
-
37
-
36
想给投资生涯画个句号了!诚心请大家不吝赐教! - 接触投资十一年了,刚开始的时候,初生牛犊不怕虎,信心满满,各种操作模式都赶上,后来越来越保守,主要操作白马股,也累积赚了不少钱,但2018年以来的各种黑天鹅事件,对我的打击实在太大,睡不好,经常头痛,头...
-
35
画个长方形也不容易呢
-
32
-
12
README.md service
-
19
一、本作介绍和准备 1. 效果图 如下图,通过 Flutter 的 Canvas 绘制如下的 静态 表面。 本文知识点
-
4
对话:给未来 CIO 画个像 2021-06-07
-
5
文章持续更新,可以关注公众号程序猿阿朗或访问
-
6
iQOO 10 Pro上手:挑战用200W充电的时间 画个都市丽人妆
-
3
1、属性描述符的基础介绍 1.1 何为属性描述符? 属性描述符是一种Python语言中的特殊对象,用于定义和控制类属性的行为。属性描述符可以通过定义__get__、__set__、__delete__方...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK