一個簡單的需求,即定時啟動python腳本犹褒,這種需求很常見,比如定時啟動一段程序?qū)Ψ?wù)器狀態(tài)進行收集弛针,寫到文件中叠骑,方便運維后期審計,查看服務(wù)器占用高峰時間段削茁,從而判斷出公司產(chǎn)品在該時間段較多人使用宙枷,或定時清除其他程序的日志,釋放線上服務(wù)器的空間茧跋,這塊常見的架構(gòu)是有個轉(zhuǎn)存程序慰丛,將日志通過nginx文件服務(wù)掛起,然后該程序請求這種文件瘾杭,將其存儲在數(shù)據(jù)服務(wù)器中诅病,而線上服務(wù)器的日志就不需要了(游戲日志通常比較大堡牡,所以轉(zhuǎn)存程序也需要設(shè)計一下)房揭。
本章主要來實現(xiàn)一下定時啟動python的需求拄丰,當然吟孙,定時啟動其他任何程序也都一樣。
Python threading模塊
一開始芥永,為了省事篡殷,直接使用python的threading模塊,threading模塊下有個Timer模塊埋涧,它可以實現(xiàn)定時啟動python程序的需求板辽,用法如下:
from threading import Timer
def timedTask():
? ?print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
? ?main() #主題程序邏輯
? ?global timer
? ?timer = Timer(300,timedTask)
? ?timer.start()
if __name__ == '__main__':
? ?timedTask()
值得一提的是,timer需要使用global timer飞袋,據(jù)說嘗試運行時戳气,會釋放無需使用的占用資源。
實現(xiàn)方法很簡單巧鸭,即創(chuàng)建Timer()實例瓶您,傳入兩個參數(shù),分別是時間間隔(單位為秒)與定時任務(wù)本身纲仍,構(gòu)成一個死遞歸(因為沒有逃出條件)呀袱,然后就是調(diào)用Timer實例的start()方法。
不推薦郑叠,雖然網(wǎng)上博客說使用global timer會釋放無用資源夜赵,但實際沒有考證,這種寫法在服務(wù)器上跑起來的程序通常一天就斷乡革,我周日啟動該程序寇僧,周一來公司看,對應(yīng)的python程序掛了沸版。
APScheduler
APScheduler是Python用于執(zhí)行定時操作的第三方框架嘁傀,作為一個框架,它就有它對應(yīng)的各種概念视粮,沒必要搞那么復(fù)雜细办,學(xué)習(xí)成本有點高,放棄
Linux crontab
最總還是轉(zhuǎn)到了Linux的crontab服務(wù)蕾殴,該服務(wù)主要就是用于實現(xiàn)定時任務(wù)的笑撞,其語法如下:
# .---------------- minute (0 - 59)
# | ?.------------- hour (0 - 23)
# | ?| ?.---------- day of month (1 - 31)
# | ?| ?| ?.------- month (1 - 12) OR jan,feb,mar,apr ...
# | ?| ?| ?| ?.---- day of week (0 - 6) (Sunday=0 or 7) ?OR
#sun,mon,tue,wed,thu,fri,sat
# | ?| ?| ?| ?|
# * ?* ?* ?* ?* ?command to be executed
minute:代表一小時內(nèi)的第幾分,范圍 0-59钓觉。
hour:代表一天中的第幾小時茴肥,范圍 0-23。
mday:代表一個月中的第幾天荡灾,范圍 1-31炉爆。
month:代表一年中第幾個月堕虹,范圍 1-12。
wday:代表星期幾芬首,范圍 0-7 (0及7都是星期天)。
who:要使用什么身份執(zhí)行該指令逼裆,當您使用 crontab -e 時郁稍,不必加此字段。
command:所要執(zhí)行的指令胜宇。
crontab服務(wù)狀態(tài)
sudo service crond start ? ? #啟動服務(wù)
sudo service crond stop ? ? ?#關(guān)閉服務(wù)
sudo service crond restart ? #重啟服務(wù)
sudo service crond reload ? ?#重新載入配置
sudo service crond status ? ?#查看服務(wù)狀態(tài)
查看定時任務(wù)
crontab -l
到這里耀怜,關(guān)于crontab常見的文件就是叫你使用 crontab-e
來編寫對應(yīng)crontab配置文件,配置內(nèi)容的語法如上桐愉,例子如下:
# 每天早上6點
0 6 * * * echo "Good morning." >> /tmp/test.txt //注意單純echo财破,從屏幕上看不到任何輸出,因為cron把任何輸出都email到root的信箱了从诲。
# 每兩個小時
0 */2 * * * echo "Have a break now." >> /tmp/test.txt ?
# 晚上11點到早上8點之間每兩個小時和早上八點
0 23-7/2左痢,8 * * * echo "Have a good dream" >> /tmp/test.txt
但這邊不會這樣操作,這種寫法并不適合于真正的工作中系洛,就是一個Toy俊性,我希望的是全自動化,這里通過shell腳本來實現(xiàn)自動添加crontab任務(wù)描扯。
shell腳本代碼如下:
#! /bin/bash
cd LogSys/
work_path=/x1_game/agent_flask/LogSys/
if [ ! -n "$1" ]; then
? ?echo "Usages: sh startLog.sh [start|stop]"
? ?exit 0
fi
if [ "$1" = start ] ;then
? ?if [ ! -x logs ] ;then
? ? ? ?mkdir logs
? ?fi
? ?if [ -f logs/logsys.ini ] ;then
? ? ? ?echo "logsys task is in crontab, can not add the task to crontab agent!"
? ? ? ?exit 0
? ?else
? ? ? ?echo $(date "+%Y_%m_%d") >> logs/logsys.ini
? ? ? ?chmod 777 start.sh
? ? ? ?echo "* * * * * ${work_path}start.sh start >> ${work_path}logs/cron.log 2>&1" >> /var/spool/cron/root
? ? ? ?echo "add LogSys task to crontab"
? ?fi
elif [ "$1" = stop ] ;then
? ?if [ -f logs/logsys.ini ] ;then
? ? ? ?rm -rf logs/logsys.ini
? ? ? ?rm -rf /var/spool/cron/root
? ? ? ?echo "Stop success and remove the logsys.ini"
? ?else
? ? ? ?echo "logsys is not running"
? ?fi
fi
這是我使用的完整shell腳本定页,這里自動添加crontab任務(wù)的命令只有一行,就是 echo"* * * * * ${work_path}start.sh start >> ${work_path}logs/cron.log 2>&1">>/var/spool/cron/root
绽诚,這個命令會每分鐘都會調(diào)用start.sh腳本典徊,而start.sh腳本中啟動了python,幾個坑需要注意恩够,crontab中請使用絕對路徑卒落,因為crontab啟動程序時,相對路徑所對應(yīng)的坐標系其實與你手動啟動該腳本時是不同的玫鸟,使用絕對路徑省事导绷,這里還將star.sh腳本的輸出內(nèi)容都重定向到對應(yīng)的日志文件中。
為什么不直接通過crontab啟動python程序呢屎飘?而是要再繞一層妥曲,通過shell腳本來啟動,這其實也是一個坑钦购,除非你是單python文件檐盟,不然通常都使用shell腳本的形式啟動python,而不在直接使用crontab來啟動押桃,這同樣是因為crontab啟動的任務(wù)相對路徑的坐標系改變了葵萎,多文件的python項目相互引入文件時,使用的坐標系與crontab啟動時不同,導(dǎo)致crontab直接啟動python項目會失敗羡忘,所以技巧就在于谎痢,通過shell腳本來啟動python程序,在啟動前卷雕,通過cd命令進入python項目對應(yīng)的目錄节猿,這樣就將啟動時的相對路徑的坐標系改成與python的一致了
具體可以看一下我的start.sh腳本,代碼如下:
#! /bin/bash
currTime=$(date "+%Y_%m_%d")
logfile=${currTime}_logsys.log
if [ ! -n "$1" ]; then
? ?echo "Usages: sh start.sh [start|stop]"
? ?exit 0
fi
if [ ! -x logs ] ;then
? ?mkdir logs
fi
pid=`ps -ef | grep log2mysql | grep -v grep|awk '{print $2}'`
if [ "$1" = start ] ;then
? ?if [ -n "$pid" ] ;then
? ? ? ?echo "log2mysql is running, can not start"
? ? ? ?exit 0
? ?fi
? ?cd /x1_game/agent_flask/LogSys/
? ?nohup /usr/local/bin/python -u log2mysql.py >> logs/$logfile 2>&1 &
? ?pid=`ps -ef | grep log2mysql | grep -v grep|awk '{print $2}'`
? ?echo "start log2mysql, pid is:"$pid
elif [ "$1" = stop ] ;then
? ?if [ -n "$pid" ] ;then
? ? ? ?echo "kill log2mysql, pid is: "$pid
? ? ? ?kill -9 $pid
? ?else
? ? ? ?echo "log2mysql is not running "$pid
? ?fi
fi
通過python啟動任務(wù)的關(guān)鍵命令在于
cd /x1_game/agent_flask/LogSys/
nohup /usr/local/bin/python -u log2mysql.py >> logs/$logfile 2>&1 &
首先會進入要啟動python項目的所在目錄漫雕,然后再通過python啟動對應(yīng)的py文件滨嘱,這里使用python解釋器同樣要使用全路徑,因為線上系統(tǒng)中存在多個python浸间,因為該python程序是耗時程序太雨,所以我希望它在后臺運行,所以使用了 nohup
與 &
關(guān)鍵字魁蒜,將其放在后臺去運行囊扳。
題外話:centos系統(tǒng)中的yum是依賴python的,具體到centos6梅惯,其yum依賴系統(tǒng)本身就存在的python2.6宪拥,但開發(fā)環(huán)境通常要使用python2.7,此時最好不要刪除系統(tǒng)中自帶的python2.6铣减,如果你直接刪除她君,會導(dǎo)致yum使用不了,此時就需要修改一下yum對應(yīng)文件中的python指向葫哗,最好的方法就是直接安裝python2.7缔刹,然后在/usr/bin下創(chuàng)建對應(yīng)的軟連接來使用。
小結(jié)
python程序員在工作中其實不能只會python劣针,因為python雖然強大校镐,但也會有其缺陷,所以什么好用捺典,用什么才是對的鸟廓,還有python是一種語言,不要被語言局限襟己。