ugrás a tartalomhoz

Nagy terhelésű rendszerek fejlesztése 2.

janoszen · 2010. Május. 25. (K), 20.23
Nagy terhelésű rendszerek fejlesztése 2.

Mielőtt a bevezető cikk után rátérnénk a konkrét load balancing és clusterező megoldásokra, szeretnék egy jelentéktelennek tűnő, ám de annál hasznosabb eszközt bemutatni. Hölgyeim és uraim, konzolokat elővenni, debuggereket bekapcsolni, háttérfolyamatokat írunk.

Bár sokan állítják, hogy webes nyelv, és ezért nem alkalmas daemonok, háttérfolyamatok kezelésére, ezúttal PHP-ban fogunk tevékenykedni. Ahhoz, hogy a példákat ki tudd próbálni, szükséged lesz egy sockets, pcntl és posix kiegészítőkkel lefordított PHP-ra, valamint egy Linux konzolra. Ha esetleg nem lenne ilyen kéznél, tölts le egy Ubuntu Linuxot, és telepítsd föl mondjuk Microsoft Virtual PC-be. (Macesek: az OS X konzolja feltehetőleg megfelel a célnak, pusztán a PHP-t kell telepíteni.)

Előre felhívom mindenki szíves figyelmét, hogy az itt leírt technikákat lehetőleg ne a céges vagy egyetemi szerveren próbálgassa! Én szóltam!

A Linux processz modell

Mivel alapvető igény egy háttérfolyamatnál, hogy több feladatot is el tudjon végezni egyszerre, ismerkedjünk meg a processzekkel. Amióta léteznek multitasking operációs rendszerek (és ez már nem ma kezdődött), ezek tudnak a feladatok között váltani. A feladatok különálló, úgynevezett virtuális memóriatérrel rendelkeznek, ami a mindenkori elvi maximális memória mérete (32 biten ez 4 GB). Ezen a memóriaterületben dolgozhatnak a folyamatok.

Mellékesen megjegyezném, hogy ennek a memóriának nem kell rendelkezésre állnia, hiszen az operációs rendszer képes az éppen nem használt lapokat (pages) kirakni a merevlemezre. Ezt a folyamatot hívjuk swapelésnek vagy page-elésnek.

Visszatérve a folyamatokra: a POSIX (Unix) világban a folyamatoknak van szülőfolyamata. Ha egy processz szeretne egy másikat indítani, azt csak úgy tudja, hogy saját magából „klónoz” egyet, azaz kettéválik, és mind a két fél rendelkezni fog a virtuális memória egy-egy másolatával és valamennyi megnyitott fájlleíróval. Ezt a folyamatot hívjuk forkolásnak.

A harmadik számunkra fontos aspektus a signalok kérdése. A processzek küldözgethetnek egymásnak signalokat, és el is kaphatják azokat. Különösen fontos, hogy amennyiben egy gyermekfolyamat terminál, a szülőfolyamat automatikusan kap egy SIGCLD signalt, avagy egy értesítést. A gyermekfolyamat mindaddig úgynevezett zombi állapotban marad, amíg a szülő le nem kezeli a signalt.

Signalok a gyakorlatban

Hogy ennek a sok elméletnek adjunk egy kis gyakorlati alapot is, lássunk egy példakódot signal handlingre:

#!/usr/bin/php
<?php

/**
 * PHP 4.3.0 ota csak igy mennek a signalok. Az
 * interpreter nem szakitja meg kozvetlenul a futast,
 * hanem N parancsonkent megnezi, volt-e signal.
 */
declare(ticks = 1);

/**
 * Ez lesz a signal handlerunk.
 */
function sig_handler($signo) {
    echo('Caught signal: ' . $signo . "\n");
}

/**
 * Beallitjuk, hogy a SIGINT-et kapjuk el.
 */
pcntl_signal(SIGINT, 'sig_handler');

/* Csinalunk valamit, amit meg lehet szakítani. */
while (1) {
    echo('Sleeping for one second...\n');
    sleep(1);
}

echo('Terminating...\n');

/* Rendes exit koddal lepunk ki */
exit(0);

Ahhoz, hogy ezt a programot el tudjuk indítani, chmod-dal rá kell tennünk az execute jogosultságot, például így:

chmod 0750 signaldemo.php

Ha magát a kódot elindítjuk, tapasztalni fogjuk, hogy Ctrl+C-vel nem lehet kilépni belőle. Ez annak köszönhető, hogy az említett billentyűkombináció a SIGINT signalt küldi, amit az alkalmazásunk elkap.

Ha már ennyit beszéltünk róluk, nézzünk egy listát a fontosabb signalokról:

  • ALRM (alarm): Azt jelzi, hogy egy előre beállított időzítő lejárt. Ezt használhatjuk aszinkron időzítésre.
  • HUP (hangup): Azt jelzi, hogy a a szülőfolyamat bezárta a terminált. Ha nem kapjuk el, a program bezárásához vezet. Egyébként a modemes időkből származik.
  • INT (interrupt): Megszakítás, Ctrl+C. Ha nem kapjuk el, a program bezárásához vezet.
  • TERM (terminate): Megkéri a programot, hogy fejezze be a működését. Elkapva van lehetőségünk befejezni az aktuális munkát.
  • KILL: A program azonnali bezárását eredményezi, nem lehet elkapni.
  • CHLD (child): Egy gyerekprocessz terminált, és várja a feldolgozást.

A részletes lista megtalálható a kill parancs kézikönyv oldalán vagy a PHP doksiban. (A kézikönyvet a man kill parancs kiadásával lehet megtekinteni.)

Forkoljunk

Na, tisztába tettük a signalokat, forkoljunk. (Alkalomadtán ellőhetjük a gyengus „Ne fhorkoljál már…” poént is.)

A fork parancs PHP-ban pcntl_fork() néven fut. Mint már említettük, a teljes memóriaterület másolásra kerül, tehát a nyitva hagyott állományleírókat, adatbázis kapcsolatokat stb. célszerű forkolás előtt vagy azt követően a keletkező gyerekprocesszben bezárni. (Attól, hogy a gyerekben bezárjuk a szülőben még nyitva marad a kapcsolat.)

A fork parancs úgy működik, hogy visszaad egy int típusú változót, ami a következő értékeket veheti fel:

  • -1: ha nem sikerült a fork. Ennek oka lehet pl. az, hogy elfogytak a folyamatazonosítók (lásd: fork bomb).
  • 0: sikerült a fork, a gyermekfolyamatban vagyunk.
  • >0: sikerült a fork, a szülőfolyamatban vagyuk. A visszakapott érték a gyermekfolyamat PID-je (process id) lesz. Ezzel tudunk rá hivatkozni pl. egy esetleges kill parancsnál.

A zombi folyamatok kezelésére két lehetőség van. Vagy használjuk a pcntl_waitpid() parancsot, és megadjuk paraméterül a konkrét PID-et amit várunk, vagy használjuk signal handlerben a pcntl_wait() parancsot. Ez abban az esetben hasznos, ha több gyereket indítunk, és nem tudjuk melyik fog előbb terminálni. Fontos figyelembe venni, hogy egy SIGCLD signal jelentheti több gyermekfolyamat terminálását is, ezt le kell kezelnünk.

Ezzel a tudással fölfegyverkezve írjunk egy olyan programot, ami elindít tíz gyermeket, a gyermekek pedig végrehajtanak öt feladatot.

#!/usr/bin/php
<?php

/**
 * PHP 4.3.0 ota csak igy mennek a signalok. Az
 * interpreter nem szakitja meg kozvetlenul a futast,
 * hanem N parancsonkent megnezi, volt-e signal.
 */
declare(ticks = 1);

$maxchildren = 5;
$children    = array();
$terminate   = false;
$parent      = true;

/**
 * Allitsuk be a signal handlereket a syscallok ujrainditasaval
 */
pcntl_signal(SIGTERM, 'handle_term', true);
pcntl_signal(SIGCLD, 'handle_child', true);

/**
 * Irjuk ki az uzeneteket a PID-jukkel egyutt
 */
function logMessage($message) {
    echo('[' . posix_getpid() . '] ' . $message . "\n");
}

/**
 * Kezeljuk a gyerekek terminalasat
 */
function handle_child($signo) {
    global $children;
    
    /**
     * Kerjuk le a terminalt gyerekek PID-jeit.
     * A WNOHANG azt jelenti, hogy ha nincs gyerek,
     * akkor ne varakozzon.
     */
    while (($pid = pcntl_wait($status, WNOHANG)) > 0) {
        unset($children[$pid]);
        logMessage('Child terminated: ' . $pid);
        logMessage('Current active workers: ' . count($children));
    }
}

/**
 * Kezeljuk le a TERM signalt.
 */
function handle_term($signo) {
    global $terminate;
    
    logMessage("Caught SIGTERM...");
    
    /**
     * Mivel a szkriptnyelvek inkabb a kollaborativ multitaskingot
     * tamogatjak, csak beallitjuk a flaget, es megvarjuk hogy a
     * fociklus kezelje a helyzetet.
     */
    $terminate = true;
}

/**
 * Fuggveny a gyerekprocesszek munkajahoz.
 */
function work() {
    global $terminate;
    
    /**
     * Varialjuk egy kicsit a feladatok szamat...
     */
     
    $maxtask = rand(2, 10);
    
    for ($i = 0; $i < $maxtask; $i++) {
        if (!$terminate) {
            logMessage('Executing task ' . $i . '...');
            sleep(1);
        } else {
            logMessage('Got termination request!');
            break;
        }
    }
}

/**
 * Iteraljunk a vegtelensegig ha a szulofolyamatban vagyunk,
 * es nem erkezett terminalasi keres.
 */
while (!$terminate && $parent) {
    logMessage('Current active workers: ' . count($children));

    /**
     * Ha szukseg van uj gyermekekre a szuloben...
     */
    while(!$terminate && $parent && (count($children) < $maxchildren)) {
        $pid = pcntl_fork();
        
        if ($pid == -1) {
            logMessage('Fork failed!');
        } else if ($pid == 0) {
            $parent = false;
            
            logMessage('Worker started...');
            work();
            logMessage('Worker finished...');
        } else {
            $children[$pid] = $pid;
            
            logMessage('Child ' . $pid . ' started...');
            logMessage('Current active workers: ' . count($children));
        }
    }

    if (!$terminate && $parent) {
        sleep(5);
    }
}

if ($parent) {
    /**
    * Minden meg futo gyereknek szoljunk, hogy
    * legyen kedves terminalni.
    */
    
    logMessage('Terminating ' . count($children) . ' children...');
    
    foreach ($children as $child) {
        logMessage('Terminating ' . $child);
        
        posix_kill($child, SIGTERM);
        
        /**
         * Megadjuk a tickelheto fuggvenyhivast...
         */
        sleep(2);
    }

    /**
    * Ha meg van futo gyerek akkor most mar erelyesen
    * szoljunk ra.
    */
    
    logMessage('Killing ' . count($children) . ' children...');
    
    foreach ($children as $child) {
        logMessage('Killing ' . $child);
        posix_kill($child, SIGKILL);
    }
}

exit(0);

Ha ezt elindítjuk, látható hogy a szülőfolyamat mindig gondoskodik arról, hogy legyen megfelelő mennyiségű worker a feladatok elvégzéséhez. SIGTERM-re értesíti a gyerekeket, és mindegyiknek ad két másodpercet hogy termináljon. (Ez a ticks működés miatt szükséges.)

A program működését a ps af paranccsal ellenőrizhetjük:

 5086 pts/1    Ss     0:00 /bin/bash
17763 pts/1    S+     0:00  \_ /usr/bin/php ./forkdemo.php
17768 pts/1    S+     0:00      \_ /usr/bin/php ./forkdemo.php
17771 pts/1    S+     0:00      \_ /usr/bin/php ./forkdemo.php
17777 pts/1    S+     0:00      \_ /usr/bin/php ./forkdemo.php
17778 pts/1    S+     0:00      \_ /usr/bin/php ./forkdemo.php
17779 pts/1    S+     0:00      \_ /usr/bin/php ./forkdemo.php

Csatlakozzunk le a terminálról

Ez eddig szép és jó, de megfigyelhető volt az, hogy a terminálon nyitva maradt a programunk. Ha becsukjuk a futtató terminált, megy a levesbe az egész. Mivel ez nem cél, tanuljunk meg lecsatlakozni a processzt nyitó terminálról. Miután a lecsatlakozás után már nem fog rendelkezésre állni az STDOUT, amire írhatnánk az üzeneteket, először is át kell írnunk a logMessage() függvényünket, hogy syslogot használjon:

openlog('php', LOG_NDELAY | LOG_PID, LOG_USER);
function logMessage($message) {
    syslog(LOG_DEBUG, $message);
}

Most, hogy ezt megugrottuk, nézzük a feladat lényegi megoldását: először is forkolunk egyet, majd a szülőben terminálunk, a gyerekben pedig megkérjük az operációs rendszert, hogy mi lehessünk az ún. session leader, azaz legyen saját munkamenetünk. Ezt követően pedig becsukjuk az STDIN és STDOUT állományleírókat. A gyakorlatban mi sem egyszerűbb:

#!/usr/bin/php
<?php

openlog('php', LOG_NDELAY | LOG_PID, LOG_USER);

function logMessage($message) {
    syslog(LOG_DEBUG, $message);
}

/**
 * Forkoljunk
 */
 
$pid = pcntl_fork();

if ($pid == -1) {
    logMessage('Detach failed!');
    exit(1);
} else if ($pid > 0) {
    exit(0);
}

/**
 * Kerjuk el a session ID-t
 */
 
$session_id = posix_setsid();

if (!$session_id) {
    logMessage('Detach failed!');
    exit(1);
}

/**
 * Lepjunk ki a konyvtarbol hogy ne fogjuk azt
 */
if (!chdir('/')) {
    logMessage('Detach failed!');
    exit(1);
}

/**
 * Zarjuk be az allomanyleirokat
 */
 
$fh_std = array(STDIN, STDOUT);

foreach ($fh_std as $fh) {
    if (!fclose($fh)) {
        logMessage('Detach failed!');
        exit(1);
    }
}

sleep(120);

exit(0);

Ha most bezárjuk a terminált, és egy másikon megnézzük a ps af parancsot, látni fogjuk, hogy a programunk ott maradt a háttérben. Ezzel kikerültük az olyan hackeket, mint a screen-ben való indítgatás és társai.

Socket-kezelés

Ahhoz, hogy tudjunk a külvilággal beszélgetni, több megoldást is választhatunk. Olvashatunk adatbázisból, értekezhetünk signalokon keresztül, de ha adatot szeretnénk mozgatni, akkor az egyetlen működőképes technika a socket kezelés. A socket az, amivel többek között hálózati adatforgalmat is lehet bonyolítani, de ugyanolyan jól lehet file socketen keresztül beszélgetni egy másik folyamattal.

Szerveroldalon a socket-kezelésnek a következő fázisai vannak:

A mintapéldánkban egy nagyon egyszerű és teljesítmény szempontjából nagyon rossz webszervert fogunk írni, ami minden kérésre 404-et ad vissza. Annyit fog csinálni, hogy minden beérkező kapcsolatra forkol egyet, majd annak kiszolgálása után kilép. Mivel a fork egy erőforrás-igényes művelet, ezt élesben nem ajánlanám.

#!/usr/bin/php

<?php

declare(ticks = 1);

$parent   = true;
$children = array();

pcntl_signal(SIGCLD, 'handle_child', true);

function handle_child($signo) {
    global $children;
    
    while (($pid = pcntl_wait($status, WNOHANG)) > 0) {
        unset($children[$pid]);
    }
}

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_bind($sock, '127.0.0.1', 8081);
socket_listen($sock);

while ($parent) {
    /**
     * Blokkolva varakozzunk addig, amig nem kapcsolodik valaki.
     */
     
    $client = socket_accept($sock);
    $pid    = pcntl_fork();
    
    if ($pid == -1) {
        /**
         * Zarjuk be a kapcsolodo kliens socketet.
         * Mivel nincs gyerek, ezert connection resetet fog kapni.
         */
        socket_close($client);
    } else if ($pid == 0) {
        $parent = false;
        $data   = '';
        
        /**
         * Olvassunk addig, amig ket ujsort nem kapunk
         */
        while (strpos($data, "\r\n\r\n") == false) {
            $data .= socket_read($client, 1024);
        }
        
        /**
         * Irjuk ki, hogy nem nyert.
         */
        socket_write($client, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nNot Found');
        socket_close($client);
    } else {
        $children[$pid] = $pid;
        
        /**
         * A szuloben zarjuk be a kapcsolodo kliens socketet
         */
        socket_close($client);
    }
}

/**
 * Zarjuk le a listen socketet.
 */
 
socket_close($sock);

És akkor most tessék szépen bepötyögni a böngészőbe: http://localhost:8081/, és lehet csodálkozni. Alig 5 perc alatt írtunk egy – igaz távolról sem tökéletes, de – webszerverként funkcionáló valamit.

Összegzés

Remélem, a fenti ízelítő jelleggel bemutatott technikákkal sikerült egy kicsit felkeltenem a szunnyadó érdeklődést a rendszer mélyebb rétegei iránt. Némi időráfordítással a PHP is nagyon hasznos háttérfeldolgozó nyelv és hasznos bajtárs tud lenni a nagy rendszerek adatfeldolgozásában.

Ha szeretnél még játszani a fentiekkel, akkor házi feladatnak próbáld meg a bemutatott technikákat egy jól működő HTTP szerverré összegyúrni. Esetleg írj róla cikket, kommentet.

 
janoszen arcképe
janoszen
Pásztor János a fejlesztés és az üzemeltetés témakörével is foglalkozik, igyekszik az egyikben szerzett tapasztalatokat a másikba is átültetni. A Weblaboron kívül Facebookon ,Twitteren, a Refaktor Magazinon és a Refactor Zone-on publikál.
1

Resource-ok

Ifju · 2010. Május. 26. (Sze), 08.24
Gratula a cikkhez!

A resource-kezelés viselkedéséről érdemes lett volna írni még egy keveset, mert forkolás esetén az összes resource használatban marad a child és a parent processben is. Viszont, ha lezárjuk bármelyik processben a resource-t, akkor a másik processben is bezárásra kerül. Ezzel én főleg adatbáziskapcsolat-kezelés kapcsán találkoztam eddig.
2

C

janoszen · 2010. Május. 26. (Sze), 09.02
Köszönöm a kiegészítést, ezt nem tudtam. Ez mondjuk betudható annak hogy ilyesmit eddig inkább C-ben írtam meg.
3

Ez mennyit gyorsít?

szaky · 2010. Május. 26. (Sze), 14.20
Csak kíváncsi vagyok, van e tapasztalat, hogy ez a párhuzamosítás mennyivel gyorsítja egy nagy terheltségű szerver (ahol tizedmásodpercenként esnek be a kérések) kiszolgálási idejét? Az hogy démonokat írunk, az jó, és hatékony dolog lehet (bár mondjuk a cron és screen helyett alkalmazva gondolom inkább csak szebb, könnyebben kezelhető mintsem gyorsabb, de fenntartom a tévedés jogát), de ezenfelül hol lehet hatékony a párhuzamosítás? Esetleg akkor azt tudom elképzelni hogy ilyen módszerrel a gép szabad kapacitását maximálisan ki tudjuk használni (pl mikor csökken a terhelés (éjszaka) a maintenanceszerű feladatokhoz)
4

Válaszolok is

szaky · 2010. Május. 26. (Sze), 14.42
Csak hogy válaszoljak a kérdésemre: Olyan feladatokat lehetne kitenni háttérfolyamatra, aminek eredménye nincs hatással az éppen aktuális oldal megjelenítésére. Pl. statisztikai adatok frissítése. Így a szerver terhelése nem csökken, de az user hamarabb kap választ. Szóval gondolkodjak, mielőtt beszélek?
5

Jogos kérdés

Ifju · 2010. Május. 26. (Sze), 15.34
Szvsz jogos volt a kérdés, mert nem feltétlenül triviális ez, mert azért viszonylag kevesen dolgoznak nagy terhelésű rendszerekkel, ahol szükség van háttérfolyamatokra.

Ahol hasznos lehet még a párhuzamosítás az pl a migráció, amikor nem egy script tolja át egyik rendszerből az adatokat a másikba, hanem párhuzamosan több.

Ugyanilyen alkalmazási lehetőség, amikor egy háttérfolyamattal webfelületen keresztül kommunikál a felhasználó, csak ott egy további kommunikációs layert be kell tenni.

Nagy terhelésű rendszereknél előfordulhat, hogy bizonyos feladatokat egy queue-ba (activeMQ, gearman, stb.) tolunk be, és háttérfolyamatok végzik el azokat.

Nekem az utóbbi időben a favoritom a supervisord által felügyelt folyamatosan futó háttérfolyamatok, már ahol szükség van ilyesmire.
6

Is

janoszen · 2010. Május. 26. (Sze), 17.21
Ez az egyik oldala a dolognak, a másik oldal hogy ha azonnal akarsz mondjuk minden feltöltött képet lekonvertálni akkor simán lehet, hogy csúcsidőben kétszer annyi gép kell amíg egyébként unatkoznak a szerverek. Tehát ha az üzleti logika engedi el tudod simítani a CPU igényes műveletekből eredő spikeokat.

Hogy rávilágítsak a módszer egy teljesen más előnyére, ha például socketen blokkolva várakozol és mindezt kombinálod a mostanában felkapott JavaScript long poll (COMET) megoldással, elég könnyen tudsz full realtime dolgokat írni. Természetesen nem azt mondom, hogy egy ActiveMQ-val szemben megállná a helyét de ez elég lightweight és hordozható, mindössze egy Unixon futó PHP kell hozzá.
7

PHP: jó étvágyat!

kayapo · 2010. Május. 26. (Sze), 19.49
Egy szempontból rizikós dolog PHP-ban daemont írni - gondoljunk csak a memory leak problémákra, amik jellemzik a PHP-t -, más felől mikor felmerül a kérdés, hogy mik azok a tanácsok amit rendszergazdaként programozóknak adnék, az elsők között szerepel, hogy "felejtsd el azt a szót, hogy _crontab_".

Tehát daemon írás a cronban ütemezett scriptek helyett, mindenképpen jó ötlet, de azt a lehető legnagyobb körültekintéssel tegye a PHP programozó. És főként ne felejtsük, hogy nem bűn a PHP mellett egy másik (daemon írásra alkalmasabb), nyelv elsajátítása.
8

+1

janoszen · 2010. Május. 26. (Sze), 20.18
Egyet tudok érteni. A PHP-ban ez nem mainstream feature és időnként vannak vele gondok akkor is, ha a fejlesztő figyel a memóriakezelésre. A cikk célja az volt, hogy a PHP fejlesztő szívéhez közelebb hozza a Unix világot, a parancsok ugyanis - lévén rendszerhívások - ugyanazok más nyelvekben is.

Ettől függetlenül természetesen lehet PHP-ban is ilyen szoftvereket írni csak kritikus szemmel kell nézni az alkalmazás mutatóit. Talán érdemes követni azt a talán nem egészen mérnöki kifinomultságnak megfelelő elvet, hogy a workerek időnként fejezzék be a működésüket és szülessenek helyettük új processzek (lásd: forkolós példa).
11

Mi a baj a crontabbal?

tgr · 2010. Május. 27. (Cs), 17.38
Mi a baj a crontabbal?
12

Rosszul skálázódik

janoszen · 2010. Május. 27. (Cs), 19.31
Egy percnél finomabban nem tudsz időzíteni, akkor is eszi a CPU-t ha nem csinál semmit, nem tud on demand reagálni és nagyon rosszul skálázódik.
31

Tegyunk tisztaba par

hron84 · 2010. Jún. 2. (Sze), 13.51
Tegyunk tisztaba par dolgot:
"Egy percnél finomabban nem tudsz időzíteni,"
Es ez miert gond? Mit szerettel volna idoziteni egy percnel surubb idotartamra?

"akkor is eszi a CPU-t ha nem csinál semmit,"
Erre bizonyitekot kerek. Valoban, a crond esz valami nagyon keves CPU-t, amikor fut, de az annyira keves, hogy meg az eppen belogott shelled is tobbet eszik nala.

"nem tud on demand reagálni"
Mert mondjuk nem feladata neki? A cron ugyanis arrol szol, hogy idozitve futtatunk valamit. Ez by definition zarja ki az on-demand reagalas lehetoseget.

"nagyon rosszul skálázódik."
Merrefele? Illetve miert is kellene skalazodnia? Szerintem definicios problema van itt is, de jobb lenne valaszokat hallani.

Nagyon nagy felreertesek vannak a crontab (pontosabban a cron altal idozitett feladatok) fogalmanak elkepzeleseben. Eredetileg ezt arra talaltak ki, hogy bizonyos feladatok elvegzesehez ne kelljen peldaul hajnali fel haromkor bejelentkezni es kezzel futtatni a parancsot, hanem ez automatikusan tortenjen meg. Ha ilyen feladat van, en rendszergazdakent jo szivvel biztatom a PHP programozokat a crontab hasznalatara, a hatterben futo php processz sokkal-sokkal tobb memoriat eszik varakozas kozben, mint az ilyen szempontbol optimalisabban megirt crond processz.

Johetnek a kovek.
33

Kövek. :)

janoszen · 2010. Jún. 2. (Sze), 19.19
"Egy percnél finomabban nem tudsz időzíteni,"
Es ez miert gond? Mit szerettel volna idoziteni egy percnel surubb idotartamra?

Hasamra csapva: amikor domain lejáratnál versenyeznek az emberek a domainért nagyon nem mindegy, hogy egy másodpercen belül reagálsz a fölszabadulásra vagy egy percen belül.

"akkor is eszi a CPU-t ha nem csinál semmit,"
Erre bizonyitekot kerek. Valoban, a crond esz valami nagyon keves CPU-t, amikor fut, de az annyira keves, hogy meg az eppen belogott shelled is tobbet eszik nala.

Valóban nem akkora szempont, de a percenként fölébredő majd egy adott esetben igen méretes táblából selectáló PHP script nem éppen kíméletes a géppel. Arról persze nem is beszélve hogy totál szekvenciálisan fog futni. (Ha meg nem akkor már eleve jobban jársz a daemonnal.)

"nem tud on demand reagálni"
Mert mondjuk nem feladata neki? A cron ugyanis arrol szol, hogy idozitve futtatunk valamit. Ez by definition zarja ki az on-demand reagalas lehetoseget.

Nem mondtam, hogy a crond bizonyos feladatokra nem megoldás, nagyon sok mindenre megoldás különben nem használnánk. De az nagyon sok butatót rejt amikor folyamatos háttérprocesszálás crondből fut.

"nagyon rosszul skálázódik."
Merrefele? Illetve miert is kellene skalazodnia? Szerintem definicios problema van itt is, de jobb lenne valaszokat hallani.

Pl értelmesen nem tudod két gépen összeszinkronizálni a futásukat. Nem mondom, hogy nem lehet megcsinálni de ahhoz azért nagyon kell tudni hogy ne legyen buktató benne.

Nagyon nagy felreertesek vannak a crontab (pontosabban a cron altal idozitett feladatok) fogalmanak elkepzeleseben. Eredetileg ezt arra talaltak ki, hogy bizonyos feladatok elvegzesehez ne kelljen peldaul hajnali fel haromkor bejelentkezni es kezzel futtatni a parancsot, hanem ez automatikusan tortenjen meg. Ha ilyen feladat van, en rendszergazdakent jo szivvel biztatom a PHP programozokat a crontab hasznalatara, a hatterben futo php processz sokkal-sokkal tobb memoriat eszik varakozas kozben, mint az ilyen szempontbol optimalisabban megirt crond processz.


Nyilván erre hülyeség daemont írni, de ha percenként futtatja valaki a PHP scriptjét akkor el kell gondolkozni a crond alkalmasságán.
36

Johetnek a kovek. Beallnek en

Tyrael · 2010. Jún. 2. (Sze), 22.22
Johetnek a kovek.

Beallnek en is a sorba.

Egyreszt ugye megvan a cronnak a sajat szerepe, van amire nagyon jol hasznalhato.
Masreszt, viszont tegye fel a kezet, aki meg nem jart ugy, hogy:
- cronjobok egymasrafutottak, mert az egyik beakadt/tul lassan futott le, es rafutott a kovetkezo.
- volt valami typo a crontab bejegyzesben (pl. egy hianyzo ujsor a fajl vegerol) es kurva maceras volt debugolni.
- website koltoztetes soran elfelejtette a rendszergazda atvinni a crontab bejegyzeseket az uj szerverre (vagy azert mert nem masolta at az /etc/cron.d konyvtarat, vagy azert, mert crontab -e -vel lett felveve, es a /var/spool/cron/crontabs/ atmasolasa maradt ki)

Persze ha naponta 1szer, mondjuk ejfelkor ki akarod purgalni az inaktiv usereket a db-dbol, vagy havonta egyszer ki akarsz kuldeni egy ertesitot a hirleveledre feliratkozott usereidnek, akkor arra a cron a legjobb megoldas.

De ha tobb procit akarsz kihasznalni(tobb peldanyban akarod futtatni a taskot), esetleg interaktiv modon (itt azt ertem, hogy ha van melo akkor folyamatosan, megszakitas nelkul dolgozik, ha nincs mit csinalni, akkor meg nem eszik cpu idot) pollolgatas nelkul, akkor valoszinuleg nem a cronra van szukseged.

btw: proclub, emlekszel, hogy mondtam anno, hogy szukseg lenne/lesz a crontab vs daemon kerdeskorben egy magyarazatra, hogy mik az elonyok hatranyok, es mikor melyiket hasznaljuk?
valahogy sejtettem, hogy lesznek olyanok, akiknek nem lesz vilagos, hogy melyiket mire erdemes hasznalni, es hogy egyik sem jobb a masiknak, hanem eltero igenyekre kinalnak nemi atfedessel megoldast.

Tyrael
9

Kipróbálta valaki?

janoszen · 2010. Május. 27. (Cs), 00.25
Kipróbálta valaki? Működött? Esetleg hol akadt el?
10

en csak most neztem ra

Tyrael · 2010. Május. 27. (Cs), 14.04
en csak most neztem ra weblaborra.
jonak tunik, de az umask(0)-t kihagytad a daemonizalas lepesei kozul (meg persze el lehetne valtani a working dir-t, de akkor meg az kell irni egy normalis autoloadert)

Tyrael
13

Működik

EdgarPE · 2010. Május. 28. (P), 00.28
Én írtam egy komolyabb PHP PCNTL alkalmazást, féltem is, hogy lesznek gondok, de teljesen jól működik. Cron percenként indítja az alkalmazást, ami megnézi hogy fut-e már, ha igen kilép, ha nem akkor megnézi hogy van-e elvégzendő feladat és ha van akkor forkol néhányat és elkezdi lefuttatni a feladatokat. Egy job - egy fork. Egy időkorlát vagy adott job szám elérése esetén pedig nem forkol újra, hanem befejezi a php script a futást, és a cron miatt ugye 1 perc múlva újra indul. Ezzel a memory leak meg van oldva, legalábbis részemről :)

Egy kényelmetlen dolog van egy ilyen fejlesztésnél: vigyázni kell, hogy fork előtt ne legyen semmilyen erőforrás megnyitva (pl.: adatbázis kapcsolat, fájl leíró) mert a fork-nál ez másolódik, és ettől az adatbáziszerver nagyon megzavarodik. Fork előtt mindig be kell ezeket zárni, ha volt nyitva, adatbázishoz kapcsolódni meg mindig fork után kell.
14

Jo cikk, de en meg emlekszem

efpe · 2010. Május. 28. (P), 17.17
Jo cikk, de en meg emlekszem arra, amikor beszoltal, hogy ne irjak daemont phpban, ami alapbol jogos, viszont a celnak megfelelt es meg mindig :)
15

Valóban

janoszen · 2010. Május. 28. (P), 20.57
Továbbra is tartom ezt a mondatomat. Most egy apró realtime projektet valósítottam meg PHP-ban (ami bent is van a repótokban) és valami segfaultolós nyűgje van. Nem tudom mi, mert minden működik benne de a logba jön néha egy-egy segfault. Arról nem is beszélve hogy thread kezelés nagy bánatomra nincs benne.

Egy ideje nézegetem a Pythont és minél többet nézem, annál jobban kezd tetszeni. Ma pl fél óra alatt írtam egy daemont ami chrootol, uidot vált, lecsatlakozik a terminálról és socket szervert indít. Mindezt úgy, hogy gugliznom is kellett utána. Ha elég jártasságot szerzek benne valszeg lesz belőle cikk.
20

Python

_subi_ · 2010. Május. 29. (Szo), 09.43
Nagyon jó cikk lett ez is, a Python-os tapasztalataidat különösen várom.
23

Scriptelek

janoszen · 2010. Május. 29. (Szo), 17.12
Már pár hónapja scriptelek benne, most írtam daemont. Első ránézésre nagyon... konzekvens nyelvnek tűnik.
32

Akkor nezd meg a ruby-t is.

hron84 · 2010. Jún. 2. (Sze), 13.53
Akkor nezd meg a ruby-t is.
34

Nem jött be

janoszen · 2010. Jún. 2. (Sze), 19.21
A ROR nem jött be, főleg hogy a neten található leírások fele még marginálisan sem volt aktuális mert annyira gyorsan fejlődik a nyelv. Más (webes)keretrendszerről meg nem tudok rá. A Django viszont szimpatikus.
35

http://www.sinatrarb.com/ sas

Tyrael · 2010. Jún. 2. (Sze), 21.55
http://www.sinatrarb.com/
sasold meg, nekem rajta van a todo listan, de elegge hatul.
amugy van meg par rubys keretrendszer, de nekem ez volt a ror mellett a legszimpatikusabb.

Tyrael
37

Python

janoszen · 2010. Jún. 2. (Sze), 23.21
Köszi de egyelőre a Python van terítéken. Amellett, hogy weben jól teljesít van pl működő LDAP modulja ami igen kevés nyelvről mondható el.
(Most olyan feladatok vannak mint realtime logelemzés LDAP lookuppal. A Perl és a PHP már elvérzett.)
38

bar nem kellett meg soha

Tyrael · 2010. Jún. 3. (Cs), 09.27
bar nem kellett meg soha ldap-ot webrol piszkalnom, de egyreszt mivel az ldap igen egyszeru felepitesu, masreszt mivel elegge elterjedt, nekem nagyon furcsanak tunik, hogy nincs hozza se php-ben (kurva elterjedt), se perl-ben(ami nincs benne a CPAN-ben az nem is letezik) nincs normalis ldap connector.

Tyrael
39

CPAN

janoszen · 2010. Jún. 3. (Cs), 12.21
A CPANos LDAP connector első ránézésre jól néz ki de sajnos minimum versenyhelyzet van benne mert ha ott hagyom kb 4-8 órát feldolgozni óránként sokszázezer rekordot, gyakorlatilag mindent összeborít abban a processzben. Ez egyébként korrelál mások megfigyeléseivel is (kérdezd a céges üzenőrendszer üzemeltetőjét pl.)

A PHP-s működik, bár nem ekkora adatmennyiséggel próbáltam.
27

Thread-kezelés

Ifju · 2010. Május. 31. (H), 15.58
Ha emlékezetem nem csal, akkor a PHP nem threadsafe linux alatt, és csak Microsoft környezetben létezik threadsafe megvalósítás az IIS miatt, de ott sincs thread-kezelés.
28

nem egeszen igy van. maga a

Tyrael · 2010. Május. 31. (H), 17.57
nem egeszen igy van.
maga a php thread safe, viszont nem mindegyik modul az.
windows alatt kevesebb a nem thread safe module, ezert ott kissebb az eselye, hogy talalkozol a problemaval.
a php manualban is kb. ez szerepel:
http://www.php.net/manual/en/faq.installation.php#faq.installation.apache2

amennyiben nem mpm kornyezetben hasznalod, akkor erdemes az nts verziot felrakni, mert gyorsabb.

Tyrael
16

Szép

Tóth Ádám · 2010. Május. 28. (P), 23.10
Ismét egy jó cikk, egy érdekes témáról!

A fentieket egyik saját projektünk esetében már közel 4 éve alkalmazzuk egy mission-kritikus rendszer esetében, különösebb problémák (segfaultok, leakek) nélkül, én tehát nyugodt szívvel merem ajánlani.

További jó anyag a témában a sokat ismételgetett PHP5 felsőfokon című könyv, amiben egy külön fejezetet szentel a démonok írásának, foglalkozva azzal az igen fontos kérdéssel, hogy egy "tisztességes szülő folyamat feladata, hogy eltakarítson a gyermekei után".

Egy jópofa folytatásként eszembe jutott az is, hogy némi továbbfejlesztéssel ki lehetne alakítani egy egyszerű map-reduce rendszert feladatok partícionálására és párhuzamos, asszinkron végrehajtására, a'la Google. A Master lehetne a szülőfolyamat, amely a worker-eket hoz létre (forkol), de minimális továbbfejlesztéssel akár a workerek is lehetnének önálló démonok, és így relatíve egyszerűen kiterjeszthető lenne több gépre is a koncepció. De jó lenne erről egy cikk... :)
26

Párhuzamos feldolgozás

Ifju · 2010. Május. 31. (H), 15.56
Ezügyben 2 hónap múlva lehet fogok tudni érdemben is beszélni, mert ehhez hasonló van van tervben gearmannal és supervisord-vel megtámogatva (nincs kedvem a kereket újra feltalálni) rendszerbevezetés kapcsán.
17

Kötelező olvasmány

inf3rno · 2010. Május. 28. (P), 23.27
Az a bajom a cikkel, vagy inkább magammal, hogy annyira új nekem a témakör, és egy ilyen cikk annyi új kifejezést tartalmaz, hogy csak kapkodom a fejemet. Nincs valami egyszerű leírás, ami vázlatosan, nagy vonalakban mutatja be a témakört?
Kicsit úgy érzem magam, hogy kinyitottam egy könyvet egy olyan témáról, amit az életben nem hallottam, és nincs hozzá tartalomjegyzék, vagy vázlatos leírás.
Lehet, hogy egyedül vagyok vele, de én úgy érzem, hogy az előző cikk nekem, mint egy témában járatlan embernek kevés volt. Vannak olyan alapfogalmak, mint process meg signal, amiket nem tisztáztál a cikksorozat elején. Persze azt is elfogadom válaszul, hogy nem akarsz leereszkedni a hülyék szinjére. :-D
18

http://en.wikipedia.org/wiki/

Tyrael · 2010. Május. 29. (Szo), 00.04
http://en.wikipedia.org/wiki/Daemon_(computer_software)
innen elindulsz, koveted a linkeket es rendben leszel.

Tyrael
19

Sok idő kell hozzá...

_subi_ · 2010. Május. 29. (Szo), 09.40
...mire minden linket végigkövet, viszont egyedülálló tudásanyagra tehet szert. :)
21

hazat epiteni az alapoktol

Tyrael · 2010. Május. 29. (Szo), 11.12
hazat epiteni az alapoktol lehet csak.
nem varhato el minden cikkirotol, hogy a hogyan epitsunk cassandra clustert jellegu irasokban a linux telepitesenek es configuralasanak a lepeseivel kezdje a bevezetot.

Tyrael
22

Nyilván

_subi_ · 2010. Május. 29. (Szo), 11.24
Én csupán arra céloztam, amennyiben nekiáll követni a linkeket, akkor gyakorlatilag feltérképezi az egész internetet. :)
24

Kösz :-)

inf3rno · 2010. Május. 29. (Szo), 17.44
Kösz :-)
25

ez ilyen. Tyrael

Tyrael · 2010. Május. 29. (Szo), 19.15
ez ilyen.

szerk:
ezt ide akartam irni:
http://weblabor.hu/cikkek/nagy-terhelesu-rendszerek-fejlesztese-2#comment-68568

Tyrael
29

Én épp most fejeztem be egy

virág · 2010. Jún. 1. (K), 07.46
Én épp most fejeztem be egy szervizt, PHP-ban írtam, először voltak fenntartásaim, mert azelőtt írtam már Windows alá szervizt, de azt vagy C-ben vagy Delphiben, ez a mostani Linuxon fut és egy eléggé komoly terhelésú SMS gateway funkciót valósít meg, figyelnie kell státuszokat és adatbázis kapcsolatokat is fenntart. Semmi gond vele, sem memória probléma nem jelentkezett sem más, észre sem lehet venni, hogy fut a szerveren. Készítettem hozzá egy Make fájlt, aminek a segítségével egy egyszerű make install, make stop, make start paranccsal lehet telepíteni, leállítani vagy elindítani, annyi, hogy érdemes a nohup paranccsal indítani :) Ez a cikk pedig kitűnő, gratula hozzá, de már az előző résznél is írtam :)
30

nohup

janoszen · 2010. Jún. 1. (K), 09.10
A nohup-ot el tudod kerülni a terminálról való lecsatlakozással ugyanis ugyanazt csinálja a kettő.
40

Köszönetet

Ustak · 2010. Jún. 15. (K), 23.27
szeretnék mondani, egy backend manager fejlesztését kezdtük el, és mivel még a szerver rész tervezés alatt áll, a klienst pedig qooxdoo -ban szeretnék íratni (velem :-)) ezért hogy ne akkor puffanjak bele, pár apró tesztet le akartunk futtanti egy telnet szerű társalgást imitálva. Próbáltam porton hallgatózó shell scriptet netcat-el, néztem a perl-t (tudom hogy meg lehet benne csinálni, de nem akartam elmélyedni) és végül ez a cikk vált be :-)
Tesztelésre nagyon hasznos!
Üdv:
Gábor