2012-10-28 43 views
26

मैं डेमन्स के लिए नया हूं इसलिए माफी मांगना अगर यह एक नौसिखिया सवाल है।मैं पाइथन-डिमन के साथ एक डिमन कैसे स्थापित करूं?

कई अन्य उत्तरों में (उदाहरण के लिए, this question) लोगों ने सुझाव दिया कि python-daemon पैकेज जाने का तरीका था क्योंकि यह PEP 3143 मानक को पूरी तरह कार्यान्वित करता है।

दुर्भाग्यवश, पायथन-डेमन a bit light on documentation (या अधिक संभावना है कि मैं ज्ञान/अनुभव पर थोड़ी सी रोशनी हूं ...;)), और मुझे लगता है कि मुझे शायद कुछ मूलभूत याद आ रही है। यहाँ मैं क्या कर रहा है:

मैं निम्नलिखित:

import daemon 

logfile = open('daemon.log', 'w') 

context = daemon.DaemonContext(stdout = logfile, stderr = logfile) 

context.open() 

with context: 
    do_something_1() 
    do_something_2() 

प्रश्न: मैं कैसे अजगर-डेमॉन के साथ एक डेमॉन की स्थापना की है, मैं यह कैसे शुरू करने और इसे रोकने के कर सकते हैं?


साइड नोट्स:

मैं मूल रूप से के बारे में एक जंगली अनुमान ले रहा हूँ कैसे/.open() विधि यहाँ का उपयोग किया जाए - डॉक्स इस मुद्दे पर वास्तविक स्पष्ट नहीं थे। ऐसा लगता है कि मैं इसे शामिल करता हूं या नहीं।

तो, अब मैं क्या करूँ? जब मैं इस फाइल, जैसे चल रहा है कोशिश:

python startConsumerDaemons.py 

यह do_something_1() चलाने के लिए प्रकट होता है, लेकिन दूसरी नहीं। और, ऐसा लगता है कि प्रोग्राम टर्मिनल विंडो में संलग्न है। आईई, stdout रीडायरेक्ट नहीं किया गया है, और जब मैं टर्मिनल विंडो बंद करता हूं तो प्रक्रिया मारे जाती है। तो, मुझे पूरा यकीन है कि मैं यहाँ कुछ गलत कर रहा हूं ... मुझे अलग-अलग क्या करना चाहिए?

और, आखिरकार, जब मैं डिमन चल रहा हूं, तो मैं इसे कैसे रोक/पुनरारंभ करूं (उदाहरण के लिए यदि मैं अंतर्निहित कोड में परिवर्तन करता हूं)?

+0

क्या पिछले 'with' क्या करना चाहिए है?(जब मॉड्यूल पहले आयात किए जाते हैं तो मॉड्यूल चलाए जाएंगे, जब तक कि मुझे कुछ याद नहीं आ रहा है, उन अंतिम वक्तव्य कुछ भी नहीं करेंगे) और "पहली स्क्रिप्ट चलाने पर दूसरा क्या नहीं" का मतलब क्या है? अगर कोई अपवाद नहीं उठाया गया तो मुझे पूरा भरोसा है कि दोनों सफलतापूर्वक भाग गए। – mgibsonbr

+0

पता नहीं है कि "आखिरी" के साथ आपका क्या मतलब है (केवल एक ही है), लेकिन उदाहरण के लिए पायथन-डिमन के लिए कोड में, वह स्क्रिप्ट को एक कथन के रूप में निष्पादित करने के लिए रखता है जैसे कि (http: // www.python.org/dev/peps/pep-3143/)। पहली स्क्रिप्ट निष्पादित होती है और संदेशों को प्राप्त/संसाधित करने में सक्षम होती है (यह एक एमक्यू ब्रोकर पर उपभोक्ता है)। क्योंकि पहला अनंत लूप पर है, शायद स्टार्टकॉन्सरडेमन्स स्क्रिप्ट कभी दूसरी बार नहीं पहुंचती है? – CQP

+0

आपके द्वारा पोस्ट किया गया उदाहरण ** आयात ** किसी अन्य फ़ाइल ('itial_program_setup', 'do_main_program', आदि) से कुछ विधियां और ** ** उन्हें ** (' do_main_program() ') कहते हैं, उनमें से एक 'के साथ' कथन के भीतर । जब तक कि पाइथन की कुछ अस्पष्ट विशेषता नहीं है, मुझे नहीं पता, मुझे विश्वास है कि ये बयान कुछ भी नहीं करेंगे। वैसे भी, जहां तक ​​मैं समझ सकता था, 'पायथन-डेमन' वर्तमान में निष्पादन प्रोग्राम को यूनिक्स डिमन प्रक्रिया में बदल देता है, यह नई प्रक्रियाओं या नए धागे नहीं बनाता है। यदि कार्यक्रम का एक हिस्सा अनंत लूप में प्रवेश करता है, तो बाद के भाग बिल्कुल नहीं चलेंगे। – mgibsonbr

उत्तर

0

linux पर, आप चलाकर Daemon बंद कर सकते हैं:

$ ps -x 

और पीआईडी ​​है कि आपके डेमॉन से मेल खाती है और फिर बस प्रक्रिया को मारने लगता है।

4

एक पूर्ण उदाहरण available here है।

आपको पाइथन-डेमन के आंतरिक कार्यों को बेहतर ढंग से समझने में सक्षम होना चाहिए।

इसके अलावा प्रदान किया गया कोड भी डेमॉन को शुरू/बंद करने के लिए एक इनिट स्क्रिप्ट का एक उदाहरण देता है। हालांकि, अगर आप शुरू कर सकते हैं/बस तर्क रोकने के साथ फिर से मूल कार्यप्रणाली को कॉल करके इसे रोकने:

python original_func.py stop 
2

आप 'with' statement documentation में देख सकते हैं, यह बयान कुछ 'जादू' प्रदर्शन करता है, जो हमारे उद्देश्य से संबंधित है ।विशेष रूप से:

एक "आइटम" आय के साथ बयान के साथ के निष्पादन के रूप में इस प्रकार है:

  1. संदर्भ अभिव्यक्ति (अभिव्यक्ति with_item में दी गई) एक संदर्भ प्रबंधक प्राप्त करने के लिए मूल्यांकन किया जाता है।

  2. संदर्भ प्रबंधक का __exit__() बाद में उपयोग के लिए लोड किया गया है।

  3. वह संदर्भ प्रबंधक की __enter__() विधि लागू की गई है।

  4. यदि कोई विवरण कथन में शामिल किया गया था, तो __enter__() से वापसी मूल्य इसे सौंपा गया है।

  5. सुइट निष्पादित किया गया है।

  6. संदर्भ प्रबंधक की __exit__() विधि लागू की गई है। अगर किसी अपवाद को सूट से बाहर निकलने का कारण बनता है, तो इसका प्रकार, मान, और ट्रेसबैक __exit__() पर तर्क के रूप में पारित किया जाता है। अन्यथा, तीन कोई नहीं तर्क प्रदान किए जाते हैं।

इसका क्या मतलब है? आप the PEP in question देखें, जो अजगर-डेमॉन प्रलेखन रूप में कार्य करता है (और जो वास्तव में ज्यादा बेहतर हो सकता है) करने के लिए करीब से देखो, तो आप देखेंगे कि यह लागू करता है कि __enter__() और __exit__():

वर्ग भी संदर्भ प्रबंधक को लागू करता है __enter__ और __exit__ विधियों के माध्यम से प्रोटोकॉल।

__enter__()

कॉल उदाहरण के खुले() विधि है, तो उदाहरण के लौटने।

__exit__(exc_type, exc_value, exc_traceback)

कॉल उदाहरण के करीबी() विधि है, तो यह सच है लौटने अगर अपवाद संभाला है या गलत है, तो यह नहीं था।

दूसरे शब्दों में, ओपन() की आवश्यकता नहीं है, पीईपी में दिया गया उदाहरण (हालांकि सही ढंग से समझाया नहीं गया है) जैसा काम करता है। जबकि कथन के साथ कुछ मतलब है, यह किसी भी लूप को नहीं रखता है, एक बार इसके दायरे के अंत तक पहुंचने के बाद, यह से बाहर निकलता है(), जिसमें पायथन-डिमन का अर्थ करीब() है। इसलिए, आपको कुछ समय सही या कभी भी अनंत लूप पर विचार करने की आवश्यकता है।

आपकी दूसरी स्क्रिप्ट के काम पर काम नहीं कर रहा है, मैं वास्तव में आपको नहीं बता सकता, मैं पहले से ही काम करता हूं आश्चर्यचकित हूं। यदि आपका डेमॉन रुक रहा है, तो निश्चित रूप से आपकी स्क्रिप्ट के साथ कोई समस्या है, आप अपने उपभोक्ता डेमोनलॉगफाइल को देख सकते हैं। (एक साइड नोट के रूप में, आपके पास एक टाइपो 'sderr' -> 'stderr' है)

इसके अलावा, आप पीईपी में देख सकते हैं कि यदि निर्दिष्ट नहीं है, तो कार्यशील निर्देशिका संपत्ति '/' पर डिफ़ॉल्ट होती है। यदि आप अपनी स्क्रिप्ट में सापेक्ष पथ का उपयोग कर रहे हैं तो यह आपकी समस्या का स्रोत हो सकता है।

अंत में, आखिरी सवाल के बारे में, आप आसानी से मार सकते हैं आप अपने पीआईडी ​​खोजने डेमॉन कर रहे हैं:

ps ax | grep startConsumerDaemons.py 

और यह एक SIGTERM भेजने:

kill <pid> 

gromain द्वारा प्रदान की उत्तर प्रदान करता है 'daemon.runner()' के साथ इसे शुरू करने और रोकने के लिए एक अधिक आसान तरीका है, लेकिन यह सेटअप के लिए कहीं अधिक जटिल है।

-1

daemon.DaemonContext कन्स्ट्रक्टर lockfile पैरामीटर स्वीकार करता है। लॉकफ़ाइल लाइब्रेरी का उपयोग करें जो प्रक्रिया के पीआईडी ​​को रिकॉर्ड करेगा, जैसे कि lockfile.PIDLockFile

फिर, प्रक्रिया का पीआईडी ​​नामित पीआईडी ​​फ़ाइल की सामग्री को पढ़कर पाया जाता है। अपने चल रहे डिमन को सिग्नल भेजने के लिए उस पीआईडी ​​का प्रयोग करें।

6

मेरे पास यह है, जो मेरे लिए काम करता है। इसमें एक sysv init स्क्रिप्ट भी है। Repo is at GitHub, और मेरे पास अन्य संभावित समाधानों के लिंक के साथ a brief blog post भी है।

केवल एक डेमॉन प्रक्रिया चल रही हो सकती है: यह पीआईडी ​​लॉक फ़ाइल द्वारा प्रबंधित की जाती है, जैसे अधिकांश अन्य लिनक्स डिमन्स। इसे रोकने के लिए, अगर यह चल रहा है कर

kill `cat /var/run/eg_daemon.pid` 

देखने के लिए:

ps -elf | grep `cat /var/run/eg_daemon.pid` 

pidfile submodule का उपयोग करना, पीआईडी ​​फ़ाइल स्वचालित रूप से किया जाता है। जब डिमन बंद हो जाता है, तो पिडफाइल साफ़ हो जाता है। कृपया init स्क्रिप्ट के लिए जुड़े GitHub repo देखें।

यहाँ पायथन डेमॉन कोड है:

#!/usr/bin/env python3.5 
import sys 
import os 
import time 
import argparse 
import logging 
import daemon 
from daemon import pidfile 

debug_p = False 

def do_something(logf): 
    ### This does the "work" of the daemon 

    logger = logging.getLogger('eg_daemon') 
    logger.setLevel(logging.INFO) 

    fh = logging.FileHandler(logf) 
    fh.setLevel(logging.INFO) 

    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 
    formatter = logging.Formatter(formatstr) 

    fh.setFormatter(formatter) 

    logger.addHandler(fh) 

    while True: 
     logger.debug("this is a DEBUG message") 
     logger.info("this is an INFO message") 
     logger.error("this is an ERROR message") 
     time.sleep(5) 


def start_daemon(pidf, logf): 
    ### This launches the daemon in its context 

    ### XXX pidfile is a context 
    with daemon.DaemonContext(
     working_directory='/var/lib/eg_daemon', 
     umask=0o002, 
     pidfile=pidfile.TimeoutPIDLockFile(pidf), 
     ) as context: 
     do_something(logf) 


if __name__ == "__main__": 
    parser = argparse.ArgumentParser(description="Example daemon in Python") 
    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid') 
    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log') 

    args = parser.parse_args() 

    start_daemon(pidf=args.pid_file, logf=args.log_file) 

पूर्णता 'के लिए, यहाँ init स्क्रिप्ट है। ध्यान दें कि "मारना" वास्तव में एक पॉज़िक्स सिग्नल भेजने का एक तरीका है - एक सिंहावलोकन के लिए सिग्नल (7) के लिए मैन पेज देखें। पायथन-डिमन संदर्भ सिग्नल को पकड़ लेगा, प्रक्रिया को फाइल डिस्क्रिप्टर को बंद करने की प्रक्रिया को समाप्त कर देगा, और पीआईडी ​​फाइल को स्वचालित रूप से हटा देगा। तो, यह वास्तव में एक साफ समाप्ति है।

आप डिमन कॉन्फ़िगरेशन के पुनः लोड करने के लिए SIGUSR1 या कुछ समान पकड़ने के लिए अपना कोड लिख सकते हैं। पाइथन को डेमॉन को रोकने के लिए कोई फायदा नहीं है।

#!/bin/bash 
# 
# eg_daemon  Startup script for eg_daemon 
# 
# chkconfig: - 87 12 
# description: eg_daemon is a dummy Python-based daemon 
# config: /etc/eg_daemon/eg_daemon.conf 
# config: /etc/sysconfig/eg_daemon 
# pidfile: /var/run/eg_daemon.pid 
# 
### BEGIN INIT INFO 
# Provides: eg_daemon 
# Required-Start: $local_fs 
# Required-Stop: $local_fs 
# Short-Description: start and stop eg_daemon server 
# Description: eg_daemon is a dummy Python-based daemon 
### END INIT INFO 

# Source function library. 
. /etc/rc.d/init.d/functions 

if [ -f /etc/sysconfig/eg_daemon ]; then 
     . /etc/sysconfig/eg_daemon 
fi 

eg_daemon=/var/lib/eg_daemon/eg_daemon.py 
prog=eg_daemon 
pidfile=${PIDFILE-/var/run/eg_daemon.pid} 
logfile=${LOGFILE-/var/log/eg_daemon.log} 
RETVAL=0 

OPTIONS="" 

start() { 
     echo -n $"Starting $prog: " 

     if [[ -f ${pidfile} ]] ; then 
      pid=$(cat $pidfile ) 
      isrunning=$(ps -elf | grep $pid | grep $prog | grep -v grep) 

      if [[ -n ${isrunning} ]] ; then 
       echo $"$prog already running" 
       return 0 
      fi 
     fi 
     $eg_daemon -p $pidfile -l $logfile $OPTIONS 
     RETVAL=$? 
     [ $RETVAL = 0 ] && success || failure 
     echo 
     return $RETVAL 
} 

stop() { 
    if [[ -f ${pidfile} ]] ; then 
     pid=$(cat $pidfile) 
     isrunning=$(ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}') 

     if [[ ${isrunning} -eq ${pid} ]] ; then 
      echo -n $"Stopping $prog: " 
      kill $pid 
     else 
      echo -n $"Stopping $prog: " 
      success 
     fi 
     RETVAL=$? 
    fi 
    echo 
    return $RETVAL 
} 

reload() { 
    echo -n $"Reloading $prog: " 
    echo 
} 

# See how we were called. 
case "$1" in 
    start) 
    start 
    ;; 
    stop) 
    stop 
    ;; 
    status) 
    status -p $pidfile $eg_daemon 
    RETVAL=$? 
    ;; 
    restart) 
    stop 
    start 
    ;; 
    force-reload|reload) 
    reload 
    ;; 
    *) 
    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}" 
    RETVAL=2 
esac 

exit $RETVAL 
+0

ठीक है, 'मार' के साथ 'स्टॉप' विधि बहुत अच्छी नहीं है ... क्या यह 'python mydaemon.py stop', आदि के साथ संभव नहीं है? इसके अलावा हम आपके उदाहरण को सरल बना सकते हैं ताकि केवल प्रारंभ/बंद डिमन आदि के बारे में प्रश्न का मूल रखा जा सके। – Basj

+0

यह डेमन्स को संभालने का एक मानक लिनक्स तरीका है। कोई इंटरैक्टिव प्रक्रिया नहीं है। संवाद करने का एकमात्र तरीका लिनक्स सिग्नल का उपयोग करेगा। कुछ पायथन विधि का उपयोग करने के लिए मारने के लिए एक कॉल लपेटना होगा। –

0

मॉड्यूल "पायथन-डेमन" मॉड्यूल के लिए एक उपयोगी दस्तावेज़ अभी भी अनुपलब्ध है। मैंने व्यक्तिगत रूप से इसका उपयोग करने के बारे में छोड़ दिया, और अब मैं सफलतापूर्वक सैंडर मरेचल के डिमन कोड referenced in this answer का उपयोग करता हूं।

मैंने python testdaemon.py stop पर कॉल करते समय चीजों को करने में सक्षम होने के लिए इसे थोड़ा संशोधित किया।

Here is the code


नमूना उपयोग:

import sys, daemon, time 

class testdaemon(daemon.Daemon): 
    def run(self): 
     self.i = 0 
     with open('test1.txt', 'w') as f: 
      f.write(str(self.i)) 
     while True: 
      self.i += 1 
      time.sleep(1) 

    def quit(self): 
     with open('test2.txt', 'w') as f: 
      f.write(str(self.i)) 

daemon = testdaemon() 

if 'start' == sys.argv[1]: 
    daemon.start() 
elif 'stop' == sys.argv[1]: 
    daemon.stop() 
elif 'restart' == sys.argv[1]: 
    daemon.restart()