Cronner – nástroj pro jednoduchou správu cronových úloh
- stekycz
- Člen | 152
Před pár dny jsem na Twitteru oznámil vytvoření tohoto nástroje a k mému překvapení získal poměrně rychle více jak 10 hvězd. Zakládám tedy toto vlákno proto, že by se mohl nástroj hodit i těm, kteří na Twitteru neobjevili informaci o jeho existenci :-)
Pro začátek upozorňuji, že se jedná o verzi 0.5.0. Verzi 1.0.0 uveřejním snad brzo. Rád bych nejprve získal nějaký feedback, názory a přípomínky od dalších lidí. Prosím, pište je pokud možno rovnou na GitHub.
Proč nástroj pro cron v PHP?
Protože ne každý web má své vlastní VPS na kterém si může nastavit
kolik chce úloh. A také proto, že ne kvůli každé změně chci měnit
nastavení přímo v cronu (resp. administraci hostingu). Tohle nastavení chci
provést jednou a pokud možno na něj už nesahat.
A co to tedy umí?
Zatím toho moc není, ale to důležité tam je.
- Nastavení periody spouštění úkolu.
- Nastavení dní v týdnu, ve které má být úloha spuštěna.
- Nastavení časů, během kterých může být úloha spuštěna.
EDIT: aktuální popis v Addons
Jak to použít?
Nástroj využívá komentářových anotací v PHP. Moje snaha je, aby bylo
možné nástroj nasadit na existující projekt s minimálním úsilím. Jsou
tam zatím nějaké mezery, ale do verze 1.0.0 by měly být doladěné.
Příklad použití by mohl vypadat takto:
Chci z nějaké webové služby automaticky stahovat data. Vím, že přes
víkend je nemá smysl stahovat, protože je nikdo nevytváří a ani nikdo
nepoužívá. Zároveň vím, že všechna nová data se veřejně objevují
v dávkách 3× denně v různých časech. Jak to udělám cronnem? Já
osobně nevím, takže pokud někdo víte, v komentáři klidně
napište :-)
Třída s úlohou:
class CronTasks extends \stekycz\Cronner\Tasks {
/**
* @cronner-task Stahuje důležitá data z webové služby
* @cronner-period 1 hour
* @cronner-days working days
* @cronner-time 08:00 - 09:00, 13:00-14:00, 20:00 - 21:00
*/
public function downloadImportantData() {
// Váš vlastní kód pro stažení a uložení dat
}
}
Dále musíme mít
Presenter
, ve kterém budeme úlohy spouštět. Nastavíme tak
jedinou úlohu přímo pro cron, kterou budeme spouštět jak chceme.
Presenter:
class CronPresenter extends \Nette\Application\UI\Presenter {
private $cronner;
public function injectCronner(Cronner $cronner) {
$this->cronner = $cronner;
}
public function actionCron() {
$this->cronner->addTasksCallback(function () {
// Some magic code can be here :-)
return new CronTasks();
});
$this->cronner->run();
$this->terminate();
}
}
A nesmíme zapomenou na konfiguraci v config.neon
:-)
services:
cronner: stekycz\Cronner\Cronner(new \stekycz\Cronner\TimestampStorage\FileStorage(%wwwDir%/../temp/cronner))
Podrobněji k použití v repozitáři na GitHubu.
Pokud máte jakékoli komentáře nebo náměty, šup s nima na GitHub :-) Stahovat můžeme buď přímo z repozitáře nebo pomocí composeru z packagist.org.
Editoval stekycz (17. 8. 2014 16:14)
- stekycz
- Člen | 152
@castamir
- Teď ještě není ověření možné, ale v issues už mám úkol, který to bude řešit.
- Tuhle situaci to teď neřeší. Dávám si do issues. Díky!
- Co myslíš vypnutím? Dočasně nebo trvale? Dočasné vypbutí by mělo
vyřešit stejné issue jako pro bod 1. Trvale to vypneš odebráním anotace
@cronner-task
od metody.
@Felix
Záleží čistě na tobě jako vývojáři, kdy to budeš spouštět :-)
Můžeš si vytvořit speciální akci pro crona, kterou nastavíš
v nastavení hostingu jako cron akci (a tedy se jednomu nastavení
v administraci hostingu nevyhneš). Další úlohy už ale stačí přidávat
v kódu. Nebo si můžeš úlohy spouštět v jakékoli jiné akci, kterou
uznáš za vhodnou, klidně při každém requestu ;-)
Bohužel nejsem kouzelník a pokud ti hosting nedovoluje pouštět úlohu častěji než 1× za hodinu, tak se ani častěji nespustí. Pokud má někdo nápad, jak to řešit jinak, sem s ním!
Má to ale jednu výhodu. Pokud se projekt rozroste a zvýšíš si balíček hostingu tak, že crona můžeš pouštět každou minutu, stačí změnit periodu pro crona jako celek a Cronner se postará o správné spouštění.
PS: Pokud ti hosting nedovolí crona častěji než hodinu a ty bys tu úlohu častěji pouštět chtěl, otázka je, zda a) máš správný hostingový balíček nebo b) opravdu potřebuješ tak často pouštět tu úlohu? ;-)
- Honza Kuchař
- Člen | 1662
Viděl jsem moc zajímavé řešení u PhpBB. Ti to řeší takto…
Mějme šablonu, která se pošle uživateli (téměř) při každém
requestu. V té šabloně bude mimo stránky ještě navíc něco typu
<img src="blahblah/cronner" alt="..." />
. A teď
otázka – jak zajistit, aby se uživateli netočilo kolečko, že se stránka
načítá, pokud zrovna běží nějaká úloha? PHP neumí ukončit spojení
před dokončením skriptu. Však je tu jeden trik. Spojení umí ukončit
prohlížeč, pokud ví, že už dostal všechna data, která potřeboval. Tedy
PhpBB to dělá následovně:
ignore_user_abort(true);
// Output transparent gif
header('Cache-Control: no-cache');
header('Content-type: image/gif');
header('Content-length: 43'); // This cause browser to close connection after 43 bytes
echo base64_decode('R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
// Flush here to prevent browser from showing the page as loading while running cron.
flush();
- Caine
- Člen | 216
stekycz napsal(a):
Bohužel nejsem kouzelník a pokud ti hosting nedovoluje pouštět úlohu častěji než 1× za hodinu, tak se ani častěji nespustí. Pokud má někdo nápad, jak to řešit jinak, sem s ním!
Mno a co treba udelat script, co spousti sam sebe?
...
... provedeni uloh ...
...
sleep($delay);
if (is_file('php-cron.enabled')) {
file_get_contents('http://na.sama.sebe'); //nebo pres curl, ci exec(), atp...
}
Samozrejme by se objevilo spoustu uskali napr rozlisovani, kdy to spustil cron a kdy se to spustilo samo, nebo jak se vyporadat s delkou vykonavani narocnejsich uloh, atp. ale i tak si myslim, ze by to bylo proveditelny…
- stekycz
- Člen | 152
Díky za Honzův a Cainův komentář! Oba přístupy jsou zajímavé a mají svá úskalí. Postupně se tedy vyjádřím k oběma.
Odkazovat na skript/akci, která pouští cron je celkem snadná a ani není potřeba, aby hosting cron podporoval. Nicméně do jisté míry odhalujeme ve vygenerovaném HTML fungování serveru. Dále by mohlo někomu vadit, že se načte stránka, ale až po jejím načtení se cronem upraví data, která stránka zobrazuje.
Spouštět speciální script stále dokola v řetězu se mi zdá možná o něco lepší, protože v zásadě neodkrývá nic. Pokud první spuštění provedeme na jednorázovou akci/skript, který pak ze serveru smažeme, nemusíme mít na hostingu vůbec cron. Nicméně tohle vyžaduje znalost maximální doby běhu skriptu a před jejím uplynutím spustit skript znovu. Navíc se může stát, že budeme neustále čekat na akci, která se spouští jednou za týden. Vytížení serveru sice zásadně nevzroste, ale při zvýšení návštěvnosti se to projevit může. Přeci jen, spouštět stále skript, který vlastně jen čeká, je zbytečné.
V každém případě se více přikláním ke druhé variantě. Záleží však na každém projektu, jak si to sám zařídí. V rámci Cronneru je možné vytvořit jen jakési doporučení.
- Caine
- Člen | 216
Sleep se do max doby behu scriptu nezapocitava (na linuxech).
Na serveru, kde je alespon nejakej cron, bych to urcite neretezil do nekonecna,
ale jen v ramci intervalu toho crona (mam k dispozici 10 min, tak se bude
retezit maximalne 9×, za predpokladu, ze budu chtit jednou za minutu overit,
jestli nemam neco spustit)… A kdyz uz bych to cely takhle delal, udelal bych
to asi tak, aby ten retezici script jen kontroloval, jestli se nema neco
spustit, ale sam to uz nespoustel, a byl uplne co mozna nejjednodusi (klidne
i cisty php), ktery pokud ho volas i treba kazdou minutu nebude mit naprosto
zadnej vliv na vykon celyho serveru, protoze sam o sobe bezi < 10 ms..
- stekycz
- Člen | 152
Souhlasím, že v případě nějakého crona na serveru má smysl řetězit minimálně. Nicméně by mě zajímalo, kolik serverů s cronem neumožňuje navýšit dobu běhu scriptu na dobu periody mezi jednotlivýma úlohama. Spíše bych si teda zvýšil dobu běhu scriptu a používal sleep.
Co se týče skriptu pro kontrolu spuštění úloh, ten by v současnosti nemohl být úplně jednoduchý. Kontrola spuštění nějaké úlohy totiž probíhá až za běhu. Bylo by potřeba někam ukládat informaci o tom, kdy nejdříve má smysl nějakou úlohu spustit. Pak by to bylo bez problémů.
Souvisí s tím však další věc. Co když mám úlohu s týdenní periodou a během týdne přidám úlohu s periodou jedné hodiny? Musím smazat zapamatovaný čas dalšího spuštění crona a při prvním spuštění se znova vygeneruje „mapa“ spouštění úloh?
Když tak nad tím přemýšlím, mohl by v Cronneru existovat systém,
který by si udržoval mapu úloha => další spuštění
. Pokud
by mapa neexistovala, prostě by se nově vytvořila. Pak by samotný Cronner
umožňoval dotaz na nejbližší čas spuštění nějaké úlohy. Pak by bylo
možné relativně rychle ověřovat nutnost spouštění Cronnera. Co si o tom
myslíte?
Editoval stekycz (3. 3. 2013 16:15)
- Caine
- Člen | 216
Myslel jsem to jinak, to retezeni by se chovalo jen jako cron, tj periodicky by si to kontrolovalo seznam uloh ke spusteni a ty pripadne spoustel (otazka je, jak je spoustet paralelne ze scriptu). Uloha sama by si kontrolovala, jestli ma vyznam spoustet dalsi svy akce – tedy jako u normalniho crona.
- duskohu
- Člen | 778
Ahoj, Mam jeden taky navrh :-)
Dnes som narazil na problem ked mam niekolko Taskov v rade vsetky maju povolene
sa spustat v tom istom case a vsetky dost dlho bezia. Bud zbehnu za 10 sec, to
znamena ze uz nemaju co riesit, alebo bezia 40–50 sec. co znamena ze stale
nieco riesia.
Na vela servroch je set_time_limit
zakazane, takze toto sa neda pouzit.
To spozôbi ze 2 task mi padne. Neda sa nejako sledovat ako dlho bezal task a
v pripade ze bezal nad limit (parameter) tak dalsi nespusti?
- MartinitCZ
- Člen | 580
@**duskohu**: Spíš než nesputit by bylo lepší jeho spuštění šoupnou. Sputit CRON B až po dokončení CRON A. Ale to už by bylo až moc složité, jelikož by se muselo monitorovat a někde „střádat“, co se aktuálně děje, resp. co Cronner dělá …
- duskohu
- Člen | 778
Skusal som nieco taketo, lenze toto asi nepojde kedze cez callback neviem ovplivnit foreach Cronnera.
class CronPresenter extends Presenter
{
.......
/** @var string */
private $startTime;
/**
* ACTION - Default
*/
public function actionDefault()
{
$this->cronner->onTaskBegin[] = function ($cronner, $task) {
if (!$this->startTime) {
$this->startTime = microtime(TRUE);
} else {
$end = microtime(TRUE);
$time = $end - $this->startTime;
if ($time > 3) { // Pocet sekund cez ktore nema presiahnut 1 process
// AKO UROBIM BREAK FOREACH?
}
}
};
$this->cronner->addTasks($this->cron1);
$this->cronner->addTasks($this->cron2);
$this->cronner->run();
$this->terminate();
}
}
- stekycz
- Člen | 152
Něco podobného by určitě nebyl problém přidat. Otázka spíš je, zda by to opravdu přineslo požadovaný výsledek, tedy snížení množství errorů nebo jejich úplnou eliminaci.
Tvůj návrh fungovat bude, ale pokud samotný úkol potrvá déle, než kolik je maximální doba běhu určená serverem, chybě se nevyhneš. Stejně tak se chybě nevyhneš, když budeš mít (pro uvedený příklad) víc jak 20 tasků a každý potrvá 2.9 sekundy s nastaveným max_time na 1 minutu.
O něco více se mi líbí co nastínil @martinit.
Tedy připravit interface/logiku, která bude postupně střádat délku běhu
jednotlivých úkolů a bude počítat průměr doby běhy a na základě toho
bude plánovat, které úkoly v daném běhu pustí a které ne. Využít by
k tomu bylo možné již existujícího interface
ITimestampStorage
. Samozřejmě by bylo potřeba i nastavit
maximální dobu běhu (kterou je možné nastavit už teď, jen by byla
využita i jinak).
V tuhle chvíli si může udělat následující:
// Důležité je dědit od RuntimeException, které Cronner nepřeskakuje.
class CronnerInterruptedException extends \stekycz\Cronner\RuntimeException {}
class CronPresenter extends Presenter
{
/** @var string */
private $startTime;
/**
* ACTION - Default
*/
public function actionDefault()
{
$this->cronner->onTaskBegin[] = function ($cronner, $task) {
if (!$this->startTime) {
$this->startTime = microtime(TRUE);
} else {
$end = microtime(TRUE);
$time = $end - $this->startTime;
if ($time > 3) { // Pocet sekund cez ktore nema presiahnut 1 process
// Zavolá se callback $cronner->onTaskError;
throw new CronnerInterruptedException();
}
}
};
$this->cronner->addTasks($this->cron1);
$this->cronner->addTasks($this->cron2);
try {
$this->cronner->run();
} catch (CronnerInterruptedException $e) {
// Tady už víš, že Cronner foreach skončil
}
$this->terminate();
}
}
Updated: 2013–11–12 1:21
Editoval stekycz (12. 11. 2013 1:21)
- duskohu
- Člen | 778
@stekycz cau, uspesne pouzivam cronner. V posleddnej dobe som narazil na jeden problem. Mam definovene pravidlo:
/**
* @cronner-task Synchronize store
* @cronner-period 1 day
* @cronner-time 07:00 - 23:55
*/
Problem je v tom ze sa mi kazdy den spusta o hodinu neskor a neskor a nie
kazdy den po 07:00, ale stale o hodinu neskor ako den pred. nevies mi poradit
ako to riesit? Dik.
…
20.7 14:01
21.7 15:01
22.7 16:01
23.7 17:00
…
Edit: pouzivam Nette 2.1
Editoval duskohu (24. 7. 2014 10:21)
- stekycz
- Člen | 152
@duskohu Osobně si myslím, že problém bude někde v kontrole na spuštění úkolu. Možná bude problém v času, který se nastaví jako poslední spuštění. Nicméně předpokládám, se ti cronner zpracuje každou 1 hodinu, že?
- duskohu
- Člen | 778
@stekycz cau, takze $lastRunTime mi vracia datum posledneho spustenia tasku, tento datum sedi, takze v tom to nebude.
- duskohu
- Člen | 778
Cau, takze som si dal nejaky debug $now, $lastRunTime a saveRunTime a vymazal som cron cache nechal som to bezat nanovo. Zadanie pre cron bolo toto, pricom cron bezi kazdu hodinu.
/**
* @cronner-period 1 day
* @cronner-time 07:00 - 23:55
*/
vystup bol takyto:
[2014–08–02 07–01–06] $now:2014–08–02 07:01:06;
$lastRunTime:null
[2014–08–02 07–01–10] saveRunTime:2014–08–02
07:01:10
[2014–08–02 08–01–10] $now:2014–08–02 08:01:10;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 09–00–54] $now:2014–08–02 09:00:54;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 10–01–07] $now:2014–08–02 10:01:07;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 11–00–51] $now:2014–08–02 11:00:51;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 12–01–42] $now:2014–08–02 12:01:42;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 13–00–54] $now:2014–08–02 13:00:53;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 14–01–10] $now:2014–08–02 14:01:10;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 15–00–59] $now:2014–08–02 15:00:59;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 16–01–16] $now:2014–08–02 16:01:16;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 17–00–46] $now:2014–08–02 17:00:46;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 18–01–15] $now:2014–08–02 18:01:15;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 19–00–57] $now:2014–08–02 19:00:57;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 20–01–14] $now:2014–08–02 20:01:14;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 21–00–55] $now:2014–08–02 21:00:55;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 22–01–07] $now:2014–08–02 22:01:07;
$lastRunTime:2014–08–02 07:01:10
[2014–08–02 23–01–01] $now:2014–08–02 23:01:01;
$lastRunTime:2014–08–02 07:01:10
[2014–08–03 07–01–00] $now:2014–08–03 07:01:00;
$lastRunTime:2014–08–02 07:01:10
[2014–08–03 08–01–15] $now:2014–08–03 08:01:15;
$lastRunTime:2014–08–02 07:01:10
[2014–08–03 08–01–16] saveRunTime:2014–08–03
08:01:16
[2014–08–03 09–00–52] $now:2014–08–03 09:00:52;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 10–01–06] $now:2014–08–03 10:01:06;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 11–00–51] $now:2014–08–03 11:00:51;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 12–01–41] $now:2014–08–03 12:01:41;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 13–00–52] $now:2014–08–03 13:00:52;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 14–01–02] $now:2014–08–03 14:01:02;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 15–01–01] $now:2014–08–03 15:01:01;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 16–01–08] $now:2014–08–03 16:01:08;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 17–00–50] $now:2014–08–03 17:00:50;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 18–01–10] $now:2014–08–03 18:01:10;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 19–00–52] $now:2014–08–03 19:00:52;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 20–01–11] $now:2014–08–03 20:01:11;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 21–00–54] $now:2014–08–03 21:00:54;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 22–01–06] $now:2014–08–03 22:01:06;
$lastRunTime:2014–08–03 08:01:16
[2014–08–03 23–01–00] $now:2014–08–03 23:01:00;
$lastRunTime:2014–08–03 08:01:16
[2014–08–04 07–01–05] $now:2014–08–04 07:01:05;
$lastRunTime:2014–08–03 08:01:16
[2014–08–04 08–01–12] $now:2014–08–04 08:01:12;
$lastRunTime:2014–08–03 08:01:16
[2014–08–04 09–01–07] $now:2014–08–04 09:01:07;
$lastRunTime:2014–08–03 08:01:16
[2014–08–04 09–01–07] saveRunTime:2014–08–04
09:01:07
[2014–08–04 10–01–08] $now:2014–08–04 10:01:07;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 11–01–01] $now:2014–08–04 11:01:01;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 12–01–43] $now:2014–08–04 12:01:43;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 13–01–07] $now:2014–08–04 13:01:07;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 14–01–15] $now:2014–08–04 14:01:15;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 15–01–03] $now:2014–08–04 15:01:03;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 16–01–12] $now:2014–08–04 16:01:12;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 17–01–08] $now:2014–08–04 17:01:08;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 18–01–20] $now:2014–08–04 18:01:19;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 19–01–08] $now:2014–08–04 19:01:08;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 20–01–13] $now:2014–08–04 20:01:13;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 21–00–48] $now:2014–08–04 21:00:48;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 22–01–12] $now:2014–08–04 22:01:12;
$lastRunTime:2014–08–04 09:01:07
[2014–08–04 23–01–10] $now:2014–08–04 23:01:10;
$lastRunTime:2014–08–04 09:01:07
Editoval duskohu (4. 8. 2014 23:27)
- stekycz
- Člen | 152
@duskohu Omlouvám se zpožděnou odpověď. Založil jsem si na to issue. Měl bych se k tomu tento týden dostat.
@Azathoth S vydáním verze 1.0.0 otálím už asi měsíc nebo dva. Přemýšlím, jestli je ten pull request na paralelní běh ok. Zatím jsem ale nepřišel na lepší řešení nebo na možný problém, který by byl řešitelný. Za případné náměty budu rád :-) Plánuju na Cronneru pracovat, ale především co se týče opravách bugů. Sám totiž příliš nevím, jaké další funkce navíc by mohl někdo využít. Takže námětům i pull requestům se meze nekladou :-)
- stekycz
- Člen | 152
Snažím se v balíku držet maximum zpětné kompatibility. Sám ale jedu
na nejnovějších stabilních verzích a tak mi může něco uniknout. Podle
dotazů předpokládám, že bude stačit na pár míst přidat
class_alias
, protože většina problémů bude nejspíše kvůli
rozdělení Nette na několik balíčků. Nicméně ze stejného důvodu pro
tyto verze nejspíš vytvořím speciální tagy (kvůli závislostem). Založil
jsem si issue, kouknu
na to během týdne (možná už dnes).
Díky za reporting!
- Azathoth
- Člen | 495
Bohužel nejnovější verze padá, řádek 79, založil jsem to jako issue,
ale dávám to pro jistotu i sem:
Notice
Undefined index: debugMode
File: …\vendor\stekycz\cronner\Cronner\DI\CronnerExtension.php:79
69: $config[‚criticalSectionTempDir‘]
70: ))
71: ->setAutowired(FALSE)
72: ->setInject(FALSE);
73:
74: $runner = $container->addDefinition($this->prefix(‚runner‘))
75: ->setClass(‚stekycz\Cronner\Cronner‘, array(
76: $storage,
77: $criticalSection,
78: $config[‚maxExecutionTime‘],
79: !$config[‚debugMode‘],
80: ));
81:
82: foreach (array_keys($container->findByTag(self::TASKS_TAG)) as
$serviceName) {
83: $runner->addSetup(‚addTasks‘, array(‚@‘ . $serviceName));
- duskohu
- Člen | 778
Ked mam: nette-2.1
cronner:
timestampStorage: stekycz\Cronner\TimestampStorage\FileStorage(%wwwDir%/cronner)
tak to neprejde cez toto
Notice
Undefined property: stdClass::$entity
lebo v $config[‚timestampStorage‘] je:
stdClass #59e3
value => "stekycz\Cronner\TimestampStorage\FileStorage" (44)
attributes => array (1)
0 => "/data/web/virtuals/30211/virtual/www/subdom/agrokom-shop/www/cronner"
Editoval duskohu (20. 8. 2014 17:36)
- Azathoth
- Člen | 495
Ahoj,
tak se snažím rozchodit cronner a mám několik velmi hloupých otázek, tak
mne prosím, neukamenujte:
- úplně jsem nepochopil ty tagy:
At the end you would need to specify your task objects. It would be some service with high probability. You can add tag cronner.tasks to all services with Cronner tasks and those services will be bind automatically. However you can still add new task objects by your own using addTasks method.
Kam konkrétně patří ten tag? Pochopil jsem to ta, že se to má dát do config.neon, ale úplně si nejsem jist, protože používání tagů v souvislosti s DI mi nějak uniklo.
- Musím nějak nastavovat cron, aby to fungovalo?
- co všechno je třeba udělat, aby to fungovalo? Přidat do config.neon cronner jako extension a potom jenom přidat do cronneru objekt, který má metody, které chci spouštět, pomocí toho tagu cronner.tasks a u těch metod pak stačí jenom v anotacích definovat, kdy se to má spouštět a to je všechno?
A nebo je lepší mít v presenteru jako závislost cronner, objekt, který má metody, které chci cronnerem spouštět a předat cronneru ten objekt pomocí $cronner->addTasks()?
Těším se na odpovědi.
Editoval Azathoth (28. 8. 2014 11:20)
- duskohu
- Člen | 778
@Azathoth neviam comu uplne nerozumies. Vsetko je pekne popisane v dokumentaci.
Ale skusim ti to zopakovat, aj ked len prepisujem to co uz niekto napisal:
1. zaregistrujes si extension v neone:
extension:
cronner: stekycz\Cronner\DI\CronnerExtension
2. injectnes si cronner do presentru:
class CronPresenter extends \Nette\Application\UI\Presenter {
/**
* @var \stekycz\Cronner\Cronner
* @inject
*/
public $cronner;
public function actionCron() {
$this->cronner->run();
}
}
3. mas moznost registrovat tasky v presentru, alebo v neone, ja si injectnem sluzby kde mam tasky do presentru a tam pridam cronerru tasky.
services:
CronTasks
class CronPresenter extends \Nette\Application\UI\Presenter {
/**
* @var \stekycz\Cronner\Cronner
* @inject
*/
public $cronner;
/**
* @var CronTasks
* @inject
*/
public $cronTasks
public function actionCron() {
$this->cronner->addTasks($this->cronTasks);
$this->cronner->run();
}
}
4. Classa CronTasks ma tasky, tie definujes na zaklade anotaci:
class CronTasks {
/**
* @cronner-task E-mail sending
* @cronner-period 1 day
* @cronner-days working days
* @cronner-time 23:30 - 05:00
*/
public function sendEmails() {
// Code which sends all your e-mails
}
/**
* @cronner-task Important data replication
* @cronner-period 3 hours
*/
public function replicateImportantData() {
// Replication code
}
}
- Azathoth
- Člen | 495
Moc děkuji, ale ještě se chci ujistit:
Takže cron vůbec nijak nastavovat nemusím?
A ještě k tomu presenteru…zase hloupá otázka: já myslel, že actionCron
se (pokud jsem správně pochopil nette) spustí pouze, když někdo navštíví
www.example.com/Cron/cron. Nebo ne? A má na cronner
nějaký vliv, když někdo navštíví nebo nenavštíví www.example.com/Cron/cron a zavolá se tedy actionCron?
- stekycz
- Člen | 152
Omlouvám se za to, že jsem nereagoval, byl jsem na dovolené.
@duskohu Podívám se na problémy co nejdříve to půjde.
@Azathoth Cron nastavit musíš. Cronner je jen možnost, jak v rámci aplikace jednotlivé úkoly spravovat. Samotný běh můžeš pouštět buď v presenteru přes nějakou URL nebo přes CLI. Pro CLI mohu doporučit použití Kdyby/Console. Jak to použiješ záleží na tobě.
Hodně základních hostingů umožňuje pouštět Crona pouze po nějakém
intervalu a pouze jednu úlohu. Přesně k tomu je Cronner určený. Navíc
většina takovým akcí je pouštěná přes curl a proto je použití
v presenteru možné. Nicméně pokud to potřebuješ takhle udělat, je
vhodné filtrovat danou akci na IP adresy a pokud není localhost (nebo jiná
povolená IP adresa), tak vrátit 404. Pokud použiješ CLI a Kdyby/Console, není filtr potřeba,
ale většinou potřebuješ nastavit cron sám přes crontab
.
Tagy u služby jsou metadata, které je možné použít třeba právě v jednotlivých rozšířeních. Je to součást nette/di. Teoreticky bych mohl nadefinovat interface bez metod a podle něj si služby vytahat. Nicméně je to zbytečná definice rozhraní a následně bys to musel ty implementovat. Mnohem jednodušší je přidat do configu k existující službě tag. Pokud tagy používat nechceš, musíš Cronneru předat jednotlivé objekty jak výše popisuje @duskohu. Tagy jsou v Cronneru až od verze 1.0.
- David Matějka
- Moderator | 6445
@duskohu @Desttro cronner nepouzivam, ale imho to pude vyresit prohnanim $config v compiler extension fci Compiler::filterArguments, tak si to zkuste a kdyztak poslete stekymu pull request
- Desttro
- Člen | 126
Azathoth napsal(a):
@Desttro Nechceš být trochu konkrétnější s tou chybou? Kde to vyskakuje?
duskohu napsal(a):
Ked mam: nette-2.1
cronner: timestampStorage: stekycz\Cronner\TimestampStorage\FileStorage(%wwwDir%/cronner)
tak to neprejde cez toto
Notice Undefined property: stdClass::$entity
lebo v $config[‚timestampStorage‘] je:
stdClass #59e3 value => "stekycz\Cronner\TimestampStorage\FileStorage" (44) attributes => array (1) 0 => "/data/web/virtuals/30211/virtual/www/subdom/agrokom-shop/www/cronner"