为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Python


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

python deamon的思路:

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

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

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

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

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

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

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

记得最一开始,用的还是shell的手段,nohup 重定向到一个log文件里面。  具体怎么用,我估计大家都懂。

nohup xxxx  xxxx &

紧接着用python的subprocess模块,来fork daemon进程,然后自己退出来。  这样也是实现了守候进程。 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

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

原文:http://rfyiamcool.blog.51cto.com/1030776/1424809

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

wKioL1OXLQ-RQcz9AAYYBVybewQ020.jpg

他的源码实现方式:  

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

©著作权归作者所有:来自51CTO博客作者rfyiamcool的原创作品,谢绝转载,否则将追究法律责任

python 守候进程python daemonpython forkpython应用


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消