service

package module
v1.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2025 License: Zlib Imports: 19 Imported by: 0

README

service GoDoc Coverage

跨平台 Go 服务(守护进程)框架,统一 Windows、Linux(systemd/Upstart/SysV/OpenRC)、macOS launchd 的安装、启动、停止和运行接口,可自动识别交互/服务模式。

特性

  • 简化 API:Runner 接口和 ServiceRunner 辅助类,消除 goroutine/channel 样板代码,代码量减少约 80%。框架自动将 Runner 接口实现适配为 ServiceRunner,无需手动包装。
  • StructuredDeps 跨平台依赖管理(after/before/require/want 自动映射,各平台自动降级或告警)。
  • TimeoutConfig 启动 / 停止 / 重启超时统一配置,未设置时采用平台默认值。
  • 重启策略配置:systemd Restart/StartLimit,Windows OnFailure/Delay,保留历史 Restart 选项兼容旧版模板。
  • 生命周期钩子:systemd ExecStartPre/ExecStartPost/ExecStopPost,upstart pre-start/post-stop。
  • 日志配置样板:一行生成 logrotate、systemd journald、Windows EventLog 配置,详见 LOGGING.md。
  • 支持安装/卸载/启动/停止/状态查询,保持 API 一致性,避免平台分支。

安装

  • Go 模块(需要 Go 1.23+):go get github.com/darkit/daemon@latest
  • 运行内置示例:go run ./example/simple

快速开始

方式一:使用 Runner 接口(推荐)

实现 Runner 接口的类型会被框架自动包装成 ServiceRunner,无需手动创建。这是最简洁的方式,适合大多数场景。

package main

import (
    "context"
    "log"
    "time"

    "github.com/darkit/daemon"
)

type myService struct{}

func (m *myService) Run(ctx context.Context) error {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            log.Println("服务运行中...")
        case <-ctx.Done():
            return nil
        }
    }
}

func main() {
    cfg := &service.Config{
        Name:        "demo",
        DisplayName: "Demo Service",
        Description: "跨平台 demo 服务",
    }

    svc, err := service.New(&myService{}, cfg)
    if err != nil {
        log.Fatal(err)
    }

    if err := svc.Run(); err != nil {
        log.Fatal(err)
    }
}
方式二:使用 ServiceRunner 辅助类

直接使用 NewServiceRunner 创建函数式服务,无需定义类型。适合快速开发和原型验证。

package main

import (
    "context"
    "log"
    "time"

    "github.com/darkit/daemon"
)

func main() {
    runner := service.NewServiceRunner(func(ctx context.Context) error {
        ticker := time.NewTicker(time.Second)
        defer ticker.Stop()
        for {
            select {
            case <-ticker.C:
                log.Println("服务运行中...")
            case <-ctx.Done():
                return nil
            }
        }
    })

    cfg := &service.Config{
        Name:        "demo",
        DisplayName: "Demo Service",
        Description: "跨平台 demo 服务",
    }

    svc, err := service.New(runner, cfg)
    if err != nil {
        log.Fatal(err)
    }

    if err := svc.Run(); err != nil {
        log.Fatal(err)
    }
}
方式三:传统方式(向后兼容)

传统的 Interface 方式,需要手动管理 goroutine 和退出信号。保留用于遗留代码或需要精细控制启动/停止逻辑的场景。

package main

import (
    "log"
    "time"

    "github.com/darkit/daemon"
)

type program struct{ quit chan struct{} }

func (p *program) Start(s service.Service) error {
    p.quit = make(chan struct{})
    go func() {
        for {
            select {
            case <-p.quit:
                return
            default:
                time.Sleep(time.Second)
            }
        }
    }()
    return nil
}

func (p *program) Stop(service.Service) error {
    close(p.quit)
    return nil
}

func main() {
    cfg := &service.Config{
        Name:        "demo",
        DisplayName: "Demo Service",
        Description: "跨平台 demo 服务",
    }

    svc, err := service.New(&program{}, cfg)
    if err != nil {
        log.Fatal(err)
    }

    if err := svc.Run(); err != nil {
        log.Fatal(err)
    }
}

API 对比

以下表格对比三种实现方式的特点:

对比维度 Runner 接口 ServiceRunner 函数式 Interface 传统方式
代码量 ~10 行 ~5 行 ~30 行
学习曲线 低(仅需理解 context) 极低(闭包即可) 中(需手动管理生命周期)
goroutine 管理 自动 自动 手动
context 传播 自动 自动 手动
panic 恢复 自动 自动 手动
超时控制 支持 支持(可配置) 手动实现
适用场景 大多数新项目 快速原型、简单服务 遗留代码、复杂启动逻辑
向后兼容
典型迁移示例

传统方式(~30 行):

type program struct{ quit chan struct{} }

func (p *program) Start(s service.Service) error {
    p.quit = make(chan struct{})
    go func() {
        ticker := time.NewTicker(time.Second)
        defer ticker.Stop()
        for {
            select {
            case <-ticker.C:
                log.Println("运行中...")
            case <-p.quit:
                return
            }
        }
    }()
    return nil
}

func (p *program) Stop(service.Service) error {
    close(p.quit)
    time.Sleep(100 * time.Millisecond) // 等待 goroutine 退出
    return nil
}

Runner 方式(~10 行):

type program struct{}

func (p *program) Run(ctx context.Context) error {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            log.Println("运行中...")
        case <-ctx.Done():
            return nil
        }
    }
}

ServiceRunner 方式(~5 行):

runner := service.NewServiceRunner(func(ctx context.Context) error {
    // 业务逻辑
    <-ctx.Done()
    return nil
})
选择指南
  • 新项目:优先使用 Runner 接口,简洁且标准
  • 快速开发:使用 ServiceRunner,无需定义类型
  • 自定义启动:使用 ServiceRunner.StartFunc/StopFunc
  • 超时需求:配置 ServiceRunner.StartTimeout/StopTimeout
  • 遗留代码:保持 Interface 方式,或渐进式迁移

平台能力矩阵

说明:✅ 完整支持;◑ 部分支持;⏳ 实验性;— 暂不支持。

能力/平台 Windows Linux systemd Linux Upstart Linux SysV Linux OpenRC macOS launchd
StructuredDeps ◑(after/require/want,before 忽略并告警) ◑(after/require) ⏳(after/require 软等待脚本)
TimeoutConfig ✅(Start/Stop) ✅(Start/Stop/RestartSec) ◑(Restart 间隔)
重启策略配置 ✅(OnFailure/Delay) ✅(Restart/StartLimit) ◑(respawn 策略) ◑(LSB retry) ◑(respawn/rc-service)
生命周期钩子 ✅(ExecStartPre/Post、ExecStopPost) ✅(pre-start/post-stop)
日志配置样板 ✅(EventLog 脚本) ✅(journald + logrotate) ✅(logrotate) ✅(logrotate)

新 API 示例

以下示例均复用最小实现:

package main

import "github.com/darkit/daemon"

type program struct{}

func (p *program) Start(service.Service) error { return nil }
func (p *program) Stop(service.Service) error  { return nil }
StructuredDeps 结构化依赖
package main

import "github.com/darkit/daemon"

func main() {
    cfg := &service.Config{
        Name: "myapp",
        StructuredDeps: []service.Dependency{
            {Name: "network.target", Type: service.DependencyAfter},
            {Name: "postgresql", Type: service.DependencyRequire},
            {Name: "logger", Type: service.DependencyBefore},
        },
    }
    svc, _ := service.New(&program{}, cfg)
    _ = svc.Run()
}
TimeoutConfig 超时配置
package main

import (
    "time"

    "github.com/darkit/daemon"
)

func main() {
    cfg := &service.Config{
        Name: "myapp",
        Timeout: service.TimeoutConfig{
            Start:   10 * time.Second,
            Stop:    20 * time.Second,
            Restart: 5 * time.Second,
        },
    }
    svc, _ := service.New(&program{}, cfg)
    _ = svc.Run()
}
生命周期钩子(systemd 示例)
package main

import "github.com/darkit/daemon"

func main() {
    cfg := &service.Config{
        Name: "myapp",
        Option: service.KeyValue{
            service.OptionExecStartPre:  []string{"/usr/local/bin/prepare.sh"},
            service.OptionExecStartPost: "echo started",
            service.OptionExecStopPost:  "rm -f /var/run/myapp.pid",
            service.OptionOnFailureUnit: []string{"alert@%n.service"},
        },
    }
    svc, _ := service.New(&program{}, cfg)
    _ = svc.Run()
}
日志配置样板
// 生成 logrotate、journald、Windows EventLog 配置文件内容
files := map[string]string{
    "logrotate.conf":       service.GenerateLogrotateConfig("myapp", "/var/log/myapp.log"),
    "journald.conf":        service.GenerateSystemdJournalConfig("myapp"),
    "windows-eventlog.ps1": service.GenerateWindowsEventLogConfig("Application", "MyApp"),
}

更多细节见 LOGGING.md

BUGS

  • Dependencies 字段在 Linux 系统与 launchd 上未完整实现(建议改用 StructuredDeps)。
  • OS X 以 UserService Interactive 运行时状态检测仍不准确。

Documentation

Overview

Package service 提供跨平台的服务(守护进程)管理框架。 支持 Windows、Linux(systemd/Upstart/SysV/OpenRC)、macOS launchd,自动检测交互/服务模式。

基本用法(Runner 接口,推荐):

// 注意:New() 现在仅接受 Interface 类型,Runner 请使用 NewFromRunner。
type myService struct{}

func (m *myService) Run(ctx context.Context) error {
    <-ctx.Done()
    return nil
}

func main() {
    cfg := &service.Config{Name: "myapp"}
    svc, _ := service.NewFromRunner(&myService{}, cfg)
    _ = svc.Run()
}

使用 NewFromRunner(Runner 接口,推荐):

type myService struct{}
func (m *myService) Run(ctx context.Context) error {
    <-ctx.Done()
    return nil
}

func main() {
    cfg := &service.Config{Name: "myapp"}
    svc, _ := service.NewFromRunner(&myService{}, cfg)
    _ = svc.Run()
}

函数式用法(ServiceRunner):

runner := service.NewServiceRunner(func(ctx context.Context) error {
    <-ctx.Done()
    return nil
})
svc, _ := service.New(runner, cfg)
_ = svc.Run()

传统用法(Interface,向后兼容):

type program struct{}
func (p *program) Start(s service.Service) error { go p.run(); return nil }
func (p *program) run()                          { /* 业务逻辑 */ }
func (p *program) Stop(s service.Service) error  { return nil }

简化用法说明:

Runner 接口消除了手动管理 goroutine、channel 和 context 的样板代码。 与传统 Interface 相比,减少约 80% 的代码量,同时保持相同功能。 NewFromRunner 会自动将 Runner 包装为 ServiceRunner 并调用 New 创建 Service。

三种使用方式对比:

1. Runner 接口(推荐):实现 Run(ctx context.Context) error 方法

  • 使用 NewFromRunner,最简洁,框架自动管理生命周期
  • 适合大多数场景

2. ServiceRunner 函数式(快速原型):传入 func(ctx context.Context) error

  • 使用 NewServiceRunner 创建,再用 New 构建 Service
  • 无需定义类型,适合快速开发,支持自定义启动/停止逻辑

3. Interface 传统方式(向后兼容):实现 Start/Stop 方法

  • 使用 New,需手动管理 goroutine 和退出信号
  • 保留用于遗留代码

迁移指南(从传统 Interface 到 Runner):

传统方式(约 30 行):

type program struct {
    quit chan struct{}
}

func (p *program) Start(s service.Service) error {
    p.quit = make(chan struct{})
    go p.run()
    return nil
}

func (p *program) run() {
    for {
        select {
        case <-p.quit:
            return
        default:
            // 业务逻辑
        }
    }
}

func (p *program) Stop(s service.Service) error {
    close(p.quit)
    return nil
}

简化为 Runner 方式(约 10 行):

type program struct{}

func (p *program) Run(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return nil
        default:
            // 业务逻辑
        }
    }
}

或使用 ServiceRunner(约 5 行):

runner := service.NewServiceRunner(func(ctx context.Context) error {
    <-ctx.Done()
    return nil
})

何时选择哪种方式:

  • 新项目:优先使用 Runner 接口或 ServiceRunner
  • 需要自定义启动/停止逻辑:使用 ServiceRunner.StartFunc/StopFunc
  • 需要超时控制:设置 ServiceRunner.StartTimeout/StopTimeout
  • 遗留代码:保持 Interface 方式,或渐进式迁移到 Runner

Windows controls services by setting up callbacks that is non-trivial. This is very different than 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.

高级示例(结构化依赖、超时及跨平台日志配置):

import "time"

func buildConfig() *service.Config {
	return &service.Config{
		Name: "myapp",
		StructuredDeps: []service.Dependency{
			{Name: "network.target", Type: service.DependencyAfter}, // systemd/launchd 会等待网络
			{Name: "postgresql", Type: service.DependencyRequire},   // Windows/Systemd 映射为硬依赖
		},
		Timeout: service.TimeoutConfig{ // 0 使用各平台默认
			Start:   30 * time.Second,
			Stop:    20 * time.Second,
			Restart: 5 * time.Second,
		},
	}
}

Linux 下可使用 GenerateLogrotateConfig 写入 /etc/logrotate.d/myapp, systemd 可将 GenerateSystemdJournalConfig 输出放到 /etc/systemd/journald.conf.d/, Windows 则可用 GenerateWindowsEventLogConfig 输出的 PowerShell 注册事件源。 平台未使用的配置安全忽略。

Example (RunnerBasic)

Example_runnerBasic 展示使用 NewServiceRunner 快速构建服务。

package main

import (
	"context"
	"fmt"
	"time"

	service "github.com/darkit/daemon"
)

func main() {
	runner := service.NewServiceRunner(func(ctx context.Context) error {
		ticker := time.NewTicker(100 * time.Millisecond)
		defer ticker.Stop()

		for i := 0; i < 3; i++ {
			select {
			case <-ticker.C:
				fmt.Printf("tick %d\n", i+1)
			case <-ctx.Done():
				return nil
			}
		}
		return nil
	})

	// 直接调用 RunFunc 模拟运行。
	_ = runner.RunFunc(context.Background())

}
Output:

tick 1
tick 2
tick 3
Example (RunnerInterface)

Example_runnerInterface 展示实现 Runner 接口。

package main

import (
	"context"
	"fmt"
	"time"

	service "github.com/darkit/daemon"
)

type exampleRunner struct{}

func (exampleRunner) Run(ctx context.Context) error {
	for i := 0; i < 3; i++ {
		fmt.Printf("working %d\n", i+1)
		time.Sleep(10 * time.Millisecond)
	}
	return nil
}

func main() {
	var svc service.Runner = &exampleRunner{}
	_ = svc.Run(context.Background())

}
Output:

working 1
working 2
working 3
Example (RunnerWithTimeout)

Example_runnerWithTimeout 展示自定义超时配置。

package main

import (
	"context"
	"fmt"
	"time"

	service "github.com/darkit/daemon"
)

func main() {
	runner := service.NewServiceRunner(func(ctx context.Context) error {
		<-ctx.Done()
		return ctx.Err()
	})

	// 自定义超时
	runner.StartTimeout = 5 * time.Second
	runner.StopTimeout = 3 * time.Second

	fmt.Println("配置完成")

}
Output:

配置完成

Index

Examples

Constants

View Source
const (
	// OptionRestart systemd:控制重启策略,对应 unit Restart=。
	OptionRestart = optionRestart
	// OptionRestartSec systemd:重启前延迟秒数,对应 RestartSec=。
	OptionRestartSec = optionRestartSec
	// OptionStartLimitBurst systemd:限流窗口内允许的最大重启次数,对应 StartLimitBurst=。
	OptionStartLimitBurst = "StartLimitBurst"
	// OptionStartLimitIntervalSec systemd:重启限流窗口长度(秒),对应 StartLimitIntervalSec=。
	OptionStartLimitIntervalSec = "StartLimitIntervalSec"
	// OptionExecStartPre systemd:主进程启动前执行的命令钩子,对应 Service.ExecStartPre=。
	OptionExecStartPre = optionExecStartPre
	// OptionExecStartPost systemd:主进程启动后执行的命令钩子,对应 Service.ExecStartPost=。
	OptionExecStartPost = optionExecStartPost
	// OptionExecStopPost systemd:停止完成后的清理钩子,对应 Service.ExecStopPost=。
	OptionExecStopPost = optionExecStopPost
	// OptionOnFailureUnit systemd:失败时要激活的 unit(Unit.OnFailure),与 Windows 的 OptionOnFailure(SCM 重启/重启机器/无动作恢复策略)语义不同。
	OptionOnFailureUnit = optionOnFailureUnit
	// OptionOnFailure Windows:服务失败后的恢复动作。
	OptionOnFailure = "OnFailure"
	// OptionOnFailureDelay Windows:服务失败后再次尝试前的延迟。
	OptionOnFailureDelay = "OnFailureDelay"
	// OptionPreStartScript upstart:服务启动前执行的脚本,对应 pre-start script。
	OptionPreStartScript = optionPreStartScript
	// OptionPostStopScript upstart:服务停止后执行的脚本,对应 post-stop script。
	OptionPostStopScript = optionPostStopScript
)

Option* 常量提供跨平台 Option Key,便于调用方避免硬编码字符串。

View Source
const (
	ControlActionStart     = "start"
	ControlActionStop      = "stop"
	ControlActionRestart   = "restart"
	ControlActionInstall   = "install"
	ControlActionUninstall = "uninstall"
)

ControlAction list valid string texts to use in Control.

Variables

View Source
var (
	// ErrNameFieldRequired is returned when Config.Name is empty.
	ErrNameFieldRequired = errors.New("Config.Name field is required.") //nolint:revive
	// ErrNoServiceSystemDetected is returned when no system was detected.
	ErrNoServiceSystemDetected = errors.New("No service system detected.") //nolint:revive
	// ErrNotInstalled is returned when the service is not installed.
	ErrNotInstalled = errors.New("the service is not installed")
)
View Source
var ConsoleLogger = consoleLogger{} //nolint:gochecknoglobals

ConsoleLogger logs to the std err.

Functions

func ChooseSystem

func ChooseSystem(a ...System)

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 Control

func Control(s Service, action string) error

Control issues control functions to the service from a given action string.

func GenerateLogrotateConfig

func GenerateLogrotateConfig(name, logPath string) string

GenerateLogrotateConfig 返回 logrotate 配置片段,可直接写入 /etc/logrotate.d/<name>。 参数 name 用于推导默认日志路径(/var/log/<name>.log),logPath 留空时采用默认;仅 Linux/Unix 使用,其他平台可安全忽略。

func GenerateSystemdJournalConfig

func GenerateSystemdJournalConfig(name string) string

GenerateSystemdJournalConfig 生成 systemd journald 的 drop-in 片段,放置于 /etc/systemd/journald.conf.d/<name>.conf。 参数 name 作为日志目录名(默认 "service"),仅 systemd 上生效,非 systemd 平台调用后输出可忽略。

func GenerateWindowsEventLogConfig

func GenerateWindowsEventLogConfig(name, source string) string

GenerateWindowsEventLogConfig 返回注册 Windows EventLog 源的 PowerShell 示例。 参数 name 为日志名称(默认 Application),source 为空时回退为 name;仅 Windows 有意义。

func Interactive

func Interactive() bool

Interactive returns false if running under the OS service manager and true otherwise.

func Platform

func Platform() string

Platform returns a description of the system service.

Types

type Config

type Config struct {
	Name        string   // Required name of the service. No spaces suggested.
	DisplayName string   // Display name, spaces allowed.
	Description string   // Long description of service.
	UserName    string   // Run as username.
	Arguments   []string // Run with arguments.

	// Optional field to specify the executable for service.
	// If empty the current executable is used.
	Executable string

	// Array of service dependencies.
	// Not yet fully implemented on Linux or OS X:
	//  1. Support linux-systemd dependencies, just put each full line as the
	//     element of the string array, such as
	//     "After=network.target syslog.target"
	//     "Requires=syslog.target"
	//     Note, such lines will be directly appended into the [Unit] of
	//     the generated service config file, will not check their correctness.
	Dependencies []string

	// StructuredDeps 使用 Dependency 定义跨平台依赖,优先于 Dependencies;空 Type 视为 DependencyAfter,
	// Windows/systemd/OpenRC 完整支持,sysv/upstart/launchd 会忽略不支持的类型。
	StructuredDeps []Dependency

	// The following fields are not supported on Windows.
	WorkingDirectory string // Initial working directory.
	ChRoot           string

	// System specific options.
	Option KeyValue

	EnvVars map[string]string

	// Timeout 配置启动、停止、重启的超时;0 使用平台默认,Restart 仅 systemd/upstart 生效。
	Timeout TimeoutConfig

	// AllowSudoFallback 仅在缺少 start-stop-daemon 等降权能力时使用 su,默认 false 保持历史行为。
	AllowSudoFallback bool
}

Config provides the setup for a Service. The Name field is required.

func (*Config) GroupDependenciesByType

func (c *Config) GroupDependenciesByType() map[string][]string

GroupDependenciesByType 按依赖类型分组(字符串键),模板使用。

type ControlsHandler

type ControlsHandler struct{}

ControlsHandler is a stub of windows specific helper for common code.

type Dependency

type Dependency struct {
	Name string         // 依赖服务名称
	Type DependencyType // 依赖类型
}

Dependency 定义结构化依赖项,Name 为目标服务名,Type 控制顺序/强度。

type DependencyType

type DependencyType string

StructuredDeps 提供跨平台的结构化依赖管理,优先于字符串形式的 Dependencies。

优先级:StructuredDeps > Dependencies。

平台支持:

  • Windows: 完整支持(映射到 SCM Dependencies)。
  • systemd: 完整支持(After/Before/Requires/Wants)。
  • OpenRC: 完整支持(need/use/before)。
  • upstart: 部分支持(start on)。
  • sysv: 部分支持(LSB 标签)。
  • launchd: 实验性(轮询等待)。

示例:

cfg := &service.Config{
    Name: "myapp",
    StructuredDeps: []service.Dependency{
        {Name: "network.target", Type: service.DependencyAfter},
        {Name: "postgresql", Type: service.DependencyRequire},
    },
}

DependencyType 描述依赖关系在不同平台上的语义映射,缺省值视为 DependencyAfter。

const (
	// DependencyAfter 表示软顺序依赖,常映射为 systemd/launchd After 或 Windows 服务依赖。
	DependencyAfter DependencyType = "after"
	// DependencyBefore 表示当前服务需在目标服务之前启动,映射为 systemd Before。
	DependencyBefore DependencyType = "before"
	// DependencyRequire 表示硬依赖,目标服务不可用时当前服务启动失败,映射为 systemd Requires 或 Windows SCM 依赖。
	DependencyRequire DependencyType = "require"
	// DependencyWant 表示推荐依赖,目标缺失时继续启动,映射为 systemd Wants。
	DependencyWant DependencyType = "want"
)

type Interface

type Interface interface {
	// Start provides a place to initiate the service. The service doesn't
	// signal a completed start until after this function returns, so the
	// Start function must not take more than a few seconds at most.
	Start(s Service) error

	// Stop provides a place to clean up program execution before it is terminated.
	// It should not take more than a few seconds to execute.
	// Stop should not call os.Exit directly in the function.
	Stop(s Service) error
}

Interface represents the service interface for a program. Start runs before the hosting process is granted control and Stop runs when control is returned.

  1. OS service manager executes user program.
  2. User program sees it is executed from a service manager (IsInteractive is false).
  3. User program calls Service.Run() which blocks.
  4. Interface.Start() is called and quickly returns.
  5. User program runs.
  6. OS service manager signals the user program to stop.
  7. Interface.Stop() is called and quickly returns. - For a successful exit, os.Exit should not be called in Interface.Stop().
  8. Service.Run returns.
  9. User program should quickly exit.

type KeyValue

type KeyValue map[string]interface{}

KeyValue provides a list of system specific options.

  • OS X

  • LaunchdConfig string () - Use custom launchd config.

  • KeepAlive bool (true) - Prevent the system from stopping the service automatically.

  • RunAtLoad bool (false) - Run the service after its job has been loaded.

  • SessionCreate bool (false) - Create a full user session.

  • Solaris

  • Prefix string ("application") - Service FMRI prefix.

  • POSIX

  • UserService bool (false) - Install as a current user service.

  • SystemdScript string () - Use custom systemd script.

  • UpstartScript string () - Use custom upstart script.

  • SysvScript string () - Use custom sysv script.

  • OpenRCScript string () - Use custom OpenRC script.

  • RunWait func() (wait for SIGNAL) - Do not install signal but wait for this function to return.

  • 自定义函数内部使用 signal.NotifyContext 时,必须调用 defer stop() 清理资源

  • 示例: func customWait() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop() <-ctx.Done() }

  • ReloadSignal string () [USR1, ...] - Signal to send on reload.

  • PIDFile string () [/run/prog.pid] - Location of the PID file.

  • LogOutput bool (false) - Redirect StdErr & StandardOutPath to files.

  • Restart string (always) - How shall service be restarted.

  • SuccessExitStatus string () - The list of exit status that shall be considered as successful, in addition to the default ones.

  • LogDirectory string(/var/log) - The path to the log files directory

  • Linux (systemd)

  • LimitNOFILE int (-1) - Maximum open files (ulimit -n) (https://serverfault.com/questions/628610/increasing-nproc-for-processes-launched-by-systemd-on-centos-7)

  • Windows

  • DelayedAutoStart bool (false) - After booting, start this service after some delay.

  • Password string () - Password to use when interfacing with the system service manager.

  • Interactive bool (false) - The service can interact with the desktop. (more information https://docs.microsoft.com/en-us/windows/win32/services/interactive-services)

  • DelayedAutoStart bool (false) - after booting start this service after some delay.

  • StartType string ("automatic") - Start service type. (automatic | manual | disabled)

  • OnFailure string ("restart" ) - Action to perform on service failure. (restart | reboot | noaction)

  • OnFailureDelayDuration string ( "1s" ) - Delay before restarting the service, time.Duration string.

  • OnFailureResetPeriod int ( 10 ) - Reset period for errors, seconds.

  • PIDFile string () [/run/prog.pid] - Location of the PID file.

  • LogOutput bool (false) - Redirect StdErr & StandardOutPath to files.

  • Restart string (always) - How shall service be restarted.

  • SuccessExitStatus string () - The list of exit status that shall be considered as successful, in addition to the default ones.

  • LogDirectory string(/var/log) - The path to the log files directory

* Linux (systemd)

* Windows

  • DelayedAutoStart bool (false) - After booting, start this service after some delay.
  • Password string () - Password to use when interfacing with the system service manager.
  • Interactive bool (false) - The service can interact with the desktop. (more information https://docs.microsoft.com/en-us/windows/win32/services/interactive-services)
  • DelayedAutoStart bool (false) - After booting start this service after some delay.
  • StartType string ("automatic") - Start service type. (automatic | manual | disabled)
  • OnFailure string ("restart" ) - Action to perform on service failure. (restart | reboot | noaction)
  • OnFailureDelayDuration string ( "1s" ) - Delay before restarting the service, time.Duration string.
  • OnFailureResetPeriod int ( 10 ) - Reset period for errors, seconds.
  • ExtraControls ControlsHandler (nil, nil) - Handle controls other than Stop and Shutdown.

type Logger

type Logger interface {
	Error(v ...interface{}) error
	Warning(v ...interface{}) error
	Info(v ...interface{}) error

	Errorf(format string, a ...interface{}) error
	Warningf(format string, a ...interface{}) error
	Infof(format string, a ...interface{}) error
}

Logger writes to the system log.

type Runner

type Runner interface {
	Run(ctx context.Context) error
}

Runner 接口定义了简化的服务运行模型。 实现此接口的类型可以直接传递给 service.New,框架会自动将其包装为 ServiceRunner, ServiceRunner 再实现 Interface 接口,完成完整的生命周期适配。

生命周期通过 context 管理:

  • Run 会在独立 goroutine 中执行
  • ctx 被取消时应立刻返回
  • 返回的 error 会被透传到 Service.Wait / Run

示例:

type MyService struct{}

func (s *MyService) Run(ctx context.Context) error {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            // 业务逻辑
        case <-ctx.Done():
            return ctx.Err()
        }
    }
}

type Service

type Service interface {
	// Run should be called shortly after the program entry point.
	// After Interface.Stop has finished running, Run will stop blocking.
	// After Run stops blocking, the program must exit shortly after.
	Run() error

	// Start signals to the OS service manager the given service should start.
	Start() error

	// Stop signals to the OS service manager the given service should stop.
	Stop() error

	// Restart signals to the OS service manager the given service should stop then start.
	Restart() error

	// Install setups up the given service in the OS service manager. This may require
	// greater rights. Will return an error if it is already installed.
	Install() error

	// Uninstall removes the given service from the OS service manager. This may require
	// greater rights. Will return an error if the service is not present.
	Uninstall() error

	// Opens and returns a system logger. If the user program is running
	// interactively rather than as a service, the returned logger will write to
	// os.Stderr. If errs is non-nil errors will be sent on errs as well as
	// returned from Logger's functions.
	Logger(errs chan<- error) (Logger, error)

	// SystemLogger opens and returns a system logger. If errs is non-nil errors
	// will be sent on errs as well as returned from Logger's functions.
	SystemLogger(errs chan<- error) (Logger, error)

	// String displays the name of the service. The display name if present,
	// otherwise the name.
	String() string

	// Platform displays the name of the system that manages the service.
	// In most cases this will be the same as service.Platform().
	Platform() string

	// Status returns the current service status.
	Status() (Status, error)
}

Service represents a service that can be run or controlled.

func New

func New(i Interface, c *Config) (Service, error)

New creates a new service based on a service interface and configuration.

func NewFromRunner

func NewFromRunner(r Runner, c *Config) (Service, error)

NewFromRunner 接收 Runner,自动包装为 ServiceRunner 后创建 Service。

type ServiceRunner

type ServiceRunner struct {
	// RunFunc 在独立 goroutine 中运行,当 ctx 取消时应退出。
	// 如果同时设置了 StartFunc,则 RunFunc 优先级更高。
	RunFunc func(ctx context.Context) error

	// StartFunc 在服务启动时执行,应快速返回(类似 Interface.Start)。
	// 通常用于初始化资源,实际工作在后台 goroutine 中进行。
	StartFunc func(ctx context.Context) error

	// StopFunc 在服务停止时执行,应快速清理资源(类似 Interface.Stop)。
	StopFunc func(ctx context.Context) error

	// StartTimeout 指定启动超时时间,默认 10 秒。
	// 超时后返回 context.DeadlineExceeded。
	StartTimeout time.Duration

	// StopTimeout 指定停止超时时间,默认 10 秒。
	// 超时后强制取消 context。
	StopTimeout time.Duration
	// contains filtered or unexported fields
}

ServiceRunner 提供函数式服务运行辅助,用于快速构建服务而无需定义完整类型。

支持两种使用模式:

  • 仅提供 RunFunc:在独立 goroutine 中运行,Stop 时取消 context
  • 提供 StartFunc + StopFunc:分别在启动和停止时执行自定义逻辑

超时配置:

  • StartTimeout:启动超时(默认 10 秒)
  • StopTimeout:停止超时(默认 10 秒)

示例 1(仅 RunFunc):

runner := &ServiceRunner{
    RunFunc: func(ctx context.Context) error {
        <-ctx.Done()
        return nil
    },
}
svc, _ := service.New(runner, cfg)
svc.Run()

示例 2(StartFunc + StopFunc):

runner := &ServiceRunner{
    StartFunc: func(ctx context.Context) error {
        // 初始化资源
        return nil
    },
    StopFunc: func(ctx context.Context) error {
        // 清理资源
        return nil
    },
}

func NewServiceRunner

func NewServiceRunner(runFunc func(ctx context.Context) error) *ServiceRunner

NewServiceRunner 创建一个新的 ServiceRunner 实例。

参数:

  • runFunc:主运行函数,在独立 goroutine 中执行

返回的 ServiceRunner 使用默认超时(启动和停止各 10 秒)。 可通过修改返回值的 StartTimeout 和 StopTimeout 字段自定义超时。

示例:

runner := service.NewServiceRunner(func(ctx context.Context) error {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            log.Println("tick")
        case <-ctx.Done():
            return nil
        }
    }
})
runner.StopTimeout = 5 * time.Second  // 自定义停止超时

func (*ServiceRunner) Start

func (r *ServiceRunner) Start(_ Service) error

Start 启动服务,实现 Interface.Start。 如果设置了 RunFunc,会在后台 goroutine 中运行;如果设置了 StartFunc,会在当前 goroutine 中执行。

func (*ServiceRunner) Stop

func (r *ServiceRunner) Stop(_ Service) error

Stop 停止服务,实现 Interface.Stop。 取消 context 并等待 RunFunc 退出或执行 StopFunc。

type Shutdowner

type Shutdowner interface {
	Interface
	// Shutdown provides a place to clean up program execution when the system is being shutdown.
	// It is essentially the same as Stop but for the case where machine is being shutdown/restarted
	// instead of just normally stopping the service. Stop won't be called when Shutdown is.
	Shutdown(s Service) error
}

Shutdowner represents a service interface for a program that differentiates between "stop" and "shutdown". A shutdown is triggered when the whole box (not just the service) is stopped.

type Status

type Status byte

Status represents service status as an byte value.

const (
	StatusUnknown Status = iota // Status is unable to be determined due to an error or it was not installed.
	StatusRunning
	StatusStopped
)

Status of service represented as an byte.

type System

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)
}

System represents the service manager that is available.

func AvailableSystems

func AvailableSystems() []System

AvailableSystems returns the list of system services considered when choosing the system service.

func ChosenSystem

func ChosenSystem() System

ChosenSystem returns the system that service will use.

type TimeoutConfig

type TimeoutConfig struct {
	Start   time.Duration // 启动超时时间(0=使用平台默认)
	Stop    time.Duration // 停止超时时间(0=使用平台默认)
	Restart time.Duration // 重启间隔时间(仅 systemd/upstart 使用,0=使用平台默认)
}

TimeoutConfig 定义服务启动、停止和重启的超时配置,0 值表示使用平台默认。 Start/Stop 适用于所有平台;Restart 仅 systemd/upstart 支持,用于控制重启窗口间隔。

Directories

Path Synopsis
example
advanced command
高级示例:展示 StructuredDeps、TimeoutConfig 与生命周期钩子用法。
高级示例:展示 StructuredDeps、TimeoutConfig 与生命周期钩子用法。
logging command
Simple service that only works by printing a log message every few seconds.
Simple service that only works by printing a log message every few seconds.
runner command
Simple service that only works by printing a log message every few seconds.
Simple service that only works by printing a log message every few seconds.
simple command
simple does nothing except block while running the service.
simple does nothing except block while running the service.
stopPause command
simple does nothing except block while running the service.
simple does nothing except block while running the service.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL