PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Archive for the ‘dienst’ tag

Ein PHP-Script als Windows-Dienst starten

with 38 comments

23dienstViele wichtige Dinge weltweit laufen permanent und tun endlos ihren Job. Webserver warten auf Besucher, Berechnungsprogramme rechnen Tag und Nacht, FTP-Server warten auf Datenübertragungen und SSH-Server warten auf Benutzer.

Auch mit PHP kann man all solche Dinge lösen. Es gibt bereits DNS-Server geschrieben in PHP, selbst ein Webserver in PHP ist verfügbar, ein Continuous Integration Server, einen FTP Server, und es gibt zig tausend Chat-Server (auf Socket-Basis, keine einfachen Webchats), die mit PHP erstellt wurden usw.

Wenn man plant, ein PHP-Script quasi permanent laufen zu lassen, gibt es 3 Möglichkeiten:

  1. Man startet das Script einmalig nach dem Rechnerstart und lässt es in einer Endlosschleife (while (true) { ) laufen
    • Linux: Cron Eintrag @reboot
    • Windows: Geplanter Task bei Rechnerstart
  2. Wenn das Script beispielsweise 2 Minuten für seine Arbeit braucht, startet man es alle 3 Minuten
    • Linux: Cron Eintrag  */3 * * * *
    • Windows: Geplanter Task alle 3 Minuten
  3. Man installiert das Script als Dienst und lässt es darüber automatisch laufen und kann es starten/stoppen
    • Linux: mit Hilfe der Runlevel-Scripte (rc.d / init.d etc)
    • Windows: Windows-Dienste

Hier will ich besonders auf die Windows-Dienste eingehen. Unter Windows ist das die einzige Art, ein PHP-Script beim System-Shutdown kontrolliert beenden zu lassen, denn PHP beherrscht unter Windows keine Signalverarbeitung. Es kann also keine Betriebssystem-Signale empfangen wie zB SIGTERM oder SIGHUP, wohingehen es unter Linux die PHP-Funktionen PCNTL zur Prozesskontrolle gibt. Ohne diese Signale läuft das PHP-Script also solange, bis es vom Betriebssystem gekillt wird (hart, also mitten im Ablauf irgendwo im Code), was natürlich große Probleme bereiten kann bei komplizierten Scripten.

Ein Dienst löst also dieses Problem, denn wenn ein Betriebssystem herunterfährt, gibt es seinen Diensten die Möglichkeit, sich selbst innerhalb einiger Sekunden zu beenden. Dazu muß das PHP-Script natürlich ab und zu in einen definierten Zustand gelangen, wo es „aussteigen“ kann.

Vielleicht sieht man es besser am Code:

while (WIN32_SERVICE_CONTROL_STOP != win32_get_last_control_message()) {
	// hier kommt der Code, der ausgeführt werden soll
}

Es gibt also eine Funktion win32_get_last_control_message(), die dem Script sagt, ob es sich beenden soll oder nicht. Damit diese Funktion (und einige weitere) zur Verfügung stehen, benötigt man die extention win32service aus der PECL.

Aber wie installiere ich nun dieses PHP-Script als Dienst? Dazu gibt es auch eine Funktion. Hier der Grundaufbau eines jeden Services:

if ($argv[1] == 'install') {
	$x = win32_create_service(array(
		'service' => 'My_first_PHP_Service',
		'display' => 'My PHP Service',
		'params' => __FILE__ . ' run',
	));
	debug_zval_dump($x);
	exit;
} else if ($argv[1] == 'uninstall') {
	$x = win32_delete_service('My_first_PHP_Service');
	debug_zval_dump($x);
	exit;
} else if ($argv[1] != 'run') {
	die("bogus args, please use install/uninstall/run");
}

$x = win32_start_service_ctrl_dispatcher('My_first_PHP_Service');

while (WIN32_SERVICE_CONTROL_STOP != win32_get_last_control_message()) {

	// here comes the code which will be executed
	// it should not last longer than 30sec if possible
	$someCode = new SomeCode();
	$someCode->start();

	usleep(500000);
}

Wir haben oben erstmal einige Zeilen, um den Dienst installieren und deinstallieren zu können. Das geht sehr einfach, man benötigt nur einen eindeutigen internen Dienstnamen (hier My_first_PHP_Service) und im Installationsfall eine Zeichenkette, die dann später angezeigt wird.

Aufgerufen mit dem Parameter „install“ wird der Dienst also installiert:service1

Dann können wir ihn starten, entweder von der Konsole oder aus der mmc:

service4

service3

Wie bereits als Kommentar geschrieben, sollte der Code in der Schleife nicht all zu lange laufen, damit der Dienst noch vernünftig gesteuert werden kann. Sollte der Code beispielsweise 5 Minuten laufen, und man versucht den Dienst zu beenden („net stop My_First_PHP_Service“ oder über die mmc), kommt nach ca. einer Minute die Nachricht:

service5

Windows wartet also nicht ewig darauf, dass sich der Dienst beendet. Im Falle des System-Shutdowns wird der Prozess dann zwangsweise hart gekillt, was wieder zu unserem Grundproblem führt. Zur Not muss man einfach die Arbeit in kleine Häppchen unterteilen und nacheinander aufrufen.

Die Deinstallation, ihr ahnt es schon, ist genauso einfach wie die Installation:

service2

Hier gibts noch einige Worte zur win32service extension vom Entwickler selbst:

Man kann natürlich nicht nur „normale“ Scripte bauen und als Dienst laufen lassen, man kann auch feine Dinge machen, indem man einen wirklichen „Dienst“ anbietet, der auf einem Port lauscht und zu dem man sich verbinden kann! Hier gibts Informationen zu Sockets unter PHP, und auch bald einen Artikel hier im Blog.

Falls ihr eure PHP-Scripte als Windows-Dienste laufen habt, würde mich interessieren, was diese Scripte so tun!

Written by Michael Kliewe

August 14th, 2009 at 7:53 pm

Posted in PHP

Tagged with , , , ,