簡(jiǎn)介
通常在windows平臺(tái)上開(kāi)發(fā)完python服務(wù)后撵割,需要通過(guò)引用虛擬環(huán)境后再運(yùn)行奠宜,或者使用IDE軟件啟動(dòng)哗讥,但如果想做成開(kāi)機(jī)啟動(dòng)的本地服務(wù)嚷那,每次都這樣啟動(dòng)就太麻煩了。?
該文檔介紹這個(gè)方法是把python程序制作成一個(gè)windows忌栅,啟停特別方便车酣,而且還可以設(shè)置開(kāi)機(jī)啟動(dòng)。虛擬環(huán)境依賴(lài)的庫(kù)也不需要考慮索绪,只要在構(gòu)建的時(shí)候在虛擬環(huán)境下構(gòu)建湖员,就會(huì)自動(dòng)將依賴(lài)的庫(kù)打包進(jìn)去。
1瑞驱、首先娘摔,有一段大家通用的代碼,可以將python程序制作成服務(wù)唤反。直接拷貝使用即可凳寺,在SvcDoRun函數(shù)內(nèi)寫(xiě)上拉起自己服務(wù)的代碼。
# -*- coding:utf-8 -*-
import win32serviceutil
import win32service
import win32event
import sys
import os
#設(shè)置編碼
reload(sys)
sys.setdefaultencoding('utf-8')
#windows服務(wù)中顯示的名字
class zlsService(win32serviceutil.ServiceFramework):
_svc_name_ ='web_movie' ###可以根據(jù)自己喜好修改
? ? _svc_display_name_ ='web_movie'? ###可以根據(jù)自己喜好修改
? ? _svc_description_ ='web_movie'? ###可以根據(jù)自己喜好修改
? ? def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.stop_event = win32event.CreateEvent(None,0,0,None)
self.run =True
? ? def SvcDoRun(self):
# 這里是你的啟動(dòng)代碼彤侍,由于我的是flask框架程序肠缨,只需要把我的主文件from過(guò)來(lái)即可。
from web_movieimport app
app.run()
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.stop_event)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
self.run =False
if __name__ =='__main__':
import sys
import servicemanager
if len(sys.argv) ==1:
try:
evtsrc_dll = os.path.abspath(servicemanager.__file__)
servicemanager.PrepareToHostSingle(zlsService)#如果修改過(guò)名字盏阶,名字要統(tǒng)一
? ? ? ? ? ? servicemanager.Initialize('zlsService',evtsrc_dll)#如果修改過(guò)名字晒奕,名字要統(tǒng)一
? ? ? ? ? ? servicemanager.StartServiceCtrlDispatcher()
except win32service.erroras details:
import winerror
if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
win32serviceutil.HandleCommandLine(zlsService)#如果修改過(guò)名字,名字要統(tǒng)一
2名斟、安裝構(gòu)建環(huán)境需要用的庫(kù)脑慧,在虛擬環(huán)境內(nèi)安裝即可。
pip install PyInstaller==3.4
pip install pywin32==224
3砰盐、編寫(xiě)構(gòu)建bat闷袒,其實(shí)就幾個(gè)cmd命令,但是為了方便調(diào)試岩梳,我還是寫(xiě)成了一個(gè)腳本囊骤。
:: 停止運(yùn)行的服務(wù)
sc stop web_movie
:: 刪除這個(gè)服務(wù)
sc delete web_movie
:: 等個(gè)幾秒鐘晃择,服務(wù)剛停止,文件不會(huì)馬上釋放也物,不然文件不讓刪除
TIMEOUT /T 3
:: 刪除舊版本構(gòu)建的內(nèi)容
rmdir /s/q dist
rmdir /s/q build
del PythonService.spec
:: 構(gòu)建現(xiàn)在的程序藕各,這里有個(gè)坑,由于templates目錄和static目錄和代碼不是直接引用焦除,在pyinstaller構(gòu)建的時(shí)候會(huì)忽略激况,導(dǎo)致服務(wù)無(wú)法運(yùn)行,后面詳細(xì)介紹膘魄。
pyinstaller -F --add-data "templates;templates" --add-data "static;static" PythonService.py
:: 將構(gòu)建好的exe程序安裝到系統(tǒng)服務(wù)
dist\PythonService.exe install
:: 啟動(dòng)服務(wù)
sc start web_movie
4乌逐、這時(shí)候在任務(wù)管理器里面就可以看到已經(jīng)運(yùn)行的服務(wù)了
5、如果不能啟動(dòng)创葡,排查方法
如果啟動(dòng)后立即停止浙踢,或者彈窗說(shuō)“服務(wù)沒(méi)有及時(shí)響應(yīng)啟動(dòng)或控制請(qǐng)求”,這時(shí)候可以查看windows的計(jì)算機(jī)管理界面應(yīng)用日志灿渴,來(lái)看詳細(xì)信息洛波。
6、我遇到的坑
Traceback (most recent call last):
File "site-packages\win32\lib\win32serviceutil.py", line 839, in SvcRun
File "PythonService.py", line 28, in SvcDoRun
File "site-packages\flask\app.py", line 943, in run
File "site-packages\werkzeug\serving.py", line 812, in run_simple
File "site-packages\werkzeug\_reloader.py", line 267, in run_with_reloader
ValueError: signal only works in main thread
上面這個(gè)報(bào)錯(cuò)使我的代碼出現(xiàn)過(guò)得骚露,經(jīng)過(guò)我的各種最小化測(cè)試蹬挤,發(fā)現(xiàn)這個(gè)錯(cuò)誤是加載配置項(xiàng)的時(shí)候就會(huì)出現(xiàn),最終定位到了我的一行配置文件棘幸。
DEBUG =True焰扳,這一行配置讓我排查了整整一天。
7误续、我遇到的坑
修復(fù)了上面的問(wèn)題吨悍,我的的服務(wù)終于起來(lái)了,但是又發(fā)現(xiàn)蹋嵌,在瀏覽器里面竟然無(wú)法訪問(wèn)育瓜。如下提示:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
這個(gè)問(wèn)題也讓我苦惱了很久,在我最小化測(cè)試的時(shí)候栽烂,縮減到僅剩一個(gè)hello world都無(wú)法正常顯示躏仇,但是創(chuàng)建一個(gè)新的flask項(xiàng)目就可以正常顯示,經(jīng)過(guò)我細(xì)心的diff這兩個(gè)項(xiàng)目愕鼓,
發(fā)現(xiàn)我在return的時(shí)候使用了render_template模板钙态,然而這個(gè)目錄在pyinstaller的時(shí)候慧起,并沒(méi)有打包進(jìn)來(lái)菇晃,然后這一切都明白了,就是因?yàn)樵跇?gòu)建的時(shí)候沒(méi)有將這個(gè)目錄添加進(jìn)來(lái)蚓挤,
導(dǎo)致項(xiàng)目啟動(dòng)后無(wú)法使用模板文件磺送。
修復(fù)方法就是bat文件內(nèi)寫(xiě)的驻子,添加上靜態(tài)目錄的編譯就可以了。
pyinstaller -F --add-data "templates;templates" --add-data "static;static" PythonService.py