PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PHP Resourcen-Zugriff verteilen


rotalever
2008-06-21, 00:59:50
Ich hab schon wieder mal eine Frage zu PHP und habe bisher ergebnislos gesucht, vll. auch weil mir einfach das richtige Stichwort fehlt..
Und zwar: Ich habe eine Ressource, auf die ich nur alle X Sekunden zugreifen darf/kann. Wenn ein paar Clients die Webseiten besuchen, werden PHP-Skripte ausgeführt, die möglicherweise auf diese Ressource zugreifen müssen. Das Problem ist allerdings, woher soll ein Skript wissen, ob es zugreifen darf oder vll. noch ein bisschen warten muss. Hier bräuchte ich jetzt irgendein Programm, dass die Ressource zuteilt. Im Grunde genommen so eine Art Warteschleife. Wenn ein Skript Zugriff auf die Ressource benötigt, dann trägt es sich in die Warteschleife ein. Ein Unabhängiger Prozess würde nun die Warteschleife abarbeiten indem dem am längsten enthaltenen Skript die Ressource zugeteilt wird, dann wartet der Prozess X Sekunden usw.

Da stellt sich für mich allerdings die Frage wie man sowas am besten realisiert. Zunächst hatte ich einfach gedacht, ich nehme eine Datenbank Tabelle und simuliere damit ein Warteschleife. Also Anfrage auf Ressource stellen ist dann eine Tabellenzeile hinzufügen, wenn dann alle vorherigen vom unabhängigen Prozess abgearbeitet worden sind, kommt man dran. Da gibt es aber immer noch Probleme. Wie teile ich dem Skript zum Beispiel mit, dass es jetzt auf die Ressource zugreifen darf? Und besonders schnell wäre das ganze mit Sicherheit wohl nicht, da dabei die Datenbank stark belastet wird.

Gibt es für soetwas nicht eine einfache Lösung?

Was mir noch einfällt: Das Skript teilt einem unabhängigen Verteil-Prozess mit, dass es die Ressource braucht (wie es das tut, ist dann noch eine andere Frage..). Der Verteiler weiß, wie viele Skript-Instanzen bereits vor diesem jetzigen diese Anfrage gestellt haben, indem ein einfacher Counter gespeichert wird. Somit kann der Verteiler dem Skript antworten, dass es die Ressource in Counter*X Sekunden nutzen darf. Das wäre bestimmt um einiges schneller, nur wie sollen diese Programme untereinander kommunizieren?

PatkIllA
2008-06-21, 08:33:04
Was ist denn das für eine Ressource und welche Zugriffsmöglichkeiten hast du auf dem Rechner?
Webserver und die Ressource liegen auf dem gleichen Rechner?

darph
2008-06-21, 09:16:39
Ein Unabhängiger Prozess würde nun die Warteschleife abarbeiten indem dem am längsten enthaltenen Skript die Ressource zugeteilt wird, dann wartet der Prozess X Sekunden usw.
PHP semmelt alle Scripte nach einer gewissen Laufzeit einfach ab. Standard sind meines Wissens 30 Sekunden. Läuft ein Script dann noch, wird's eben abgewürgt. Den Wert kann man zwar ändern, aber trotzdem ist sowas für Warteschlangen gänzlich ungeeignet. Nach 2 Minuten oder so bricht der Browser dann wohl auch von selbst ab.

Wäre es da nicht möglich, zu schauen, ob die Resource verfügbar ist und falls nicht, dem User mitzuteilen, er möge es doch später (in ungefähr x Sekunden) nochmal versuchen?

rotalever
2008-06-21, 12:48:08
Bei der Ressource handelt es sich um eine externe API-Schnittstelle, die über HTTP erreicht wird.
Ich gehe nicht davon aus, dass es lange Wartezeiten geben wird, da dafür dann ja schon sehr viele Anfragen parallel eingehen müssten und ich auch einiges Cachen werde. Trotzdem könnte es ja sein, dass mal ein paar User gleichzeitig drauf zugreifen müssen, für den Fall ist diese Warteschlange dann da.
Wenn der User länger als 5 Sekunden warten muss, dann läuft sowieso etwas schief, da wird dann ausgegeben, er solle es später noch einmal versuchen, der automatischen Skriptabruch sollte also nicht so das Hindernis darstellen.

Dem User direkt zu sagen, dass die Ressource nicht verfügbar ist, halte ich nicht für Sinnvoll, lieber lasse ich ihn erstmal 5 Sekunden warten.

Gast
2008-06-21, 21:05:26
Ich kenn zwar PHP nicht näher, aber ich nehm mal an du suchst sowas wie Mutex Objekte oder Semaphoren:

http://in2.php.net/sem

Kinman
2008-06-23, 08:27:00
Du könntest im Script mittels Polling die Daten aus der DB abfragen und sobalds frei ist, weitermachen


$bRun = true;
do
{
$status = datenbankAbfrage();
if ($status == FREI) $bRun = false;
else usleep(500000); //0,5 Sekunden warten
}
while($bRun);

//weitermachen

Gast
2008-06-23, 08:52:07
$bRun = true;
do
{
$status = datenbankAbfrage();
if ($status == FREI) $bRun = false;
else usleep(500000); //0,5 Sekunden warten
}
while($bRun);
[/php]
Sieht nach ner schönen Race-Condition aus.

Monger
2008-06-23, 09:25:49
Das Queuen von irgendwelchen Operationen sollte eigentlich die Datenzugriffsschicht übernehmen, sprich: in aller Regel die Datenbank selbst.

Und wenn du es selber implementieren musst, sieht das in aller Regel so aus:

- Einfrage kommt rein
- Server eröffnet einen neuen Worker Thread für die Anfrage
- Anfrage wird ausgewertet
- Operation wird über eine sychronisierte Methode abgearbeitet

Das warten wird hier also auf den Server verlagert, und nicht etwa einfach die Anfrage des Clients abgewürgt. Das wäre sicherlich ungeschickt.

Kinman
2008-06-23, 14:46:45
Sieht nach ner schönen Race-Condition aus.

Kommt drauf an, ob Informationen geändert werden oder nicht. Wenn nicht, dann ist das ganz normales Polling (http://de.wikipedia.org/wiki/Polling_%28Informatik%29)

mfg Kinman

rotalever
2008-06-23, 17:22:37
@Kinman:
Für den schlechtesten Fall von 5 Sekunden muss ich 10 Mal die Datenbank abfragen, damit kann man wohl noch leben. Das Setzen der Variable $status in der Datenbank müsste dann allerdings durch eine Operation geschehen, die nicht unterbrochen wird, sonst wird es spaßig. Zudem ist hier nicht wirklich eine Warteschleife gegeben, wo länger wartende einen Vorteil haben. Ob das allerdings praxisrelevant ist, lässt sich sicher streiten.

@Monger:
Wie soll das Queuen die Datenzugriffsschicht übernehmen? Es handelt sich dabei um eine externe Verbindung ins Internet.
Ansonsten verstehe ich nicht so ganz, was du da meinst. Warum brauche ich da spezielle Worker-Threads?

rotalever
2008-06-27, 13:20:06
Da ich nichts besseres finde und es sehr einfach zu implementieren ist, werde ich jetzt doch ein polling verwenden. Und zwar folgendermaßen:

Wenn ein Script Zugriff auf die Ressource haben möchte, dann holte es sich die Semaphor ID über sem_get(). Anschließend wartet es mit sem_acquire() bis die Ressource frei ist. Wenn die Ressource dann frei ist, macht das Script die entsprechenden Anfragen an die API, wartet X Sekunden und gibt den Semaphore dann wieder wieder frei.

Das sollte wahrscheinlich ganz gut funktionieren, vor allem ohne viel Programmieraufwand :rolleyes:

Das einzige Problem was sich dabei ergibt ist, dass sem_aquire() auf unbestimmte Zeit das Script blockt, solange der Semaphore nicht frei ist. Das könnte dann aber bedeuten, dass der User beliebig lange warten muss. Leider kann man bei sem_aquire() keine Zeit angeben, die höchstens gewartet werden soll, bis es abbricht. Gibt es da für PHP eine Lösung?

Ansonsten müsste ich mir überlegen, was ich dann mache. Vll. eine schicke AJAX Oberfläche, mit der das Script aufgerufen wird, dann bricht das javascript Clientseitig ab:biggrin:


edit: So das hier ist wohl besser:

Script das die ganze Zeit im Hintergrund läuft:

<?php
$shmid = shmop_open(ftok("bla","t"), "c", 0666, 1);
while (True)
{
sleep(1);
shmop_write($shmid, "0",0); // alle 1 Sekunde die Ressource freigeben ("0")
}
shmop_close($shmid);
?>


Frontend Script(e) die auf die Ressource zugreifen wollen:

<?php
$ok = False;
$semid = sem_get(0, 1, 0666, 1);
if ($semid !== False)
{
if(sem_acquire($semid))
{
$shmid = shmop_open(ftok("bla","t"), "c", 0666, 1);
while (shmop_read($shmid, 0, 1) == "1") usleep (50000);
shmop_write($shmid, "1",0);
shmop_close($shmid);
$ok = True;
}
sem_release($semid);
}
if ($ok) ;// auf Ressource zugreifen
?>

robobimbo
2008-06-28, 09:43:15
ich denke du solltest auch das script das im hintergrund läuft mittels einer semaphore auf den shared memory zugreifen lassen - sonst läufst du auch hier in eine racecondition

rotalever
2008-06-28, 11:33:22
ich denke du solltest auch das script das im hintergrund läuft mittels einer semaphore auf den shared memory zugreifen lassen - sonst läufst du auch hier in eine racecondition
Das würde wohl nicht gehen. Da das User-Script (so nenne ich es mal) den Semaphore blockiert und darauf wartet, dass das Shared Memory auf 0 geht (die while-schleife). Das Background-Script hätte so keine Möglichkeit mehr an den Speicher dranzukommen, da das User-Script den Semaphore blockiert.

Ich denke, dass es so, wie es jetzt ist, ganz gut funktioniert, da im Shared-Memory ja nur ein eniziges Byte verändert wird, das sollte doch wohl eine atomare Operation sein. Das Background-Script schreibt ja die "0" auch nicht abhängig von dem Inhalt des Shared-Memory, sondern einfach alle 1 Sekunde.

Natürlich kann es hier passieren, dass der Abstand zwischen zwei API-Zugriffen gegen 0 Sekunden geht. Im Durchschnitt sind aber nur 1 Zugriff pro Sekunde möglich, da das Shared Memory nur alle 1 Sekunde freigeschaltet wird, und ein gleichzeitiger Zugriff im User-Script auf das Shared Memory durch den Semaphore nicht möglich ist.

robobimbo
2008-06-28, 21:33:43
das nennt sich "ostrich-algorithm" :)

auch ne möglichkeit - aber sharedmemoryaccess ist ein syscall, und der ist rel. langsam.

eigent würd ja reichen: semaphore setzen - speicherzugruff - semaphore freigeben. damit bist dann 1000% sicher, das es keine racecondition gibt

rotalever
2008-06-28, 21:53:21
eigent würd ja reichen: semaphore setzen - speicherzugruff - semaphore freigeben. damit bist dann 1000% sicher, das es keine racecondition gibt
Das geht ja nicht, wie ich gerade beschrieben habe. Da der Semaphore bereits blockiert ist.

So langsam ist Shared Memory aber auch nicht.. Ich habs grad mal gebenchmarked:

$max = 100000;
$shmid = shmop_open(ftok("/var/www/test.php","t"), "c", 0666, 1);
$time = microtime(true);
for ($i = 0; $i < $max; $i++)
{
shmop_write($shmid, "0",0);
$x = shmop_read($shmid, 0, 1);
}
echo microtime(true)-$time,"<br>";
shmop_close($shmid);

=> 0.709084033966 s für 2*100000 Zugriffe


Ich glaube übrigens immer noch daran, dass mein Algorithmus richtig ist ;)

rotalever
2008-07-03, 16:52:36
--hier stand müll--