在用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.cc
from subprocess import call
f=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.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社区用supervisor做进程管理的居多。原因,够简单,够直白。  supervisor配置文件是相当的丰富,他还有supervisorctl 终端管理器,更有web 管理界面 。   对我来说,supervisor tornado 绝配。

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

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

#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()

他的源码实现方式:  

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

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


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注