Autor Zpráva
SwimX
Profil
Dobrý den,

řeším na inzertním webu problém, jak upozornit, jednou asi i velké počty lidí, že jejich inzerátu končí platnost.

mám skript, který by měl fungovat, ale nefunguje. Pokud jsou pauzy mezi dávkami emailů 0, pak je vše ok, pokud ale zvětšuji pauzy, skript se chová divně. V nastavení, mezi každým emailem 1s a mezi dávkou 180s to skonči na tom, že se mi log soubor vytvoří vícekrát - vždy tolikrát, kolik je dávek, jakoby se skript restartoval..?

Poradil by někdo, kde mám chybu?

<?php
    //pocet emailu v davce
    define ( "SENDBLOCK", 50);
    //pauza mezi kazdym emailem v sec
    define ( "EMAILPAUSE", 1 );
    //pauza mezi kazdym blokem v sec
    define ( "BLOCKPAUSE", 180 );
    //maximalni doba behu skriptu v sec
    define('SCRIPT_RUNTIME_LIMIT', 7200); // 2 hod.
    
    set_time_limit(SCRIPT_RUNTIME_LIMIT);
    ignore_user_abort();

    // pripraveni emailu z databze do pole 

   $logName = "logs/endingadd-" . date ("Y-m-d-H-i-s") . "-" . mt_rand(0,5000) . ".txt";
   $f = fopen ( $logName, "w" );

   foreach {
      $mail = new phpmailer() ;
      // nejake texty emailu, atd.
      if ( $mail -> Send() ) {
                fwrite( $f,  "OK" );
                $allOk ++;
            }
            else {
                fwrite( $f,  "FAILED" );
                $allFailed ++;
      }
      $sendDone ++;
      

      if ( $sendDone % SENDBLOCK == 0 ) {
                $sendDone = 0;
                fwrite( $f, " * - sleep " . BLOCKPAUSE . "s at " . date ( "d.m.Y (H:i:s)" ) . "\r\n");
                sleep ( BLOCKPAUSE );
            }
            
            sleep ( EMAILPAUSE );
   }
Alphard
Profil
Ten script se spouští ručně, není možné, že ho třeba prohlížeč zavolá vícekrát když čeká odpověď? Já jsem u podobných systémů využíval cron, tj. žádná BLOCKPAUSE, jen EMAILPAUSE, kde je 1 s po každém mailu možná zbytečný luxus. Odešle se dávka a script se ukončí. PHP není stavěné na dlouhoběžící scripty.
SwimX
Profil
Alphard:
teď ho kvůli testování spouštím ručně, potom tam bude nastaven cron. ten restart skriptu zřejmě měl opravdu za vinu prohlížeč, ale stále to skončí na tom, že se po jedné dávce skript uspí – pokud to je na 10s tak je to ok, pokud je to na víc tak už se neprobudí.

Přitom u set_time_limit v manuálu píší, že nefunguje jen při zaplém safe_mode což podle http://www.hotelpersonal.cz/info.php nemáme.
Alphard
Profil
Nevím, s tím nemám moc zkušeností. Nejdéle držím script při životě tak 15 minut, 2 hodiny jsem nezkoušel. Cron není problém volat po minutě, takže bych to celé navrhoval jinak, jak jsem psal [#2].
David P
Profil *
Jednou mi script běžel 12 hodin také pomocí sleep, takže technicky to možné je
Keeehi
Profil
Alphard:
PHP není stavěné na dlouhoběžící scripty.
Nějaký důvod?
Alphard
Profil
Keeehi:
Nějaký důvod?
Obecně nemám konkrétní důvod, neznám detailně vnitřní kódy Apache + PHP, takže nemohu posoudit kvalitu garbage collectoru, memory leaků, schopnosti optimálně držet dlouho trvající procesy, vytížení při uspání. Byl tady pokus o PHP GTK, ale moc se to neujalo, nevím co bylo příčinou. Osobně bych na dlouhoběžící programy vybral jiné jazyky.
Jako možný problém mi např. připadá velmi problematické ukončení scriptu při chybě (která dělá něco nevhodného a sama neukončí běh, např. tento script by začal kvůli neintegrity databáze neřízeně spamovat a nešlo by ho zastavit), asi jediná možnost psát na podporu (pokud to není náš server).

Tady konkrétně s tím byl problém, doporučil jsem řešení, které mi dobře funguje a odpovídá obvyklé praxi dlouho neběžícího PHP scriptu. Svůj návrh jsem napsal až po 3 dnech neaktivity, jestli vám někomu dobře funguje něco jiného, mohli jste dávno uvést své řešení.
Svůj výrok přeformuluji, omlouvám se, že si nehlídám každou větu.
Keeehi
Profil
Alphard:
Svůj výrok přeformuluji, omlouvám se, že si nehlídám každou větu.
Mrzí mě, že jsi to vzal takto. Mě opravu zajímá, jestli jsou s dlouhoběžícími scripty problémy. Vím, že třeba u DVBgrabu jsou 2 nekonečné scripty a nevím o žádných problémech. Doma mi taky nějaké nekonečné smyčky běží a nezaznamenal jsem žádný problém. Takže by mě zajímalo, jestli mám očekávat nějaké problémy.
Alphard
Profil
[#8] Keeehi
O nic nejde, já se obecně snažím psát přesně a neuvádět nějaké mýty. Tady jsem napsal věc, která je nepodložená, byla to chyba. To ale samozřejmě nebrání diskusi o vhodnosti dlouhoběžících skriptů. Snad to nebude SwimXovi vadit, kdyžtak to vyčlením.

Já právě raději vícekrát spouštím stránku s kratším max_execution_time, než dělat nekonečné scripty. Doma mi cron nahrazuje funkce Opery Reload Every někaký čas.
U rozesílání mailů mám script kombinový s databází, kde vždy vytáhnu určitou dávku mailů
$queue = $this->newsletterM->getQueue();
    $queue->where('((n.last_send < date_sub(now(), interval 20 hour) or n.last_send is null))');
    $queue->limit(30);
    
    foreach ($queue as $item)
    {
      $this->sendMail($item);
    }
a tu pak odesílám. Po odeslání mailů se do databáze updatuje čas odeslání.
Cron je spouštěn v určitých hodinách po 5 minutách, to omezení 20 hodin brání opakovanému odeslání.
Keeehi
Profil
Alphard:
Pokud jsem dělal někde nějakou jednorázovou akci, kde jsem potřeboval "cron" tak jsem také používal tu operu nebo <meta http-equiv="refresh" content="xxx">. Ovšem pokud to chci mít nezávislé na prohlížeči tak musím použít cron nebo nějakou smyčku.
Teď třeba chci kontaktovat nějaký server každých 5 sekund. Tak místo cronu používám:
while(1){
    ...
    sleep(5);
}
Zatím jsem žádný problém nezaznamenal. Myslíš, že použití cronu by bylo lepší? Nebo by jsi navrhnul nějaké jiné řešení?
SwimX
Profil
Alphard:
Snad to nebude SwimXovi vadit, kdyžtak to vyčlením.
rád se poučím, netřeba to vyčleňovat.

Keeehi:
a mohl bys poradit, proč můj nekonečný skript nechce být nekonečný?
Keeehi
Profil
Už to možná vidím: řádek 19, máte špatně ten foreach. Není tam napsáno co procházet a do jaké proměnné to vkládat.
SwimX
Profil
Keeehi:
bohužel, to je jen ilustrační kód, pokud zmenším pauzy - funguje vše ok. Jakoby nefungovalo to set_time_limit, přitom podle php manuálu fungovat nemá jen při zaplém safe_mode a to na serveru nemáme (viz php info někde výše v komentu).

udělat to tak, jak říká Alphard by sice bylo pěkné, ale moc se mi do toho nechce, je to další zápisy do DB kdy se co poslalo.

takle se jednou za 24 hodin spustí cron - a pošle se spousta emailů..
Alphard
Profil
SwimX:
Jakoby nefungovalo to set_time_limit
Vypisuje se vám nějaká chyba? Nebo ukončujete spojení? Je něco v logu? Jestli máte povolené ini_set(), zkuste to přímo.
Můžete zkusit
set_time_limit(7200);
while (true)
{
  file_put_contents('log.txt', date('c').PHP_EOL, FILE_APPEND);
  sleep(5);
}
co to udělá...

ale moc se mi do toho nechce, je to další zápisy do DB kdy se co poslalo
Nevím, kde berete adresy, ale jestli je máte v db jako samostatné záznamy, tak to lze řešit i jedním update where id in(...) na závěr, to server přežije.
Keeehi
Profil
Z manuálu PHP
"The set_time_limit() function and the configuration directive max_execution_time only affect the execution time of the script itself. Any time spent on activity that happens outside the execution of the script such as system calls using system(), stream operations, database queries, etc. is not included when determining the maximum time that the script has been running. This is not true on Windows where the measured time is real."
Pokud tedy nejedete na windows, čas v set_time_limit() by na to neměl mít vliv.

Problémové místo by šlo identifikovat třeba takto: Na několik míst vložit
file_put_contents("log.txt", "Misto: xxx\n", FILE_APPEND);
A pak zjistit, kam to došlo a kam už ne.
SwimX
Profil
Alphard:
Nevím, kde berete adresy, ale jestli je máte v db jako samostatné záznamy, tak to lze řešit i jedním update where id in(...) na závěr, to server přežije.
Nejde o zátěž serveru, ten toho vydrží hodně a adresy sou z DB. Nechce se mi to psát, sem od přírody líný člověk :-))

Vypisuje se vám nějaká chyba? Nebo ukončujete spojení? Je něco v logu? Jestli máte povolené ini_set(), zkuste to přímo.
Nevypisuje se nic. Spustím skript - zavřu okno prohlížeče. 30s to jede na pozadí, do logu to píše email odeslán, email mi přijde. Po 50ti emailech se to uspí na 40s (např, mezi dávkami) a už se to neprobudí, v logu je sleep at TIME

Keeehi:
problémové místo je jednoduché - #36 sleep ( BLOCKPAUSE ); a pak už nic :-)

Zkusím si zítra víc pohrát, nastavit to jinak, než pomocí set_time_limit. ini_set by jít měl.

Zatím mnohokrát děkuji, zítra napíšu.
Keeehi
Profil
SwimX:
Zkoušíte to doma nebo někde na hostigu.
SwimX
Profil
Keeehi:
na serveru, www.hotelpersonal.cz/info.php doma nemám rozchozené smtp.
Keeehi
Profil
Zkoušel jste dlouhý sleep nahradit několika krátkými?

 if ( $sendDone % SENDBLOCK == 0 ) {
                $sendDone = 0;
                fwrite( $f, " * - sleep " . BLOCKPAUSE . "s at " . date ( "d.m.Y (H:i:s)" ) . "\r\n");
                for ($i=0; $i<BLOCKPAUSE; ++$i) {
                     sleep ( 1 );
                }
}
Davex
Profil
SwimX:
Možná by mohlo pomoci zvýšit v konfiguraci hodnotu max_execution_time.
Keeehi
Profil
Jelikož je server na Linuxu, tak doba strávená v sleep by se do max_execution_time ani do time_limit započítávat neměla -> mělo by být jedno jaké číslo je v sleep nastaveno.
SwimX
Profil
Alphard:
Jestli máte povolené ini_set(), zkuste to přímo.
nepomohlo, skript se uspí na 60s pak druhá dávka, dalších 60s a pak se neprobral

Keeehi:
Zkoušel jste dlouhý sleep nahradit několika krátkými?
nepomohlo, logy se tváří v obou případech stejně

nyní jsem spustil: [#14] Alphard skript až skončí, postnu log


poprvé jsem Alphardův skriptík spustil omylem nevím kolikrát (z prohlížeče). Tento skript už běží 25 minut a stále zapisuje do souboru. Potom jsem udělal kopii a spustil ho právě jednou. Log je zde: http://hotelpersonal.cz/admin/cron/log2.txt běžel 1 minutu 40s pokud dobře počítám. Spustil jsem tedy 3 kopii opět právě jednou.
viz http://hotelpersonal.cz/admin/cron/log3.txt běžela uplně stejně dlouho.

Teď nechápu, proč tedy http://hotelpersonal.cz/admin/cron/log.txt stále narůstá?

kód testu

<?php
ignore_user_abort();
set_time_limit(7200);
while (true)
{
  file_put_contents('log.txt', date('c').PHP_EOL, FILE_APPEND);
  sleep(5);
}

?>



tak ještě jeden test: http://hotelpersonal.cz/admin/cron/log5.txt byl spuštěn 5x. Ve skriptu se nechá vypozorovat, že v
jeden zápis zača v 2012-01-29T00:41:25+01:00 a ten zřejmě skončil 2012-01-29T00:43:05+01:00 přesně po minutě a 40s
další začal v 2012-01-29T00:41:45+01:00 a skončil v 2012-01-29T00:43:25+01:00 taktéž minuta 40.

a ty další tři budou jistě podobné. tento log nenarůstá.



Dokáže někde poradit? :-)
Davex
Profil
Podobný skript mi tu běžel několik hodin. Zkus si zapnout chybový log PHP, zda se v něm náhodou něco neobjeví.

.htaccess:
<IfModule mod_php5.c>
  php_flag log_errors On
  php_value error_log php_errors.log
</IfModule>
SwimX
Profil
Davex:
nic to nevypsalo. Skript běžel svých 100s.
Davex
Profil
SwimX:
Tak bych se na to zkusil zeptat technické podpory hostingu. Třeba skripty běžící na pozadí takto omezují.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: