筆者最近在搞一個項目,須要把python客戶端代碼(能夠理解成綁定不一樣帳號的爬蟲吧)部署在20+臺windows機器上(之後還會追加新機器)。因爲客戶機多,並且經常優化、修bug、加功能,使得筆者爲了更新代碼而不勝其擾!小小的調整竟然要一臺臺部署(windows只能遠程鏈接,可沒辦法linux那樣同時給多個ssh鏈接發相同的命令)。因而,筆者測試並開發了一套「基於windows自動下拉git代碼並重啓程序模塊」(筆者不知道該簡稱什麼...大概就是自動更新代碼吧)。python
linux系統就很少說了,只須要git檢測到更新後,kill -9 便可,能夠根據參數識別,例如 python client_main.py 利用ps篩選出來殺掉便可。linux
windows沒有這命令,可是能夠根據執行文件名殺掉。例如殺掉名叫 python.exe 的全部進程。這是惟一方法,可是你會把系統下全部的python都幹掉,包括基於python的調試工具。git
因而,我想到的方法是用最土的方法,「用標識位告知程序是否有更新,是否要終止,是否要重啓」。雖然又土又不帥氣,卻很實用。特別像我這種客戶端必須等一個任務完成後才能終結的,很是合適。一個守護進程檢測更新,一個文件標識狀態,子進程本身檢測標識狀態去中止。簡簡單單的完成了自動化需求。windows
測試git倉庫 : https://gitee.com/kid0/tx 【有須要測試,代碼請上傳到本身的git上方便測試。固然也能夠在本身的git項目中直接引用auto.py下的函數】網絡
模塊代碼(我命名爲auto.py)ssh
#-*-coding:utf-8-*- import os import time import codecs from configparser import ConfigParser from multiprocessing import Process local_path = os.path.join(os.getcwd(),'local.txt' ) code_check_time = 10 def set_code_check_time(t=10): """ 設置git檢測頻率,單位秒 """ global code_check_time code_check_time = t def auto_manager_main(fun,*args,**kwargs): """ 委託進程 """ print('主進程PID:',os.getpid()) global code_check_time _code_check_time = code_check_time if 1: if not os.path.isfile(local_path): f = open(local_path,'w+') txt = """[local] need_stop = 0 need_restart = 0""" f.write(txt) f.close() con = ConfigParser() con.read_file(codecs.open(local_path,'r+',"utf-8-sig")) con.set('local','need_stop','0') con.set('local','need_restart','0') con.write(open(local_path,'w+')) _f = True while True: con.read_file(codecs.open(local_path,'r+',"utf-8-sig")) if not con.get('local','need_stop') == '1': _res = os.popen('git pull').read() if 'Fast-forward' in _res: con.set('local','need_stop','1') else: con.set('local','need_stop','0') con.write(open(local_path,'w+')) if con.get('local','need_restart') == '1' or _f: _f = False p = Process(target=fun,args=args,kwargs=kwargs) p.start() con.set('local', 'need_restart','0') con.write(open(local_path,'w+')) time.sleep(10) def check_stop_flag(): """ 檢測是否應該中止,在被託管的進程裏使用 """ con = ConfigParser() con.read_file(codecs.open(local_path,'r+','utf-8-sig')) if con.get('local','need_stop') == "1": print('code was updata,now stop running!!') con.set('local','need_stop',"0") con.set('local','need_restart',"1") con.write(open(local_path,"w+")) exit(0)
測試main.py文件函數
#-*-coding:utf-8-*- from auto import check_stop_flag,auto_manager_main,set_code_check_time import time def run(x=0,y=0): #開始子進程 print('run!!!!') for i in range(x,500): #設置在這裏檢測是否要退出進程 check_stop_flag() #....作不該該忽然中斷任務.... time.sleep(1) print('i={} y={}'.format(i,y)) #....完成了... def tx(y=1):#(稍後測試我把1改爲10) #main函數【可變參數能夠傳這裏,若是參數值改變,程序重啓後會跟隨改變】 run(x=1,y=y)#(稍後測試我把1改爲10) if __name__ == '__main__': #設置檢測頻率(彷佛並不會生效,待驗證優化) set_code_check_time(1) #把進程委託自動管理【固定參數能夠這裏傳入,重啓程序參數也不會跟隨改變的】 auto_manager_main(tx,y=1)#(稍後測試我把1改爲10)
我先運行main.py文件,順利的執行後,我在git上把參數x和y的值由1改爲10。結果以下。工具
主進程PID: 6004 run!!!! i=1 y=1 i=2 y=1 i=3 y=1 i=4 y=1 i=5 y=1 i=6 y=1 i=7 y=1 i=8 y=1 i=9 y=1 i=10 y=1 i=11 y=1 remote: Enumerating objects: 5, done. remote: Counting objects: 100% (5/5), done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 2), reused 0 (delta 0) i=12 y=1g objects: 33% (1/3) Unpacking objects: 100% (3/3), done. From gitee.com:kid0/tx 3ca10d8..9f08a5d master -> origin/master i=13 y=1 code was updata,now stop running!! run!!!! i=10 y=1 i=11 y=1 i=12 y=1 i=13 y=1 i=14 y=1
在我git改了代碼後,main正常的本身退出,而且重啓了。參數也是用最新的去運行。測試
實驗完成!!!投入使用!優化
一、.gitignore文件必須加入忽略local.txt文件!!!不然會致使衝突,沒法下拉代碼!!!
二、可能會被修改的參數絕對不能由auto_manager_main傳!應當用另外一個函數簡單包起main函數,像我測試的那樣。
一、爲何用Process(進程)而不是threading(線程)?
python雖然是解釋語言,但它須要把py編譯成pyc文件去運行。進程啓動時會從新編譯,但線程不會。你們能夠試試。雖然會更新代碼並重啓,可是運行結果不會改變。
二、是否適用於全部python項目?
適用於「一套代碼放在多個客戶機,而且常常更新git」的狀況。對於server服務我並不推薦。由於server服務應該是累積必定的更新後再更新到線上。
三、直接殺掉進程用腳本啓動會不會更簡單?
條條大路通羅馬。方法不少,看你喜歡。像個人項目,任務是從隊列拿的。若是忽然幹掉了進程,任務就會丟失,並且我這每一個任務都十分關鍵!條條任務都是錢!(我拿到任務會寫到本地文件內,完成再刪除,防機器宕機的可能)若是是普通爬蟲之類的倒無所謂,丟失1-2條數據不要緊的。
四、git pull爲何不是檢測「Already up to date」?
一開始我也是這樣的考慮的。可是,網絡異常、衝突、sshkeygen失效等問題都會致使無限的重啓程序。所以只能檢測pull成功下拉的標識。
五、爲何在上述案例中x的值改了,y沒有改?
父進程是守護進程,負責更新代碼和啓動main,參數y是父進程給的,父進程並無重啓,因此y沒有改變。x是子進程從新編譯後纔給的參數,因此是最新的。
(ps:設置檢測頻率是否生效我沒去測,不過10秒或者60秒的檢測頻率已經足夠的了)
——————————————————————————————————————————
以上就是我開發的模塊和理解。歡迎你們使用和改進。
轉載請註明出處。https://my.oschina.net/jacky326/blog/3027504