中國電建集團(tuán)華東勘測設(shè)計研究院有限公司 黃成家 李增煥 楊磊
根據(jù)Windows服務(wù)器中的網(wǎng)站系統(tǒng)運維和開發(fā)工作需求,開發(fā)并實現(xiàn)了Windows系統(tǒng)下方便適用且非常輕量的系統(tǒng)服務(wù)注冊及管理軟件,克服了常規(guī)應(yīng)用程序無法注冊為合規(guī)的Windows系統(tǒng)服務(wù)的限制,可將任意的應(yīng)用程序注冊為系統(tǒng)服務(wù),由此簡化了Windows服務(wù)器中的網(wǎng)站或其他服務(wù)的開發(fā)、部署及更新工作。
為應(yīng)對服務(wù)器非正常重啟等異常狀況,網(wǎng)站服務(wù)、API服務(wù)或其他類似應(yīng)用程序需要隨系統(tǒng)開機(jī)自動運行。Linux系統(tǒng)下,采用service或systemd命令可以非常簡便地將任意的應(yīng)用程序注冊為系統(tǒng)服務(wù),實現(xiàn)應(yīng)用程序隨系統(tǒng)開機(jī)自動運行[1]。然而,Windows系統(tǒng)下卻缺少類似的工具或軟件,給Windows服務(wù)器的運維和管理人員帶來了不便。
本文作者利用.Net Framework開發(fā)并實現(xiàn)了方便適用且非常輕量的系統(tǒng)服務(wù)注冊及管理軟件,引入中間二進(jìn)制程序,克服了常規(guī)應(yīng)用程序無法注冊為合規(guī)的系統(tǒng)服務(wù)的限制,可將任意應(yīng)用程序注冊為系統(tǒng)服務(wù),由此簡化了Windows服務(wù)器下的網(wǎng)站或其他服務(wù)的開發(fā)、部署及更新工作。
Windows系統(tǒng)下,將應(yīng)用程序設(shè)置為隨開機(jī)自動啟動的現(xiàn)有方案主要有以下5種方法:
(1)修改注冊表中的啟動項;(2)將應(yīng)用程序的快捷方式復(fù)制到啟動目錄下;(3)創(chuàng)建隨開機(jī)啟動的計劃任務(wù);(4)用sc命令將應(yīng)用程序注冊為系統(tǒng)服務(wù);(5)用第三方工具(NSSM或類似工具)將應(yīng)用程序注冊為系統(tǒng)服務(wù)。
其中前兩種方式均需用戶登陸成功后才能運行,需要人工的介入,不適合系統(tǒng)重啟后需要自動運行的應(yīng)用程序(如網(wǎng)站服務(wù)或數(shù)據(jù)庫服務(wù)等)。
創(chuàng)建計劃任務(wù)可實現(xiàn)應(yīng)用程序隨開機(jī)自動啟動,但是創(chuàng)建過程較為繁瑣,且應(yīng)用程序需要更新時也不方便。
采用系統(tǒng)自帶的sc命令可實現(xiàn)系統(tǒng)服務(wù)的注冊和啟動,但是應(yīng)用程序必須是可執(zhí)行的二進(jìn)制程序,且必須按照微軟的服務(wù)程序格式編寫,在程序內(nèi)部實現(xiàn)一個特定的消息響應(yīng)循環(huán),否則,用sc命令注冊為系統(tǒng)服務(wù)后,服務(wù)無法正常啟動[2-5]。事實上,絕大多數(shù)常規(guī)應(yīng)用程序都無法用sc命令注冊為合規(guī)的系統(tǒng)服務(wù)。
采用NSSM或類似第三方工具將常規(guī)應(yīng)用程序注冊為系統(tǒng)服務(wù)是目前來說較為可行的解決方案,但是也存在命令行操作方式復(fù)雜、缺少日志管理功能、無退出通知機(jī)制等缺點。
系統(tǒng)運維工作中,網(wǎng)站服務(wù)、API服務(wù)或其他長期運行的服務(wù)類應(yīng)用程序都有以下共性的需求:
(1)程序需要在后臺長期運行,即便用戶注銷仍不停止。(2)系統(tǒng)開機(jī)或重啟后,程序被自動啟動。(3)程序意外停止時,程序被自動重新啟動。(4)程序可能依賴于其他服務(wù),啟動之前需要先等待相關(guān)服務(wù)啟動完成。(5)程序應(yīng)能隨時被用戶停止或重啟,以方便對服務(wù)進(jìn)行更新。
根據(jù)以上需求,結(jié)合Windows系統(tǒng)服務(wù)提供的功能,并考慮操作簡便的要求,整理本軟件需要提供的命令及功能如下:
用戶將應(yīng)用程序的命令行參數(shù)、工作目錄、程序輸出目錄、輸出編碼,以及需注冊的系統(tǒng)服務(wù)名稱、服務(wù)依賴等信息保存在配置文件中。
注冊并啟動一個自啟動的系統(tǒng)服務(wù),服務(wù)名稱等信息由配置文件指定,該服務(wù)啟動時會去啟動配置文件中指定的應(yīng)用程序。同時,該服務(wù)會在系統(tǒng)開機(jī)時等待相關(guān)依賴啟動后自動啟動。
停止并刪除由配置文件中指定的系統(tǒng)服務(wù),該服務(wù)停止時會通知并等待應(yīng)用程序退出。
停止、啟動或重啟指定的系統(tǒng)服務(wù)。
當(dāng)停止服務(wù),應(yīng)具有通知應(yīng)用進(jìn)程退出、并等待其自行退出的功能。
應(yīng)用程序運行過程的中輸出(寫入到標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯誤的數(shù)據(jù))均按日期寫入到配置文件中指定的目錄下。
系統(tǒng)服務(wù)的運行過程中,對應(yīng)用程序進(jìn)程進(jìn)行監(jiān)視,如果發(fā)現(xiàn)該進(jìn)程已退出或占用內(nèi)存超過指定值,則重新啟動應(yīng)用程序。
軟件采用C#語言編寫,基于.NetFramework 4.0框架[6-7],開發(fā)環(huán)境采用Visual Studio Community 2019。
由于常規(guī)的應(yīng)用程序無法注冊為系統(tǒng)服務(wù),因此,軟件本身采用微軟要求的服務(wù)程序格式要求編制,在程序內(nèi)部實現(xiàn)服務(wù)消息循環(huán)。在調(diào)用軟件的install命令時,該命令會將軟件自身的二進(jìn)制可執(zhí)行文件注冊為系統(tǒng)服務(wù)。而軟件作為系統(tǒng)服務(wù)被啟動時,會啟動一個子進(jìn)程去運行配置文件中指定的應(yīng)用程序;該軟件作為系統(tǒng)服務(wù)被停止時,會通知子進(jìn)程退出并在必要的情況下直接終止子進(jìn)程。按此方式,便繞過了常規(guī)應(yīng)用程序無法注冊為系統(tǒng)服務(wù)的限制,實現(xiàn)了任意應(yīng)用程序隨開機(jī)自動啟動、并按系統(tǒng)服務(wù)模式隨時停止或重啟。
根據(jù)上述方式,軟件有服務(wù)管理模式和服務(wù)運行模式兩種運行模式。其中服務(wù)管理模式下,軟件執(zhí)行install|remove|stop|start|restart等命令,讀取配置文件中指定的服務(wù)信息,調(diào)用sc create命令將自身的二進(jìn)制可執(zhí)行文件注冊為系統(tǒng)服務(wù),或調(diào)用sc delete命令刪除已注冊的系統(tǒng)服務(wù),同時利用.Net Framework提供的ServiceController類對已注冊的系統(tǒng)服務(wù)進(jìn)行停止或啟動等操作。而在服務(wù)運行模式下,軟件的作為系統(tǒng)服務(wù)的執(zhí)行程序,被Windows系統(tǒng)的服務(wù)管理器控制,響應(yīng)系統(tǒng)傳來的啟動或停止等控制消息,并啟動、監(jiān)視或停止應(yīng)用程序的運行。
軟件內(nèi)部運行流程分別見圖1、圖2和圖3。
圖1 軟件總體運行流程Fig.1 The overall running procedure of the software
圖2 服務(wù)運行模式內(nèi)部運行流程Fig.2 The running procedure of service-runner mode
圖3 服務(wù)管理模式內(nèi)部運行流程Fig.3 The running procedure of service-manager mode
在服務(wù)管理模式下,軟件由用戶在命令行終端運行,因此配置文件直接使用當(dāng)前工作目錄下的特定文件(svc.conf)。
而在服務(wù)運行模式下,情況比較復(fù)雜,因為軟件由Windows 系統(tǒng)的服務(wù)管理器啟動,當(dāng)前工作目錄為系統(tǒng)目錄,不是用戶存放配置文件的目錄。因此,本軟件采用了以下策略來定位配置文件:
(1)注冊服務(wù)時(此時在服務(wù)管理模式下),將配置文件路徑保存在系統(tǒng)服務(wù)的Discription中。(2)服務(wù)運行模式下,首先根據(jù)本進(jìn)程ID查找到本服務(wù)的ServiceName,再根據(jù)Service-Name獲取本服務(wù)的Discription,由此便可得到配置文件路徑。
以下以一個簡單的Node.js程序為例,說明本軟件的使用方法。
示例應(yīng)用程序采用Node.js編寫,其功能僅僅是每隔1秒打印一行信息,且鍵入回車后退出,程序代碼如下:
將軟件自帶文件拷貝到任意位置,右鍵單擊bin目錄下的register-this-path.bat,以管理員身份運行,將bin目錄加入至系統(tǒng)路徑中,也可以手動將此目錄加入至系統(tǒng)路徑。
重新打開“我的電腦”,在任意位置打開一個命令行窗口,輸入svc-v,如果正常輸出版本信息,則表明安裝成功。
打開命令行窗口,輸入svc create hello-svc,將創(chuàng)建一個工程hello-svc。將示例程序index.js拷貝到hellosvc/worker目錄下。打開hello-svc/svc.conf文件,輸入以下內(nèi)容:
用管理員身份打開命令行窗口(Win10系統(tǒng)下,需要在開始菜單中搜索cmd然后右鍵以管理員身份運行),cd到hello-svc目錄:
(1)運行svc check命令檢查配置是否合法。(2)運行svc test-worker命令測試應(yīng)用程序是否能正常運行。
若配置和應(yīng)用程序無誤,則:
(3)運行svc install命令安裝并啟動系統(tǒng)服務(wù),此時應(yīng)用程序就已經(jīng)開始在后臺運行了;
(4)運行svc log查看正在運行的服務(wù)程序的輸出;
(5)運行svc stop|start|restart|remove停止、啟動、重啟或刪除本系統(tǒng)服務(wù)。
在系統(tǒng)重啟、用戶注銷等情況下,用ssh遠(yuǎn)程連接系統(tǒng),cd到hello-svc目錄,運行svc log查看程序輸出,確認(rèn)應(yīng)用程序一直在運行。
按以上步驟,用svc create創(chuàng)建多個目錄,修改svc.conf中的服務(wù)名和程序名等內(nèi)容,再在這些目錄下打開命令行窗口執(zhí)行svc check|test-worker|install等命令可以注冊多個服務(wù),這些服務(wù)可使用以下命令進(jìn)行統(tǒng)一管理:
(1)svc list|ls:列出所有服務(wù)的信息及運行狀態(tài)。(2)svc start|stop|remove all:啟動、停止或刪除所有服務(wù)。(3)svc check|status|test-worker|install|start|stop|restart|remove|log $project-directory:操作$project-directory目錄下svc.conf指定的服務(wù),$project-directory中必須含有字符或/。(4)svc start|stop|restart|remove |log $service-name:操作名稱為$service-name的服務(wù)。(5)svc start|stop|restart|remove|log $service-index:操作第$service-index個服務(wù)(運行svc ls可查看所有服務(wù)的序號)。
從應(yīng)用實例可以看出,本文實現(xiàn)的軟件可將任意的應(yīng)用程序注冊為系統(tǒng)服務(wù),克服了常規(guī)應(yīng)用程序無法注冊為合規(guī)的Windows系統(tǒng)服務(wù)的限制,與傳統(tǒng)的計劃任務(wù)或第三方工具方案相比,網(wǎng)站或其他服務(wù)的開發(fā)、部署及更新工作大為簡化,為運維工作人員帶來了很大的便利。軟件自2019年11月發(fā)布在開源社區(qū)Github上以來,已被多個網(wǎng)站引用,并廣受同行的好評。
數(shù)字技術(shù)與應(yīng)用2021年11期