服务程序实现自己更新自己
Tag 服务, 自更新, on by view 90

作为一个广泛存在于众多设备上的常驻程序如何更新?他可以是一个agent,也可以是一个service,可以运行在众多容器中作为基础支持的程序,也可以运行在路由器、空调、洗衣机等家用设备或者物联设备上的支持程序。或许有些程序可以通过第三方平台进行更新,比如某些服务程序,配有专门的发版平台。但是更多的是无法通过发版平台发布的程序,比如是linux上的一个自启动的service,这个linux是你家里的路由器。这时候就需要程序自己更新自己了。

程序更新通常有下列步骤,下载新的程序,替换旧的可执行文件和配置文件,重启程序(退出旧进程,拉起新进程)。

程序自己如何去做这些操作?通常下载和替换文件并不难,只需要有一个接口能查询到新的程序版本和下载地址,很容易实现下载新版程序和替换。唯一需要注意一点的是重启程序这一步骤,这一步骤在业务程序进程上是无法实现的,因为它要拉起一个业务进程(worker),并且退出自己,在它拉起业务进程的时候,如果自己本身就是业务进程(worker),这样就会有两个业务进程(worker),这对于大多数服务是不允许的,会出错。那么我们可以专门设计一个守护进程(watcher)仅用于程序的更新与重启。

因此watcher进程里,更新程序和重启程序有如下流程 hhy70tzc

下载替换与杀旧进程(worker)放在一个线程中,另一个线程只负责监视worker进程是否存在,如果worker进程不存在,则拉起worker进程。

同一个程序中,如何区分worker进程与watcher进程?我这里使用的是特定环境变量来区分的。终端中拉起程序,默认启动的是watcher进程,watcher进程启动后检测到worker进程不存在,那么它将会通过exec.Command拉起进程,并且注入特定环境变量,如下

cmd := exec.Command(procName, os.Args[1:]...)
cmd.Env = append(os.Environ(), "PROC_FLAG=worker")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if e := cmd.Start(); nil != e {
	log.Printf("ERROR: %v\n", e)
	return
}

而且程序启动的时候,首先也会判断该环境变量是否存在,如果存在该环境变量,那么就进入业务进程的业务逻辑。如果不存在该环境变量,则进入守护进程的守护逻辑。

这样一来就能够实现程序自己更新自己了。


windows服务注册工具nssm
Tag nssm, windows, 服务, 注册, on by view 8740

在windows系统上注册一个服务通常你要这么做:

首先,如果这个服务程序是你写的话,你必须在程序中编写ServiceMain服务主函数用来启动服务,并且要在程序中注册服务启动函数,函数原型如下。

VOID WINAPI ServiceMain(
  DWORD dwArgc,
  LPTSTR* lpszArgv
);

然后,你需要修改注册表相关信息,在注册表项"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\"中自动增加项[服务名],并在其下成功添加各个键值:

"DisplayName"   =服务显示名     (字符串值)
"Description"      =服务描述       (字符串值)
"ImagePath"       =应用程序路径   (可扩充字符串值)
"ObjectName"    ="LocalSystem"  (字符串值)
"Type"                =10(16进制)     (dword值)
"Start"                =2(16进制)      (dword值)
"ErrorControl"     =1(16进制)      (dword值)

或者直接执行instsrv [服务名] [应用程序路径],自动添加注册表信息。(不过貌似只有服务器版的windows才有instsrv命令)

完成上述操作后,如果不出意外你可以在服务管理器services.msc中可以看到你的服务。当然你如果想卸载服务,至少得把上面的第二步反过来做一遍,即清理注册表。

现在有一个更好的工具可以完成这项任务,nssm

nssm是一个命令行工具,也支持gui。

nssm install <ServerName> <Program Path>  #安装
nssm remove <ServerName> confirm          #卸载

nssm_usage_demo.png
命令行使用

nssm_gui_usage_install.png
GUI使用(安装)

nssm_gui_usage_remove.png
GUI使用(卸载)

使用nssm来安装windows服务是不需要主程序中注册服务的,也就是说想将一个程序变为服务程序有了nssm后你无需修改代码。