Ich habe ein Python Skript geschrieben, das eine bestimmte E-Mail-Adresse überprüft und neue E-Mails an ein externes Programm weiterleitet. Wie kann ich erreichen, dass dieses Skript rund um die Uhr ausgeführt wird, z. B. als Daemon oder als Dienst unter Linux? Benötige ich auch eine Schleife, die niemals im Programm endet, oder kann dies einfach dadurch geschehen, dass der Code mehrmals ausgeführt wird?
Hier haben Sie zwei Möglichkeiten.
Erstellen Sie einen richtigen Cron-Job , der Ihr Skript aufruft. Cron ist ein gebräuchlicher Name für einen GNU/Linux-Daemon, der Skripte regelmäßig nach einem von Ihnen festgelegten Zeitplan startet. Sie fügen Ihr Skript einer Crontab hinzu oder platzieren einen Symlink dazu in einem speziellen Verzeichnis, und der Dämon übernimmt den Start im Hintergrund. Sie können lesen Sie mehr bei Wikipedia. Es gibt verschiedene Cron-Daemons, aber auf Ihrem GNU/Linux-System sollte es bereits installiert sein.
Verwenden Sie eine Art Python-Ansatz (z. B. eine Bibliothek), damit sich Ihr Skript selbst dämonisieren kann. Ja, es ist eine einfache Ereignisschleife erforderlich (bei der Ihre Ereignisse durch einen Timer ausgelöst werden, möglicherweise durch die Sleep-Funktion).
Ich würde Ihnen nicht empfehlen, 2. zu wählen, da Sie in der Tat die Cron-Funktionalität wiederholen würden. Das Linux-System-Paradigma besteht darin, mehrere einfache Tools interagieren zu lassen und Ihre Probleme zu lösen. Wählen Sie den anderen Ansatz, es sei denn, es gibt zusätzliche Gründe, warum Sie einen Daemon erstellen sollten (zusätzlich zum regelmäßigen Auslösen).
Wenn Sie daemonize mit einer Schleife verwenden und ein Absturz auftritt, wird die E-Mail danach von niemandem geprüft (wie von Ivan Nevostruev in den Kommentaren zu this answer). Wenn das Skript als Cron-Job hinzugefügt wird, wird es nur erneut ausgelöst.
Hier ist eine Nice-Klasse, die aus hier stammt:
#!/usr/bin/env python
import sys, os, time, atexit
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
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("/")
os.setsid()
os.umask(0)
# 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)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = file(self.stdin, 'r')
so = file(self.stdout, 'a+')
se = file(self.stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
file(self.pidfile,'w+').write("%s\n" % pid)
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""
Start the daemon
"""
# 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
if pid:
message = "pidfile %s already exist. Daemon already running?\n"
sys.stderr.write(message % self.pidfile)
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pf = file(self.pidfile,'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if not pid:
message = "pidfile %s does not exist. Daemon not running?\n"
sys.stderr.write(message % self.pidfile)
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, SIGTERM)
time.sleep(0.1)
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)
def restart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
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().
"""
Sie sollten die Python-Daemon Bibliothek benutzen, sie kümmert sich um alles.
Aus PyPI: Bibliothek zum Implementieren eines anständigen Unix-Daemon-Prozesses.
Sie können fork () verwenden, um Ihr Skript vom tty zu trennen und weiter ausführen zu lassen.
import os, sys
fpid = os.fork()
if fpid!=0:
# Running as daemon now. PID is fpid
sys.exit(0)
Natürlich müssen Sie auch eine Endlosschleife implementieren, z
while 1:
do_your_check()
sleep(5)
Hoffe, Sie haben damit angefangen.
Sie können das Skript python als Dienst ausführen lassen, indem Sie ein Shell-Skript verwenden. Erstellen Sie zunächst ein Shell-Skript, um das Skript python auszuführen (scriptname beliebiger Name )
#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &
erstellen Sie jetzt eine Datei in /etc/init.d/scriptname
#! /bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to Shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting feedparser"
start_daemon -p $PIDFILE $DAEMON
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping feedparser"
killproc -p $PIDFILE $DAEMON
PID=`ps x |grep feed | head -1 | awk '{print $1}'`
kill -9 $PID
log_end_msg $?
;;
force-reload|restart)
$0 stop
$0 start
;;
status)
status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
Jetzt können Sie Ihr python Skript mit dem Befehl /etc/init.d/scriptname starten oder stoppen.
cron
ist eindeutig eine gute Wahl für viele Zwecke. Es wird jedoch kein Dienst oder Dämon erstellt, wie Sie es im OP angefordert haben. cron
führt Jobs nur in regelmäßigen Abständen (dh der Job startet und stoppt) und nicht öfter als einmal pro Minute aus. Es gibt Probleme mit cron
. Wenn beispielsweise eine frühere Instanz Ihres Skripts beim nächsten Start des Zeitplans für cron
noch ausgeführt wird und eine neue Instanz gestartet wird, ist das in Ordnung? cron
verarbeitet keine Abhängigkeiten. Es wird nur versucht, einen Job zu starten, wenn der Zeitplan dies vorsieht.
Wenn Sie eine Situation finden, in der Sie wirklich einen Daemon benötigen (einen Prozess, der nie aufhört zu laufen), schauen Sie sich supervisord
an. Es bietet eine einfache Möglichkeit, ein normales, nicht dämonisiertes Skript oder Programm zu verpacken und es wie einen Dämon zu betreiben. Dies ist ein viel besserer Weg, als einen nativen Python Daemon zu erstellen.
Ein einfaches und unterstütztes version ist Daemonize
.
Installieren Sie es von Python Package Index (PyPI):
$ pip install daemonize
und dann wie folgt verwenden:
...
import os, sys
from daemonize import Daemonize
...
def main()
# your code here
if __== '__main__':
myname=os.path.basename(sys.argv[0])
pidfile='/tmp/%s' % myname # any name
daemon = Daemonize(app=myname,pid=pidfile, action=main)
daemon.start()
wie wäre es mit $Nohup
Befehl unter Linux?
Ich verwende es, um meine Befehle auf meinem Bluehost-Server auszuführen.
Bitte raten Sie mir, wenn ich falsch liege.
Wenn Sie Terminal (ssh oder so) verwenden und ein Langzeitskript nach dem Abmelden vom Terminal weiterarbeiten möchten, können Sie Folgendes versuchen:
screen
apt-get install screen
erstelle ein virtuelles Terminal (nämlich abc): screen -dmS abc
jetzt verbinden wir uns mit abc: screen -r abc
Nun können wir python script: python Keep_sending_mail.py
von nun an können Sie Ihr Terminal direkt schließen. Das Skript python wird jedoch weiter ausgeführt, anstatt heruntergefahren zu werden
Da die PID dieses
Keep_sending_mail.py
Zum virtuellen Bildschirm und nicht zum Terminal (ssh) gehört
Wenn Sie den Status Ihres Skripts überprüfen möchten, können Sie erneut screen -r abc
Verwenden
Lesen Sie sich zuerst die Mail-Aliase durch. Ein Mail-Alias erledigt dies im Mail-System, ohne dass Sie mit Daemons, Diensten oder Ähnlichem herumalbern müssen.
Sie können ein einfaches Skript schreiben, das von sendmail jedes Mal ausgeführt wird, wenn eine E-Mail-Nachricht an ein bestimmtes Postfach gesendet wird.
Siehe http://www.feep.net/sendmail/tutorial/intro/aliases.html
Wenn Sie wirklich einen unnötig komplexen Server schreiben möchten, können Sie dies tun.
Nohup python myscript.py &
Das ist alles was es braucht. Ihr Skript wird einfach wiederholt und in den Ruhezustand versetzt.
import time
def do_the_work():
# one round of polling -- checking email, whatever.
while True:
time.sleep( 600 ) # 10 min.
try:
do_the_work()
except:
pass
Verwenden Sie den Service Manager, den Ihr System anbietet - zum Beispiel unter Ubuntu use pstart. Dadurch werden alle Details für Sie verarbeitet, z. B. Start beim Booten, Neustart beim Absturz usw.
Ich würde diese Lösung empfehlen. Sie müssen die Methode run
erben und überschreiben.
import sys
import os
from signal import SIGTERM
from abc import ABCMeta, abstractmethod
class Daemon(object):
__metaclass__ = ABCMeta
def __init__(self, pidfile):
self._pidfile = pidfile
@abstractmethod
def run(self):
pass
def _daemonize(self):
# decouple threads
pid = os.fork()
# stop first thread
if pid > 0:
sys.exit(0)
# write pid into a pidfile
with open(self._pidfile, 'w') as f:
print >> f, os.getpid()
def start(self):
# if daemon is started throw an error
if os.path.exists(self._pidfile):
raise Exception("Daemon is already started")
# create and switch to daemon thread
self._daemonize()
# run the body of the daemon
self.run()
def stop(self):
# check the pidfile existing
if os.path.exists(self._pidfile):
# read pid from the file
with open(self._pidfile, 'r') as f:
pid = int(f.read().strip())
# remove the pidfile
os.remove(self._pidfile)
# kill daemon
os.kill(pid, SIGTERM)
else:
raise Exception("Daemon is not started")
def restart(self):
self.stop()
self.start()
um etwas zu erstellen, das wie ein Dienst läuft, können Sie folgendes verwenden:
Als Erstes müssen Sie das Cement Framework installieren: Cement Frame Work ist ein CLI-Framework, auf dem Sie Ihre Anwendung bereitstellen können.
kommandozeilenoberfläche der App:
interface.py
from cement.core.foundation import CementApp
from cement.core.controller import CementBaseController, expose
from YourApp import yourApp
class Meta:
label = 'base'
description = "your application description"
arguments = [
(['-r' , '--run'],
dict(action='store_true', help='Run your application')),
(['-v', '--version'],
dict(action='version', version="Your app version")),
]
(['-s', '--stop'],
dict(action='store_true', help="Stop your application")),
]
@expose(hide=True)
def default(self):
if self.app.pargs.run:
#Start to running the your app from there !
YourApp.yourApp()
if self.app.pargs.stop:
#Stop your application
YourApp.yourApp.stop()
class App(CementApp):
class Meta:
label = 'Uptime'
base_controller = 'base'
handlers = [MyBaseController]
with App() as app:
app.run()
YourApp.py Klasse:
import threading
class yourApp:
def __init__:
self.loger = log_exception.exception_loger()
thread = threading.Thread(target=self.start, args=())
thread.daemon = True
thread.start()
def start(self):
#Do every thing you want
pass
def stop(self):
#Do some things to stop your application
Denken Sie daran, dass Ihre App auf einem Thread ausgeführt werden muss, um ein Daemon zu sein
Um die App auszuführen, führen Sie dies einfach in der Befehlszeile aus
python interface.py --hilfe