kardianos / service

Run go programs as a service on major platforms.
zlib License
4.45k stars 677 forks source link

windows self call Restart will not Start #163

Open tommady opened 5 years ago

tommady commented 5 years ago

sorry I am facing a problem on doing the Restart in windows service, I want to do the self-updating things.

said I have a goroutine

        go func() {
        <-p.updater.updateSignal
        s.Restart()
    }()

when the channel has been triggered, then going down to s.Restart() everything good, the service got Stop, but the service just stopped forever, no Start again.

the document said

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

so is there something I miss-understanding?

please guide me, thank!

haroldHT commented 5 years ago

Can u provide more detail? How did u restart? Or ur code.

yi-ge commented 5 years ago

I have the same problem.

Program example in windows:

func (ws *windowsService) Restart() error {
    m, err := mgr.Connect()
    if err != nil {
        return err
    }
    defer m.Disconnect()

    s, err := m.OpenService(ws.Name)
    if err != nil {
        return err
    }
    defer s.Close()

    err = ws.stopWait(s)
    if err != nil {
        return err
    }

    return s.Start()
}

You can't restart by itself.

Feasible example:

var restartCmd *exec.Cmd
restartCmd = exec.Command(YOUR PATH, "restart")
restartCmd.Start()
joshforbes commented 4 years ago

I have the same problem. If I attempt to Stop the service and then sleep for a long period before calling Start (essentially what "Restart" is doing) I get:

image

From what I can tell you are unable to restart a windows service from within the service. My only thought now is that I could crash the process and allow the windows service manager to recover it.

@tommady and @yi-ge how did you end up solving this?

JefMasereel commented 10 months ago

Same issue here. One "quick and dirty" solution could be to run a secondary service (s1) that restarts the primary service (s2) when s1 goes down, or when s1 sends a specific command to s2. This works well, but it implies a bit more overhead when managing many instances across a fleet of machines. And when s2 requires an automatic restart it gets more complicated again...

On Unix it seems easier to build a "live reloading" mechanism that restarts the process without downtime, so another solution could be to run the program on a Unix container or VM. Containerization can be interesting for fleet management, but you have to be careful not to accumulate too much overhead again.

For a Windows compatible solution you will probably have to implement your own live reload mechanism as part of the service definition. Try using standard library "os" to identify the active process(es) (getPid, FindProcess) and start/stop them. Make sure to keep Windows quirks in mind, e.g. only using os.Interrupt or os.Kill signals with the "signal" pkg.

Or you could implement the service management from scratch... Should be possible with the windows service utilities from the "sys" package (https://pkg.go.dev/golang.org/x/sys@v0.15.0/windows/svc)

Some reference links for "hot reload" solutions in Go:

joshforbes commented 10 months ago

We considered implementing a second watchdog service to keep an eye on the primary service but I was worried about the extra complexity. Our current solution is two-fold:

  1. We ensure the service always exits with an exit code so that the Windows service manager restart options will take effect. Specifically, we use exit code "1467" ("The application must be restarted" - https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-)
  2. The service adds a scheduled "watchdog task". This uses Window's task scheduler and runs every X minutes to see if the service is running. If the service is running, it does nothing. If the service is not running, it starts it.

Typically the service manager will keep the service running without any extra assistance but this scheduled task provides extra piece of mind. This feels a bit hacky so we may eventually go with a secondary service to be more robust but this has worked for us so far.