golang实现注册系统服务(Windows、Darwin)

2023-12-20 13:52

本文主要是介绍golang实现注册系统服务(Windows、Darwin),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

golang实现注册系统服务(Windows、Darwin)

仓库地址:https://github.com/ziyifast/yiSystemService

使用第三方包:go get “github.com/kardianos/service”
日志库:go get “github.com/sirupsen/logrus”

  • log “github.com/sirupsen/logrus”

1 初始化日志

util/log.go:

package utilimport (log "github.com/sirupsen/logrus""io""os"
)func InitLog(logPath string) {//设置输出样式,自带的只有两种样式logrus.JSONFormatter{}和logrus.TextFormatter{}log.SetFormatter(&log.TextFormatter{})log.SetOutput(os.Stdout)//设置output,默认为stderr,可以为任何io.Writer,比如文件*os.Filefile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)writers := []io.Writer{file,os.Stdout}//同时写文件和屏幕fileAndStdoutWriter := io.MultiWriter(writers...)if err == nil {log.SetOutput(fileAndStdoutWriter)} else {log.Info("failed to log to file.")}//设置最低loglevellog.SetLevel(log.InfoLevel)
}

2 导入三方Service库+编写serviceConfig(描述、可执行文件路径等)

2.1 consts

①consts/consts.go:

package constsimport "path"const (ServiceVersion     = "v1.0"ServiceName        = "yiService"ServicePort        = 9999ServiceDisplayName = "yi.service"ServiceDescription = "my test service"
)var LogPath stringfunc init() {LogPath = path.Join(WorkDir, "yiService.log")
}

②consts/consts_darwin.go:

//go:build darwin
// +build darwinpackage constsimport "path/filepath"var (StartupBash  = filepath.Join(WorkDir, "startup.sh")ShutdownBash = filepath.Join(WorkDir, "shutdown.sh")
)const (WorkDir = "/usr/local/yiService"MainExe = "yiService"
)    

③consts/consts_windows.go:

package constsimport "path/filepath"var (StartupBash  = filepath.Join(WorkDir, "startup.bat")ShutdownBash = filepath.Join(WorkDir, "shutdown.bat")
)const (WorkDir    = "c:/yiService"MainExe    = "yiService.exe"
)

2.2 system_service

system_service/system_service.go:

package system_serviceimport ("awesomeProject1/consts""fmt""github.com/kardianos/service"log "github.com/sirupsen/logrus""os""runtime"
)var (logger service.Loggersvc    service.Service
)func init() {svcConfig := &service.Config{Name:             consts.ServiceName,WorkingDirectory: consts.WorkDir,DisplayName:      consts.ServiceDisplayName,Description:      consts.ServiceDescription,Arguments:        []string{"service", "run"}, //服务注册成功之后,由服务去执行yiService.exe service run【运行服务】Executable:       fmt.Sprintf("%s/%s", consts.WorkDir, consts.ServiceName),Option:           service.KeyValue{},}if runtime.GOOS == "windows" {svcConfig.Executable = fmt.Sprintf("%s\\%s.exe", consts.WorkDir, consts.ServiceName)}var err errorprogram := &Program{}svc, err = service.New(program, svcConfig)if err != nil {log.Errorf("create service fail %v\n", err)return}errChan := make(chan error, 5)logger, err = svc.Logger(errChan)if err != nil {log.Errorf("%v\n", err)return}if err != nil {log.Errorf("%v\n", err)return}go func() {log.Info("watching err chan....")for {err := <-errChanif err != nil {log.Fatalf("service err %v", err)}}}()
}func StartSVC() {log.Infof("StartSVC...")serviceControl("install")serviceControl("start")
}func StopSVC() {log.Infof("try to stop service, if already exists.")serviceControl("stop")
}func RunSVC() {fmt.Sprintf("%s service running \n", runtime.GOOS)if err := svc.Run(); err != nil {fmt.Sprintf("%s service running fail %v \n", runtime.GOOS, err)os.Exit(1)}
}func serviceControl(action string) {log.Infof("%s service %s \n", runtime.GOOS, action)if err := service.Control(svc, action); err != nil {log.Infof("%s service: %v \n", action, err)}
}

3 编写服务脚本(startup、shutdown脚本)

3.1 startup脚本

  1. assets/startup.bat.tmpl
@echo off@REM It is recommended to start here.
@REM Modify bash args below.
@REM "name" and "tags" must be alphanumeric sequence: [a-zA-Z-_.]
@REM Chinese chars are not supported here in bash file.
@REM
@REM Example:
@REM
@REM yiService.exe service start ^
@REM
@REM Startup from here
@REM
c:/yiService/yiService.exe service startecho "yiService started!"
echo ""
pause
  1. assets/startup.sh.tmpl
#!/bin/bash# It is recommended to start here.
# Modify bash args below.
# "name" and "tags" must be alphanumeric sequence: [a-zA-Z-_.]
# Chinese chars are not supported here in bash file.
#
# Example:
#
# yiService.exe service start \
#
# Startup from here
#
# launchctl start yiService
./yiService service startecho yiService started!
ps aux |grep yiService
echo ""

3.2 shutdown脚本

  1. assets/shutdown.bat.tmpl
@echo off
@REM This command bash will stop the yiService windows service
@REM And then uninstall this service from operation system
@REM Configurations will be remained in directory c:/yiService/yiService on the disk.
@REM You can restart from those configurations in the near future.
@REM
c:/yiService/yiService.exe service stop
set "$process=yiService.exe"
for %%a in (%$process%) do tasklist /fi "imagename eq %%a"  | find "%%a" && taskkill /f /im %%aecho shutdown yiService successfully!
pause
  1. assets/shutdown.sh.tmpl
#!/bin/bash# This command bash will stop the yiService windows service
# And then uninstall this service from operation system
# Configurations will be remained in directory c:/yiService on the disk.
# You can restart from those configurations in the near future.
#./yiService service stop
PID=$(ps -eaf | grep '/usr/local/yiService' | grep -v grep | awk '{print $2}')
if [[ "" !=  "$PID" ]]; thenecho "killing $PID"kill -9 "$PID"
fiecho shutdown yiService successfully!

4 编写extractFiles(执行exe文件之后,拷贝exe到服务工作目录)

system_service/extract_files.go:

package system_serviceimport ("awesomeProject1/assets""awesomeProject1/consts""fmt"log "github.com/sirupsen/logrus""io""os""runtime"
)func ExtractFiles() {CopyMainExe()//copy startup\shutdownif err := os.WriteFile(consts.StartupBash, assets.StartupBatTmpl, os.ModePerm); err != nil {log.Errorf("create startup bash failed %v", err)}if err := os.WriteFile(consts.ShutdownBash, assets.ShutdownBatTmpl, os.ModePerm); err != nil {log.Errorf("create shutdown bash failed %v", err)}
}func CopyMainExe() {executable, err := os.Executable()log.Infof("install %s to %s", executable, consts.MainExe)if err != nil {log.Errorf("%v", err)}sFile, err := os.Open(executable)if err != nil {log.Errorf("%v", err)}defer sFile.Close()exePath := fmt.Sprintf("%s/%s", consts.WorkDir, consts.MainExe)if runtime.GOOS == "windows" {exePath = fmt.Sprintf("%s\\%s", consts.WorkDir, consts.MainExe)}_, err = os.Stat(exePath)if err == nil {//overwriteif err := os.RemoveAll(exePath); err != nil {log.Errorf("%v", err)}}eFile, err := os.Create(exePath)if err != nil {log.Errorf("%v", err)}defer eFile.Close()if _, err = io.Copy(eFile, sFile); err != nil {log.Errorf("%v", err)}if err = eFile.Sync(); err != nil {log.Errorf("%v", err)}if err = os.Chdir(consts.WorkDir); err != nil {log.Errorf("%v\n", err)}if err = os.Chmod(consts.MainExe, os.FileMode(0777)); err != nil {log.Errorf("%v", err)}
}

5 编写firewall部分+main函数部分(开放端口+由系统服务拉起进程)

firewall部分:以管理员身份运行,开放进程服务端口
cmd/main.go部分:初次运行尝试卸载服务,带os.Args参数运行启动服务

5.1 firewall部分

  1. darwin

system_service/firewall/firewall_darwin.go:

//go:build darwin
// +build darwinpackage firewallimport ("awesomeProject1/consts""bytes""fmt"log "github.com/sirupsen/logrus""os/exec""strconv""strings"
)func OpenPort() {log.Infof("darwin firewall checking\n")cmd0 := exec.Command("/usr/bin/nc", "-z", "127.0.0.1", fmt.Sprintf("%d", consts.ServicePort))log.Warnf("cmd0=%s\n", cmd0)stdout, err := cmd0.CombinedOutput()result := string(stdout)if err != nil {log.Infof("err=%v \n", err)return}if strings.Contains(result, "command not found") {fmt.Println("[warn]:", result)return}if strings.Contains(result, "not running") {fmt.Println("[warn]:", result)return}if strings.Contains(result, strconv.Itoa(consts.ServicePort)) {log.Warnf("%d already opened\n", consts.ServicePort)return}cmd := exec.Command("bash", "-c", fmt.Sprintf("firewall-cmd --zone=public --add-port=%d/tcp --permanent && firewall-cmd --reload", consts.ServicePort))var out bytes.Buffervar stderr bytes.Buffercmd.Stdout = &outcmd.Stderr = &stderrif err := cmd.Run(); err != nil {log.Warnf("%s", stderr.String())log.Warnf("%v", err)}log.Warnf(out.String())
}
  1. Windows
    system_service/firewall/firewall_windows.go
//go:build windows
// +build windowspackage firewallimport ("bytes""fmt"log "github.com/sirupsen/logrus""os/exec""runtime"
)func OpenPort() {log.Infot("windows firewall checking")cmd := exec.Command("cmd", "/c", "netsh advfirewall firewall delete rule name=\"yiService\"")var out bytes.Buffervar stderr bytes.Buffercmd.Stdout = &outcmd.Stderr = &stderrif runtime.GOOS == "windows" {}if err := cmd.Run(); err != nil {log.Errorf("%s", stderr.String())log.Errorf("%v", err)}cmd2 := exec.Command("cmd", "/c",fmt.Sprintf("netsh advfirewall firewall add rule name=\"yiService\" dir=in action=allow protocol=TCP localport=%d",consts.ServicePort,))var out2 bytes.Buffervar stderr2 bytes.Buffercmd2.Stdout = &out2cmd2.Stderr = &stderr2if runtime.GOOS == "windows" {}if err := cmd2.Run(); err != nil {log.Errorf("%s", stderr2.String())log.Errorf("%v", err)}
}

5.2 main函数

cmd/main.go

package mainimport ("awesomeProject1/consts""awesomeProject1/system_service""awesomeProject1/system_service/firewall""awesomeProject1/util"log "github.com/sirupsen/logrus""os"
)func init() {//os.Setenv("dev", "true")util.InitLog(consts.LogPath)
}func main() {if len(os.Getenv("dev")) != 0 {system_service.StopSVC()firewall.OpenPort()system_service.StartSVC()system_service.RunSVC()}log.Errorf("os.Args=%v len=%v \n", os.Args, len(os.Args))if len(os.Args) == 1 {//stop svc if existsystem_service.StopSVC()log.Errorf("install %v \n", consts.ServiceName)if err := os.MkdirAll(consts.WorkDir, os.ModePerm); err != nil {log.Errorf("%v\n", err)}firewall.OpenPort()system_service.ExtractFiles()pwd, err := os.Getwd()if err != nil {log.Errorf("%v\n", err)}log.Infof("install svc, working directory %s", pwd)system_service.StartSVC()log.Infof("yiService installed!")return}os.Chdir(consts.WorkDir)log.Errorf("service %s \n", os.Args[2])switch os.Args[2] {case "start":system_service.StartSVC()returncase "stop":system_service.StopSVC()returndefault:system_service.RunSVC()log.Info("running yiService")}
}

这篇关于golang实现注册系统服务(Windows、Darwin)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/516289

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import