在用python做服务端时实现守候进程的那些事

嗯哪,说说需要做守候进程的时候,我是怎么进化到高端的。(怎么高端,具体自己定义,我的土,说不定就是你妹的高端)

最土的,也是最基本的python deamon的思路:

1.进程脱离父进程及终端绑定,如果不这样的话,主进程退出,派生的子进程也跟着倒霉了。脱离终端也是这个理。

2.进程唯一性保证,这是废话

3.标准输入/输出/错误重定向,为了不让错误打到前面,又为了更好的分析数据。

说的洋气点、nb点、细化点(其实就os的关键三个动作):

os.chdir(“/”) 将当前工作目录更改为根目录。从父进程继承过来的当前工作目录可能在一个装配的文件系统中。

os.setsid() 调用 setsid 以创建一个新对话期,创建了一个独立于当前会话的进程。

os.umask(0) 重设文件创建掩码,子进程会从父进程继承所有权限,可以通过调用这个方法将文件创建掩码初始化成系统默认。

哎,提示下我的原文地址是, blog.xiaorui.cc

记得刚做运维那会,要实现售后进程用的还是shell的手段,nohup 重定向到一个log文件里面。 具体怎么用,我估计大家都懂,别问我,我怕我干掉你。

nohup xxxx xxxx &

紧接着用python的subprocess模块,来fork daemon进程,然后自己退出来。 这样也是实现了守候进程。 subprocess 派生了子进程后。由于用subprocess可以拿到他的子进程的pid,这样他就可以有效的控制子进程,比如kill,挂起。

import subprocess#xiaorui.ccfrom subprocess import callf=open("/dev/null",'r')proc=subprocess.Popen(xxx, shell=True,stdout=f,executable='/bin/bash')f.close

然后的然后,在学习python的服务端一大利器 twisted的时候,他本身也可以做守候进程的。当然方法有些局限,仅仅适合依照twisted为左右的网络编程。

#!/usr/bin/twistd -y#xiaorui.ccfrom twisted.application import service, internetfrom twisted.internet import reactorimport timeimport os,sysi=0def 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社区用supervisor做进程管理的居多。原因,够简单,够直白。 supervisor配置文件是相当的丰富,他还有supervisorctl 终端管理器,更有web 管理界面 。 对我来说,supervisor tornado 绝配。

这段时间找到了一个好模块,pip installdaemonize

这是我写的关于 daemonize demo例子,大家可以直接跑跑。 之后,可以看到,我虽然死循环了,但是后台的服务器还是一直跑着,可以通过进程的状态,或者是通过daemonize本身的函数接口获取状态。

#xiaorui.ccfrom time import sleepimport os,sysfrom daemonize import Daemonizepid = "/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()

他的源码实现方式:

不多说了,就是fork fork fork ….

import atexitimport osimport sysimport timeimport signalclass 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做守候进程服务,不知道还有没有更好点、更霸道的方法。大家有的话,要分享下,咱们一块交流下 ….

在用python做服务端时实现守候进程的那些事

相关文章:

你感兴趣的文章:

标签云: