用python做服務(wù)端時實現(xiàn)守候進程的那些方式
說說,需要做守候進程的時候,我是怎么進化高端的。(怎么高端,具體自己定義,我的土,說不定是你的高端)
python deamon的思路:
1.進程脫離父進程及終端綁定,如果不這樣的話,主進程退出,派生的子進程也跟著倒霉了。脫離終端也是這個理。
2.進程唯一性保證,這是廢話
3.標準輸入/輸出/錯誤重定向,為了不讓錯誤打到前面,又為了更好的分析數(shù)據(jù)。
說的洋氣點、nb點、細化點(其實就os的三個動作):
os.chdir("/") 將當前工作目錄更改為根目錄。從父進程繼承過來的當前工作目錄可能在一個裝配的文件系統(tǒng)中。
os.setsid() 調(diào)用 setsid 以創(chuàng)建一個新對話期,創(chuàng)建了一個獨立于當前會話的進程。
os.umask(0) 重設(shè)文件創(chuàng)建掩碼,子進程會從父進程繼承所有權(quán)限,可以通過調(diào)用這個方法將文件創(chuàng)建掩碼初始化成系統(tǒng)默認。
記得最一開始,用的還是shell的手段,nohup 重定向到一個log文件里面。 具體怎么用,我估計大家都懂。
nohup xxxx xxxx &
緊接著用python的subprocess模塊,來fork daemon進程,然后自己退出來。 這樣也是實現(xiàn)了守候進程。 subprocess 派生了子進程后,他還是可以有效的控制子進程,比如kill,掛起。
- import subprocess
- #xiaorui.cc
- from subprocess import call
- f=open("/dev/null",'r')
- proc=subprocess.Popen(xxx, shell=True,stdout=f,executable='/bin/bash')
- f.close
學(xué)習(xí)python的服務(wù)端一大利器 twisted的時候,他本身也可以做守候進程的。當然方法有些局限,僅僅適合依照twisted為左右的網(wǎng)絡(luò)編程。
- #!/usr/bin/twistd -y
- #xiaorui.cc
- from twisted.application import service, internet
- from twisted.internet import reactor
- import time
- import os,sys
- i=0
- def writedata():
- global i
- i+=1
- a=i
- print 'waiting to write data (%d)'%a
- time.sleep(8)
- print 'writing data!!!! (%d)'%a
- while True:
- time.sleep(0.2)
- aa=time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
- os.system("echo %s >>log"%aa)
- def writeinthread():
- reactor.callInThread(writedata)
- application =service.Application('timeserver')
- tservice = internet.TimerService(10000,writeinthread)
- tservice.setServiceParent(application )
上面介紹了很多的方法,但是不管是python、golang、ruby社區(qū)用supervisor做進程管理的居多。原因,夠簡單,夠直白。 supervisor配置文件是相當?shù)呢S富,他還有supervisorctl 終端管理器,更有web 管理界面 。 對我來說,supervisor tornado 絕配。
這段時間找到了一個好模塊,pip install daemonize
這是我寫的關(guān)于 daemonize demo例子,大家可以直接跑跑。 之后,可以看到,我雖然死循環(huán)了,但是后臺的服務(wù)器還是一直跑著,可以通過進程的狀態(tài),或者是通過daemonize本身的函數(shù)接口獲取狀態(tài)。
- #xiaorui.cc
- from time import sleep
- import os,sys
- from daemonize import Daemonize
- pid = "/tmp/test.pid"
- def wlog():
- f=open('/tmp/nima','a')
- f.write('11')
- f.close()
- def main():
- while True:
- sleep(5)
- wlog()
- daemon = Daemonize(app="test_app", pid=pid, action=main)
- daemon.start()
- daemon.get_pid()
- daemon.is_running()
他的源碼實現(xiàn)方式:
不多說了,就是fork fork fork ....
- # Core modules
- import atexit
- import os
- import sys
- import time
- import signal
- class Daemon(object):
- """
- A generic daemon class.
- Usage: subclass the Daemon class and override the run() method
- """
- def __init__(self, pidfile, stdin=os.devnull,
- stdout=os.devnull, stderr=os.devnull,
- home_dir='.', umask=022, verbose=1):
- self.stdin = stdin
- self.stdout = stdout
- self.stderr = stderr
- self.pidfile = pidfile
- self.home_dir = home_dir
- self.verbose = verbose
- self.umask = umask
- self.daemon_alive = True
- def daemonize(self):
- """
- Do the UNIX double-fork magic, see Stevens' "Advanced
- Programming in the UNIX Environment" for details (ISBN 0201563177)
- http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
- """
- try:
- pid = os.fork()
- if pid > 0:
- # Exit first parent
- sys.exit(0)
- except OSError, e:
- sys.stderr.write(
- "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
- sys.exit(1)
- # Decouple from parent environment
- os.chdir(self.home_dir)
- os.setsid()
- os.umask(self.umask)
- # Do second fork
- try:
- pid = os.fork()
- if pid > 0:
- # Exit from second parent
- sys.exit(0)
- except OSError, e:
- sys.stderr.write(
- "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
- sys.exit(1)
- if sys.platform != 'darwin': # This block breaks on OS X
- # Redirect standard file descriptors
- sys.stdout.flush()
- sys.stderr.flush()
- si = file(self.stdin, 'r')
- so = file(self.stdout, 'a+')
- if self.stderr:
- se = file(self.stderr, 'a+', 0)
- else:
- se = so
- os.dup2(si.fileno(), sys.stdin.fileno())
- os.dup2(so.fileno(), sys.stdout.fileno())
- os.dup2(se.fileno(), sys.stderr.fileno())
- def sigtermhandler(signum, frame):
- self.daemon_alive = False
- signal.signal(signal.SIGTERM, sigtermhandler)
- signal.signal(signal.SIGINT, sigtermhandler)
- if self.verbose >= 1:
- print "Started"
- # Write pidfile
- atexit.register(
- self.delpid) # Make sure pid file is removed if we quit
- pid = str(os.getpid())
- file(self.pidfile, 'w+').write("%s\n" % pid)
- def delpid(self):
- os.remove(self.pidfile)
- def start(self, *args, **kwargs):
- """
- Start the daemon
- """
- if self.verbose >= 1:
- print "Starting..."
- # Check for a pidfile to see if the daemon already runs
- try:
- pf = file(self.pidfile, 'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
- except SystemExit:
- pid = None
- if pid:
- message = "pidfile %s already exists. Is it already running?\n"
- sys.stderr.write(message % self.pidfile)
- sys.exit(1)
- # Start the daemon
- self.daemonize()
- self.run(*args, **kwargs)
- def stop(self):
- """
- Stop the daemon
- """
- if self.verbose >= 1:
- print "Stopping..."
- # Get the pid from the pidfile
- pid = self.get_pid()
- if not pid:
- message = "pidfile %s does not exist. Not running?\n"
- sys.stderr.write(message % self.pidfile)
- # Just to be sure. A ValueError might occur if the PID file is
- # empty but does actually exist
- if os.path.exists(self.pidfile):
- os.remove(self.pidfile)
- return # Not an error in a restart
- # Try killing the daemon process
- try:
- i = 0
- while 1:
- os.kill(pid, signal.SIGTERM)
- time.sleep(0.1)
- i = i + 1
- if i % 10 == 0:
- os.kill(pid, signal.SIGHUP)
- except OSError, err:
- err = str(err)
- if err.find("No such process") > 0:
- if os.path.exists(self.pidfile):
- os.remove(self.pidfile)
- else:
- print str(err)
- sys.exit(1)
- if self.verbose >= 1:
- print "Stopped"
- def restart(self):
- """
- Restart the daemon
- """
- self.stop()
- self.start()
- def get_pid(self):
- try:
- pf = file(self.pidfile, 'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
- except SystemExit:
- pid = None
- return pid
- def is_running(self):
- pid = self.get_pid()
- print(pid)
- return pid and os.path.exists('/proc/%d' % pid)
- def run(self):
- """
- You should override this method when you subclass Daemon.
- It will be called after the process has been
- daemonized by start() or restart().
- """
使用python做守候進程服務(wù),不知道還有沒有更好點、更霸道的方法。大家有的話,要分享下,咱們一塊交流下 ....