Autor | Zpráva | ||
---|---|---|---|
Numero1990 Profil |
Zdravím,
mám následující problém. Na serveru mám nárazově stovky návštěv za minutu a potřebuji naprogramovat následující věc: Kvůli tomu, že nejsem na moc výkonném serveru a nemůžu si dovolit platit za VPS, se musím obejít bez databáze (při takových návštěvách mi MySQL server po chvíli vrací - "MySQL server has gone away." - avšak všechny dotazy do DB mám optimalizované, už jsem to tu řešil). Potřebuji, aby se po zavolání skriptu otevřel soubor, přečetl se, lehce rozparsoval, pozměnil nějaké hodnoty a opět uložil. V nejlepším případě, aby se žádná data neztratila a ostatní instance skriptu čekaly, než doběhne ten předchozí. Příklad: V souboru budu mít posloupnost čísel. Úplně na začátku to bude "0 0 0 0 0". Když se zavolá skript, otevře se tento soubor, rozparsuje se a provede se jistý výpočet. Uloží se třeba "0 3 9 2 5". Potřeboval bych (pokud to je možné), aby pokud se spustí skript vícekrát, ostatní čekaly, než doběhne předchozí atp. Každé spuštění skriptu mi vygeneruje novou posloupnost z té předchozí a pokud se jednou zapíše něco špatně (výpočet posloupnosti je ovlivňován vnějšími vlivy (předané parametry, aj)), chyba se bude šířit lavinově a ovlivní mi všechny další výpočty. Je tohle možné v PHP nějak udělat? Díky. |
||
Alphard Profil |
#2 · Zasláno: 6. 7. 2012, 00:40:31
Koukal jste třeba na http://doc.nette.org/cs/atomicity nebo jiné hotové třídy, které řeší atomicitu?
|
||
Numero1990 Profil |
#3 · Zasláno: 6. 7. 2012, 01:33:37
Našel jsem si tohle, jen si nejsem jist, jestli to jde použít bez Nette.
Zajímá mě použítí pro můj případ, pokud chci otevřít soubor a on je právě čten, vrátí mi fopen FALSE? $file = fopen('safe://data.txt', 'r+'); Díky. |
||
Majkl578 Profil |
#4 · Zasláno: 6. 7. 2012, 01:36:39
Numero1990:
„jestli to jde použít bez Nette“ Lze. Zrovna tato třída nemá žádné závislosti na dalších částech Nette. „Zajímá mě použítí pro můj případ, pokud chci otevřít soubor a on je právě čten, vrátí mi fopen FALSE?“ FALSE by vracet neměl, měl by vracet normálně resource . Registroval jsi předtím protokol safe:// pomocí Nette\Utils\SafeStream::register() ?
|
||
Numero1990 Profil |
Proč mi tento kód funguje (pro zjednodušení jsem nezahrnul složitý výpočet)?
Zkoušel jsem fread() i fgets(), ani jedna mi nic nenačte. index.php <?php $clock = microtime(TRUE); require_once './SafeStream.php'; Nette\Utils\SafeStream::register(); echo '<pre>'; $handler = fopen('safe://data.txt', 'r+'); //sleep(5); $data = fgets($handler); var_dump($data); $numbers = explode(' ', $data); $numbers[0] = ((int) $numbers[0]) + 1; var_dump($numbers); fwrite($handler, implode(' ', $numbers)); fclose($handler); echo '</pre>'; echo '<div style="position: fixed; right: 10px; bottom: 10px;">'.((microtime(TRUE) - $clock) * 1000).' ms</div>'; ?> Výstup: bool(false) array(1) { [0]=> int(1) } data.txt 0 0 0 0 0 Tak problém je v módu 'r+', při 'r' vše funguje. Soubor data.txt se uzamkne a nedovolí mi ho číst (??? WTF). Jak to vyřešit? |
||
Majkl578 Profil |
#6 · Zasláno: 6. 7. 2012, 04:14:28
|
||
Numero1990 Profil |
#7 · Zasláno: 6. 7. 2012, 10:39:01
$clock = microtime(TRUE); require_once './SafeStream.php'; Nette\Utils\SafeStream::register(); echo '<pre>'; $handler = fopen('safe://data.txt', 'r+'); fseek($handler, 0); sleep(5); $data = fread($handler, 8192); var_dump($data); $numbers = explode(' ', $data); $numbers[0] = ((int) $numbers[0]) + 1; var_dump(implode(' ', $numbers)); fseek($handler, 0); fwrite($handler, implode(' ', $numbers)); fclose($handler); echo '</pre>'; echo '<div style="position: fixed; right: 10px; bottom: 10px;">'.((microtime(TRUE) - $clock) * 1000).' ms</div>'; Takhle bych si představoval ten zápis, ale nefunguje to tak, jak bych potřeboval. Spustím-li 2 procesy, tak se stane toto: 1) 1. proces si otevře soubor a načte "0 0 0 0 0" a soubor uzamkne. 2) 2. proces si otevře soubor a načte "0 0 0 0 0" a soubor uzamkne. (!) 3) 1. proces chce uložit "1 0 0 0 0", ale selže: Warning: rename(E:\development\www\sandbox\fread-fwrite-atomicity\data.txt~~0.53670409946458.tmp,E:\development\www\sandbox\fread-fwrite-atomicity\data.txt) [function.rename]: Přístup byl odepřen. (code: 5) in E:\development\www\sandbox\fread-fwrite-atomicity\SafeStream.php on line 194 4) 2. proces chce uložit "1 0 0 0 0" a jemu se to povede. (!) Já bych to potřeboval právě že opačně, aby další procesy čekaly, dokud neskončí ten předchozí. |
||
Numero1990 Profil |
Mezi voláním file_exist() a následném vytváření může jiný proces soubor vytvořit, proto mě napadlo ho vytvořit a zkontrolovat, jestli funkce nevrátila error.
Tak mě napadlo toto: 1) Jakmile se spustí skript, zavolá se mkdir('lock') (podle testu je vytvoření a mazání složky rychlejší než vytvořit soubor, zavřít ho a smazat). 2) Zkontroluji poslední error přes error_get_last() - pokud složka už existovala, uspím se přes usleep na náhodně vygenerovaný čas (v rozmezí 10 - 50 ms) - tím zajistím, že nebude tolik docházet ke kolizím. Tohle bude v cyklu. 3) Pokud k erroru nedošlo, znamená to, že můj proces může soubor otevřít. Otevřu ho, přečtu, zparsuju, provedu, co potřebuji, uložím a složku smažu. Myslíte, že by to mohlo fungovat? Edit:// Tak nakonec jsem musel použít soubory, se složkami byl problém, že se někdy nesmazaly, a tak tam lock zůstal na furt. Výsledek je takový a funguje to docela dobře. <?php $clock = microtime(TRUE); require_once './SafeStream.php'; Nette\Utils\SafeStream::register(); while(TRUE) { $handler2 = fopen('safe://lock', 'x'); if($handler2 !== FALSE) { sleep(2); echo '<pre>'; $handler = fopen('safe://data.txt', 'r+'); fseek($handler, 0); $data = fread($handler, 8192); var_dump($data); $numbers = explode(' ', $data); $index = mt_rand(0, sizeof($numbers) - 1); $numbers[$index] = ((int) $numbers[$index]) + 1; var_dump(implode(' ', $numbers)); fseek($handler, 0); fwrite($handler, implode(' ', $numbers)); fclose($handler); echo '</pre>'; fclose($handler2); unlink('lock'); break; } else usleep(mt_rand(10000, 50000)); } echo '<div style="position: fixed; right: 10px; bottom: 10px;">'.((microtime(TRUE) - $clock) * 1000).' ms</div>'; ?> |
||
Časová prodleva: 11 let
|
0