Autor Zpráva
JunekCZ
Profil
Dobrý den,
snažím se vytvořit script, který např. za hodinu ověří, jestli uživatelovi v DB skončilo Premium (Neodebralo by se mu až po jeho přihlášení, ale přesně po hodině koupení).

Koukal jsem se na CRONTAB, jenže ten se nepíše přes PHP.
Dále jsem se snažil pochopit microtime, jenže s tím si nejsem jistý.

Mohl by existovat takovýto skript, který by se exekutoval individuálním nákupem uživatele?

<?php
  $time = time();
  sleep(3600);
  if($time + 3600 == time()) {
    runCode();...
   };
?>

Předem děkuji za odpověď,
JunekCZ
Keeehi
Profil
JunekCZ:
Teoreticky ano, prakticky ne.

Správně je ten cron. Cron se opravdu nepíše přes PHP, ale cron ti může spustit nějaký script. Třeba ten co si v PHP vytvoříš.

Neodebralo by se mu až po jeho přihlášení, ale přesně po hodině koupení
A proč je toto důležité? To je hlavní otázka.
JunekCZ
Profil
Keeehi:
Děkuji za rychlou odpověď.

Při tomto případě by to bylo důležité z hlediska výpisu počtu lidí s prémium účtem.. bylo by divné, kdyby se nekativní premium účet zobrazoval i po dvou měsících ve výpisu.

Každopádně to byl pouze příklad.

Snažím se vytvořit script, který vypne po měsíci nezaplacení virtuální server (Dejme tomu klasický game hosting).

Cron by při tomto příkladu nefungoval (Už jenom proto, že to běží na windowsech by s tím byl určitě problém), a i kdyby ano, musel by se při každém zaplacení pustit nový cron, což jsem posoudil jako nereálné.

Jaké by bylo tedy řešení bez crontabu?
juriad
Profil
JunekCZ:
Použij cron, nebo windowsí plánovač, to je jedno. Prostě něco, co zavolá tvůj PHP skript každou hodinu. Můžeš si zvolit i jiný interval.

Veškerá logika bude v PHP skriptu, který zjistí, co vše je potřeba za poslední hodinu provést. Může to být odstranění uživatele ze seznamu "premium", může to být zrušení virtuálního serveru, může to být kombinace.
Budeš mít jednu tabulku pojmenovanou udalosti, která obsahuje vše co se kdy v budoucnu má provést: (id, pokus, zpracovano, typ, uzivatel, param1, param2, param3). Podle této tabulky budeš pravidelně provádět akce, které "už měly být" a ještě nebyly zpracované.

Taková hrubá kostra:
SELECT * FROM udalosti WHERE datum <= NOW() AND zpracovano = 0 AND pokus < 5
foreach (udalosti as udalost) {
  try {
    if udalost.typ == 'zrusit vps'
      zrus_vps(udalost.param1)
    if udalost.typ == 'vytvor.vps'
      vytvor_vps(udalost.param1, udalost.uzivatel)
    if udalost.typ == 'konec premium'
      konec_premium(udalost.uzivatel)
    UPDATE udalost SET zpracovano = 1 WHERE id = udalost.id
  } catch (Exception $e) {
    UPDATE udalost SET pokus = pokus + 1 WHERE id = udalost.id
    log.error($e);
  }
}
JunekCZ
Profil
juriad:
Toto se zdá jako správné řešení.
Problém ovšem nastane, pokud se za jednu hodinu porovná hodně událostí.

Nevím jak by se např. server choval, pokud by se každou hodinu porovnávalo 200 událostí.

Má myšlenka byla taková, že (pokud bereme vps) uživatel zakoupí a tím spustí odpočítávání.
To by se vyhlo porovnávání několika záznamů najednou (nevím ovšem, jestli by tento způsob s odčítáváním nedopadl stejně).

Unesl by toto server s větším počtem tabulek, nebo je "má teorie" optimálnější?

Mnohokrát děkuji za pomoc.
juriad
Profil
JunekCZ:
Nechceš žádné dlouho běžící skripty. Alespoň ne ty, které nemůžeš restartovat. Určitě chceš ty události někam zapsat a nemít odpočet, protože ten se může snadno narušit (restart serveru, příliš mnoho procesů).
Kdybys nastartoval časovač na měsíc a za 14 dnů zjistil, že v tom běžícím skriptu je chyba, tak ho musíš nějakým způsobem restartovat. Co však když jich budou desítky s různým zbývajícím časem?

Pokud se obáváš, že bude událostí příliš, tak můžeš jednoduše spouštět ten skript častěji, třeba každou minutu (pozor na to, aby ten skript nezačal dřive než skončí předchozí). Když žádná událost nebude, tak jeden dotaz za minutu nevadí.

Můžeš to upravit, tak aby to fungovalo paraleleně, v takovém připadě to může běžet na mnoha serverech najednou (s připojením do stejně databáze). Pak je třeba chytře pracovat s transakcemi a zajistit, že jeden záznam nebude zpracován vícekrát a že v připadě chyby zůstane v tabulce událostí pro pozdější pokus. (Budou tedy stavy: nezpracovaný, zpracovávaný, zpracovaný.)

Jak přesně to bude navržené záleží, jaké garance od toho chceš. Například, pokud vytvoření VPS je akce, která musí být spolehlivá a rychlá, tak můžeš zajistit, že ji bude paralelně zpracovávat 5 serverů a první, který jej vytvoří vyhraje a výsledek uloží. Zbylé VPS se zahodí. Pokud zjistíš, že jsi měl chybu ve skriptu zpracovávající nějaký typ události, tak jednoduše UPDATEm v databázi změníš všechny události toho typu na nezpracované a počkáš až se systém o ně postará. (Je dobrý nápad naprogramovat zpracování události tak, aby nevadilo jeho opakované spuštění.)

Tím, že rozdělíš vytvoření události od zpracování události, získáš ohromnou svobodu v rozhodování, co se má stát.
kolemjdouci
Profil *
A když přidáš do databáze uživatelů sloupec "expire", kde bude datum ukončení a vypisovat budeš podle aktuálního datumu, který se porovnává s expire. Pak by vše fungovalo jak potřebuješ. Pokud si někdo prodlouží, tak se přepíše "expire".
Keeehi
Profil
JunekCZ:
Při tomto případě by to bylo důležité z hlediska výpisu počtu lidí s prémium účtem.
Tak by přece neměl být problém projí uživatele a odstranit premium nejen při přihlášení uživatele ale i při výpisu lidí ;)

Ale teď k novému problému
musel by se při každém zaplacení pustit nový cron, což jsem posoudil jako nereálné.
Ano, pouštět nový cron je nereálné. Ovšem není to nutné. Stačí mít jednu cronovou událost, která bude spouštět script co bude obsluhovat všechny účty. Bylo by opravdu hloupé vytvářet zvláštní script pro každý účet.

Nevím jak by se např. server choval, pokud by se každou hodinu porovnávalo 200 událostí.
To záleží na tom, jak dlouho trvá vyřídit jednu událost. Udělat update 200 řádů v databázi je trivialita a není kvůli tomu potřeba nic vymýšlet. Pro dlouho-běžící události, které je taky v případě selhání potřeba restartovat atp. se používají fronty. Jednotlivé Joby ve frontě se pak vyřizují paralelně. Vypadalo by to asi takto:
Cron jednou za nějaký čas (třeba hodinu) spouští script casovac.php. Ten se koukne do tabulky uživatelů a zjistí, kdo má vyexpirováno. Vrácené uživatele projde a vytvoří pro každého Job pro smazání jeho prémiového statusu a tyto Joby vloží do fronty. Tím končí jeho činnost. Až bude znovu (za hodinu) zavolán, tak udělá to samé, jen pro uživatele, kterým to nově vyexpirovalo.
No a co je to ta kouzelná fronta a joby? Fronta je vlastně nějaký dlouho běžící program/script, který se dívá do nějakého uložiště (databáze) a když se mu tam něco objeví (Job), tak to zpracuje. Když se mu tam objeví více Jobů, tak je zpracovává postupně v chronologickém pořadí. Takový velmi jednoduchý příklad fronty vlastně napsal juriad v [#4]. Lepší bude ale použít už nějakou vytvořenou.
No a Job je pak zase nějaký kód, který fronta spustí, předá mu potřebné parametry, on udělá nějakou práci (reálné odstranění statusu v databázi, vypnutí server, ...) a skončí.
JunekCZ
Profil
Dobrý den,
vytvořil jsem událost v task scheduleru, ale pouštělo mi to stále prohlížeč.
Už jsem našel podobné řešení, ale mohu to udělat pouze díky vaší pomoci.

Našel jsem stránku cron-job.org, kde se mi script zavolá z jejich stránky.
Teď přichází ta php/sql část, kterou jsem obdržel od vás.

Mnohokrát děkuji za pomoc,
JunekCZ
M4n
Profil *
Obecně si myslím, že cron je potřeba jen pro náročné úlohy, které by zbytečně brzdily klienty (velké výpočty) nebo pro úlohy, které jsou na klientech nezávislé (odesílání mailů).

Tvůj případ není ani jedno ani druhé, použití cronu nedává smysl. Výpočet můžeš provést až ve chvíli, kdy je potřeba zobrazit jeho výsledek — a hodnotu klidně uložit na nějakou dobu do nějaké keše, aby se neprováděl zbytečně často.
Keeehi
Profil
JunekCZ:
vytvořil jsem událost v task scheduleru, ale pouštělo mi to stále prohlížeč.
Ty jsi tam dával URL, že? Správně by to mělo být něco jako php C:\server\data\sites\example.com\app\Commands\scheduler.php
Tu cestu jsem si samozřejmě vymyslel

Našel jsem stránku cron-job.org, kde se mi script zavolá z jejich stránky.
Úplně zbytečně jsi teď závislý na službě třetí strany.Když nastane jakýkoli výpadek (u nich, v síti, ...) který by tě normálně nezasáhl, tak se ti přestanou spouštět úlohy. Kdyby to bylo něco pro zábavu tak prosím ale ty na tom chceš založit placenou službu.

M4n:
Tvůj případ není ani jedno ani druhé
No tak zrovna management serverů může patřit do obou.
JunekCZ
Profil
M4n:
Záleží na tom co si pod slovem naročný představujete..
Hosting bych nebral zrovna jako malou věc.

Keeehi:
Rozumím-li tomu správně, pak prefix php spustí script bez okna.. prostě vykoná.

Úplně zbytečně jsi teď závislý na službě třetí strany.
Samozřejmě to byla nevýhoda.

Už vše funguje, prosím o uzavření ticketu.

Děkuji za všechny návrhy,
JunekCZ
M4n
Profil *
JunekCZ:
Záleží na tom co si pod slovem naročný představujete..
Povedzte Kefalín, čo vy si predstavujete pod takým slovom absurdné?

Správně sestavená databáze dokáže porovnat miliony DATETIME klíčů za pár setin sekundy. Na sečtení záznamů podle časového sloupce nepotřebuješ CRON. Tečka, konec debaty.
Keeehi
Profil
JunekCZ:
Rozumím-li tomu správně, pak prefix php spustí script bez okna.. prostě vykoná.
To ani tak není prefix jako název programu a ta cesta je jeho parametr. Ale spíš jsem to tak střelil, několik let už výhradně pracuji v Linuxu a ve Windows tedy takové problémy neřeším. Ale v jistých ohledech jsou si podobné.
Nicméně, přímo PHP dokumentace uvádí, jak má vypadat příkaz na spuštění scriptu pod Windows
C:\PHP5\php.exe -f "C:\PHP Scripts\script.php"
JunekCZ
Profil
M4n:
Děkuji za příjemnou odpověď.
I když se tu už několik odpovědí taháme s tím, že se použijeme taskscheduler, jsem rád, že se do debaty stále zapojujete.

Máte tedy jiné řešení, nebo nás tu stále chcete poučovat o CRONu?

Keeehi:
Rozumím, jen jsem si to zjednodušoval.
Děkuji za pomoc.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

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