在windows 系统下,通过命令行窗口启动服务,有一个缺点:当鼠标置于窗口内,可能将程序挂起(窗口是交互界面,如果你有在程序内部捕获标准输入输出的话,是会挂起程序的)。所以,将程序制作成服务,以服务的方式启动程序,成了windows系统下服务器程序最佳的启动方式。
但是用golang编写的程序,通过sc工具制作的服务无法正常启动。报错信息如下:
在网搜索了很多关于这方便的信息,要么是没有结贴的,要么是通过传入参数,自动将程序制作成服务的。但是,这些都不能满足我们项目现在的使用场景。我们需要用手动方式将程序做成服务(通过sc工具),然后在添加开机任务(属其他内容)。
在网上找到了"golang.org/x/sys/windows"包,该包对Windows服务提供了必要的支持。只是这个扩展包比较偏向底层使用比较繁琐。后来又找到了"github.com/kardianos/service"包,这个包是对"golang.org/x/sys/windows"包的进一步封装,使用起来非常方便。示例代码如下:
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/kardianos/service"
"log"
)
var logger service.Logger
type program struct{}
func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async.
go p.run()
return nil
}
func (p *program) run() {
// Do work here
StartServer()
}
func (p *program) Stop(s service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}
func main() {
svcConfig := &service.Config{
Name: "GoServiceTest",
DisplayName: "Go Service Test",
Description: "This is a test Go service.",
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
}
err = s.Run()
if err != nil {
logger.Error(err)
}
//StartServer()
}
// 开启服务
func StartServer() {
lwlog.Logs.Info("lwboottask started !!!")
// TODO :初始化代码
beego.Run()
}
// 停止服务
func StopServer() {
}
程序启动后,首先检测程序运行模式,根据运行模式不同,决定是否启用windows服务。
下面列出操作命令,分别是注册服务、启动服务、停止服务、删除服务:
sc create lwagentservices binpath= "%~dp0lwyagent.exe" start= auto displayname= "lwyagent"
net start myserver
net stop myserver
sc delete myserver
通过这种方式,成功解决了sc工具制作windows服务后,启动失败问题。
注意:通过服务的方式启动后,一定要注意读取配置文件的路径以及日志文件的输出路径都要采用绝对路径。由于,这个问题,我曾经一直认为通过sc工具做服务失败,其实是读取配置文件失败(路径问题)。后来改用绝对路径后,服务就正常工作了。