笔者最近在搞一个项目,须要把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