Elfogy a memória PHP alól
Sziasztok!
A segítségemet kérték egy oldalnál, ahol a galéria furcsa módon néha megjeleníti az összes képet, néha meg kihagy párat. A rendszergazda szerint az a gond, hogy elfogy a memória a PHP alól, de én ebben nem vagyok 100%-ig biztos, mondom is miért. Eddig phpThumb gyártotta le a galéria képek kis méretű változatát, de mivel ennek a kódját nem ismerem annyira, írtam egy nagyon egyszerű, saját átméretező scriptet, ami ha nem létezik megadott méretű kis kép, legenerálja azt, ellenkező esetben csak beolvassa a fájlt, majd kitolja az outputra. Ami egészen biztos, hogy most már az összes képnek legenerálta a kis méretű változatát, így azokat csak be kell olvasni, mégis van olyan, hogy egy 5 képes (maximum képméret 30 Kb) album esetén 2-3 képnél is meghal a script. Szerintetek ez lehetséges, hogy ennyitől elfogyna a memória? Azt nem értem, ha elfogy a maradék process miért nem várakozik kicsit, amíg felszabadul elég memória a futáshoz, ahelyett hogy egyből elszáll 500-as erroral.
(ha jól tudom 256 mb memóriával fut a vps)
■ A segítségemet kérték egy oldalnál, ahol a galéria furcsa módon néha megjeleníti az összes képet, néha meg kihagy párat. A rendszergazda szerint az a gond, hogy elfogy a memória a PHP alól, de én ebben nem vagyok 100%-ig biztos, mondom is miért. Eddig phpThumb gyártotta le a galéria képek kis méretű változatát, de mivel ennek a kódját nem ismerem annyira, írtam egy nagyon egyszerű, saját átméretező scriptet, ami ha nem létezik megadott méretű kis kép, legenerálja azt, ellenkező esetben csak beolvassa a fájlt, majd kitolja az outputra. Ami egészen biztos, hogy most már az összes képnek legenerálta a kis méretű változatát, így azokat csak be kell olvasni, mégis van olyan, hogy egy 5 képes (maximum képméret 30 Kb) album esetén 2-3 képnél is meghal a script. Szerintetek ez lehetséges, hogy ennyitől elfogyna a memória? Azt nem értem, ha elfogy a maradék process miért nem várakozik kicsit, amíg felszabadul elég memória a futáshoz, ahelyett hogy egyből elszáll 500-as erroral.
(ha jól tudom 256 mb memóriával fut a vps)
FirePHP
Ha a HTML oldalt generáló
Igaz
Meg simán a böngszőablakban
Nincs hibaüzenet
Ha kézzel generálsz egy fatal
nincs_ilyen_fuggveny()
), akkor van? CLI-ből is?Igen, van
Apache hibalog (meghaló child
Illetve ha az OOM killer öli
Ha van!
Azt nem értem, ha elfogy a
Mert amíg várakozik, foglalja a memóriát -> deadlock.
Nem értem
Sehogy, ezért kellemetlen, ha
Leírom egyértelműbben
Ha a "kéne" alatt azt érted,
Ha azt érted alatta, hogy nem így viselkedik, de szerinted így okosabb lenne, akkor meg azért nem, mert így amikor az első két szkript megette az összes szabad memóriát, akkor elkezdenének egymásra várni, hogy mikor fejeződnek be, te meg kelhetnél fel az éjszaka közepén újraindítani a webszervert, mert magától ebből nem mászna ki (vö. deadlock).
Memoria
Egyebkent elnezest, de kepeket barmilyen szinten beolvasni oldal rendereles kozben a vilag legnagyobb baromsaga, mert a PHP kepkezelo rutinjai borzaszto modon memoriaigenyesek es ami meg fontosabb, nem mindig veszik figyelembe a memory_limitet. A PHP ugyanis ket parancs vegrehajtas kozott ellenorzi az elfoglalt memoriat, az extensionok fuggvenyeinek futassa kozben viszont nem. Ergo a kepkezelo rutin siman tullepheti a memory_limitet, ha szarul van megirva.
Nem vitatva, hogy rossz
Nyilvan
Alapvetően rossz ötlet a
Melyik része?
Tisztában vagyok vele, de így kell megoldanom a feladatot!
Melyik része hibás? Az hogy beolvasok egy fájlt és kiiratom? :) Egyébként nem titkos a kód, úgyhogy: képkezelő class és a generáló script.
Én két centem
Én legelőször azt nézném meg, hogy van-e thumbnail az adott file-ból mielőtt még ImageMagick objektumokat generálnék.
Aztán az ilyen képméretek lekérdezéséhez sincs szükség ImageMagick-ra. getimagesize()
Mondanom se kellene, de azért mégis: a bemenetet nem használnám ilyen nyersen.
Aztán, ha ez valami képgaléria lenne, érdemes inkább rendszerbe foglalni, mert így ez nagyon bindzsi. Gondolom valaki html-be pötyögi a képek url-jét, ha pedig nem, mert valahonnan kapja akkor ott érdemesebb lenne optimalizálni az egész folyamatot.
Az ImageMagick...
Az ImageMagick objektum egészen pontosan 1296 byte memóriát eszik, az azért nem olyan vészes
Mivel a használt class ImageMagick-re épül és az előbb említett egészen bődületes méretű memóriát megeszi, ezért gondoltam, hogy az egységesség jegyében használom erre is a már legyártott objektum metódusát.
Mondj egy példát kérlek, amikor az input problémát okoz!
A kód még közel sem production ready, egyrészt. Másrészt engem az érdekelne, hogy miért száll el a script, alternatív megoldásom van nekem is :)
Awwww....
Minden egyes képet beolvas, ergo pixelenként tárolja a memóriában. Nem 30kbyte-al meg 1296byte -nak a többszörösével kell számolnod, hanem sokkal többel.
Rossz a következtetés az elözőből kifolyólag, a többit már tudod.
Állásinterjún majd ne ezt válaszold, ha felvetik problémaként.
A rendszergazda megmondta, a script-edból látszik. Nem tudom mi kell még. Nyilván a pontos leállás okát nem a forrásból fogjuk megmondani... Ha logjaid nincsenek akkor már próbálgathatod is az alternatív megoldásaidat.
Re
Tévedsz! Az objektum inicializálás önmagában nem foglalja le a kép teljes (vagy raw) méretét a memóriában. De ha nem hiszed el végezz el egy tesztet!
Ez nem válasz a kérdésre, hanem terelés. Egyébként ismerem a filter_var és filter_var_array függvényeket (és az egyéb filterezési és validálási best practice-eket), és szoktam is használni őket. Szóval mondasz egy olyan input halmazt, amivel a rendszer "törhető" lesz?
Igen, a rendszergazda megmondta, csak nekem szöget ütött a fejembe, hogy 2-3 30 Kb-os kép beolvasása és kiiratására hatására hogy a fenébe fogyhat el a memória :)
Az ember csak segíteni próbál, de azt is nehezen hiszik el
Még ha nem is a memória miatt van akkor is bemutatsz egy csomó felesleges komplickációt egy külső php modul meghívásával, lehet, hogy attól szarik be a rendszer, mert szarul van vmi konfigurálva. Én se írtam, hogy memória, csak gondolom, mert elég valószínű. Vettem a fáradtságot, megnéztem, és ezt láttam benne, de nem muszáj elhinni.
2. Leszarom ezt a szánalmas okfejtésedet a terelésről. Ebbe a rossz szokásba ahányszor látom annyiszor fogok belerúgni. Nem törhetőségről van szó, szerinted ezen mi a faszt törnek fel? Megbízható kódról van szó, érted? Ahol inputokat nem hagynak nyersen, nem csak azért, hogy ne lehessen injektálni, hanem azért mert szűkited a hiba lehetőségét. Gondolom, hogy nem a NASA-nak írsz gépgalériát, de azért bocs, hogy megjegyeztem.
Annyira vicces, hogy ír az ember egy tőmondatot és már a kis elméjével az olvasó hozzágondol 234234978234897 prekoncepciót, aztán magyarázkodhatsz sorokon keresztül.
3. Sok sikert kívánok akkor.
Köszi!
Köszönöm, és az észrevételeket is!
Olyannyira...
Memoria limit
Esetleg postolj mar egy phpinfo kimenetet.
PhpInfo
A phpinfo kimenet: http://szabogabor.net/blog_files/phpinfo.html.
Parhuzamos
Egyebkent milyen okbol kifolyolag van RAM-korlatos virtualis gepben a PHP? Tarhelyeknel szokas burstolni hagyni az ugyfelet.
Tipp: ha gyors megoldast akarsz, csinalj egy flock() hivast egy filera es akkor egyszerre csak egy kep dolgozodik fel, a tobbi var.
Egyebkent milyen okbol
Azt nem tudom, csak azt, hogy erről a szolgáltatásról van szó: Cloud 20 csomag
Köszi, mindenképp azt szeretném megoldásképpen, amit tgr is javasolt, hogy webszerverről szolgálom ki a képet (mármint a php kihagyásával ha lehetséges, ha pedig nincs még generálva, akkor php-ból meggenerálom). Ez volt az eredeti terv is. Alapvetően engem az érdekelne, hogy mi okozza a problémát, mert ilyet még nem láttam.
Mondj egy példát kérlek,
allow_url_fopen=1 mellett tetszőleges forrásfájlt meg lehet neki adni, valamelyik EXIM mezőbe, amit megőriz az imagemagick, PHP kódot írni, és meg is törték a webszerveredet.
Ebbe azért én is belegondoltam
De mégegyszer elmondom: a kód nem production ready, lesz benne még ellenőrzés, csak tesztelésképpen írtam, hogy ugyanaz történik-e mint a phpThumbnail esetén.
Én legelőször azt nézném meg,
A normális megoldás az, hogy GET paraméterek helyett a tényleges thumbnail fájlneveket teszed a HTML fájlba, és 404 handlerrel vagy rewrite rule-lal átküldöd a PHP-nek azokat a képletöltéseket, amiknél hiányzik a fájl. (És ha nagy forgalmú a site, akkor teszel bele valami cache stampede védelmet, de nyilván ha nagy forgalmú lenne, akkor nem egy 256 megás virtuális gépről futna.)
Igen így még jobb
Ha nem szabadítod fel a
A memória fogyást ki lehet íratni memory_get_usage-al, próbálkozz azzal a kód egyes részein, meg én a helyedben egy xdebuggal megprofiloznám, ha tényleg ilyen gond van, viszont szerintem csak szimplán valami hiba van a kódodban.
Most nézem, hogy a kódban, amit küldtél php-val szolgálod ki a képeket echo-val, ami szerintem elég pazarló. Egy fájlt simán ki tud küldeni a szerver is, ilyesmire csak akkor van szükség, ha védeni akarod a fájlokat valami miatt...
Nincs átméretezés minden képnél
Másodszor tisztáznám a helyzetet mégegyszer, mert úgy érzem meg kell védenem a mundér becsületét és mert a legtöbb embernek a téma elolvasása után annyi jött le, hogy itt egy balfék, aki php scripttel átméretez minden egyes oldalletöltésnél minden egyes képet. És ne érts félre inferno, ezt nem konkrétan Neked írom. Tehát:
1. Ahhoz, hogy kiderítsem a problémát, a phpThumb helyett kellett csinálnom egy olyan scriptet, ami kb. ugyanazt csinálja és átlátom. Ezt mindössze DEBUGGOLÁSI CÉLBÓL ÍRTAM.
2. Leírtam a témaindító hozzászólásban, hogy nem (csak) az átméretezés során fut hibára a script, hanem egy-egy igen kis méretű fájl beolvasása és kiiratása után is (és most fognak jönni azok a válaszok, hogy "deigenisvanimagemagickmertmegnézedaméretétis" és igen, igazad van, van, de a phpThumb viszont úgy emlékszem, hogy egyből cache-ből tolja a képet, méret ellenőrzés nélkül és az a script IS elszáll)
3. Az volt a kérdés, hogy lehetséges-e ekkora memória fogyasztás egy ilyen algoritmusnál. Nem az, hogy szar-e a kódom, hogy szar-e a logikám vagy hogy mit csinálnátok a raw inputtal. Ehhez képest totál nem erre kaptam választ. Elmondtam ezt is, de leírom mégegyszer: engem az érdekel, hogy miért fut 500-as hibára a script, mert ez akár a többi oldal esetén is okozhat problémát, más scriptek esetén.
És akkor most jöjjön az érdemi válasz része :)
Az egy GD függvény, ha jól emlékszem, ImageMagick-nél elvileg a destruktor automatikusan felszabadítja a memóriát.
30 Kb-os fájlok esetén meg kiváltképp nem. Főleg, hogy a saját gépemen futtatott VM-ben hasonló műveletnél a php beérte 25-30 Mb-al. (processenként)
Én az exec("free", $meminfo) paranccsal próbálkoztam, hogy hitelesebb képet kapjak a memóriáról.
Igen, ez volt a másik dolog, amit figyelembe sem vett senki, hogy akár lehet valami oka is annak, hogy így szolgálom ki a képeket. Nincs egyébként :) De lehetett volna!
Ha felszabadítja a scripted a
A baj
Jó, hát én írtam, hogy ne
Az IE10 egyébként már 8
Az előttem felszólalókkal
Ha csak egy thumbnailt jelenítesz meg, minek imagemagick, vagy bármi más kép manipuláló akármi? Kitolod 2kb-onként fread echo párossal amig tart a fájl és kész. Megfelelően kialakított logikával még a fájl tipusát sem kell ellenörizned, kéri a fájlt ha van már tolod is, memoria használat pedig 1mb alatt marad.
A memória számolásba nem vettetek bele egy gyakori esetet, mi van ha 2 látogatód nézi az oldalt egyszerre, és nem 6, hanem 12 kép letöltés megy párhuzamossan? Vagy ha így még épp elférsz, mi van ha 3 látogatód van? Nagyon hamar eljutsz egy olyan látogató számhoz ahol a felvázolt megoldásod nem alkalmazható.
Fenti okok miatt is, az első kéréskor generált thumbnail is hanyagolnám, több vele a szívás mint a haszna, kép feltöltéskor generálj thumbnailt. Van egy nagyon jó library: wideimage, érdemes használni.
Lásd
Az én tapasztalataim:Az
Az imagick által használt memóriát a memory_get_peak_usage illetve a memory_get_usage
nem mutatja, php memory limit nincs rá hatással.
Ha nem csinálod a kép átméretezést, ugyanazt az eredményt kapot.
Virtuális gépen koznolban a "free" parancsal figyeltem a memória fogyasztást, fenti script cli-ben indítva kb a fentinek megfelelő mennyiségű memóriát evett (akkor beletettem egy sleep parancsot, hogy legyen időm meg nézni), kép átméretezés nélkül is próbáltam.
Riszpekt
Nyilván nem lehet megúszni a kép pixelenkénti tárolásának memóriaigényét még képméret lekérdezés esetén sem, maximum metadata-ból kiolvasva, de hát egy komoly képkezelő könyvtár esetében ez elég banális feltételezés lenne.
Ha már valaki írt mondjuk egy bmp file beolvasót valami normális programozási nyelven, egyértelmű lehet számára a következtetés, de teszt nélkül már nem akartam mégjobban belemélyedni, hogy mit jelent informatikában a képkezelés.
Fenti okok miatt is, az első
Fura, legközelebb rendesen végigolvasom, amit írt, nekem eszembe sem jutna nem kép feltöltéskor megcsinálni a thumbnail-eket, mert adatbázisba nem tudnám lementeni a nevüket...
Akkor nagyon rosszul teszem?
Az erőforrásigényes dolgokat
Ha kicsi a látogató szám, és kiküldesz egy header-t, hogy cachelje a böngésző, akkor nem számít sokat. Ha nő a látogató szám, akkor viszont feleslegesen terheled vele a szervert... Az általánosan elfogadott megoldás (legalábbis szerintem), hogy a kép feltöltésekor generálod hozzá a thumbnail-t. A scripteden alapból lehetne javítani, ha nem a végén szabadítanád fel a memóriát, hanem minden olyan lépés után, amikor megszabadulsz valamelyik képtől. Mondjuk 4 képnél ez nem számít annyira, de egy nagyobb sprite készítésénél már simán lehet szűk keresztmetszet. A jogosultság kezelést RewriteMap-el meg lehet oldani, ha védett képekről van szó, nem kell ezért beolvasni minden egyes képet a memóriába. (Nem tudom, hogy az apache féle fájl küldés hogy zajlik, de gondolom hatékonyabban, mint a php féle beolvasás + echo.)
Szerintem is
A memória számolásba nem
Limitálni kell a PHP processzek számát, nem egy nagy varázslat. A többi addig várakozik. Simán alkalmazható (és alkalmazzák is) nagy látogatószám mellett, egy sor előnye van: külön szerver(ek)en futtathatod a képgenerálást, ha bármi megfekszik benne, az oldalad még mindig olvasható marad (egy galériánál pont nem érsz sokat ezzel, de egy blogszolgáltatónál pl. nagyon nem mindegy), sokkal gyorsabban töltődik az oldal (nem kell a HTML kiküldésével megvárni a thumbnailek elkészültét). Nem is beszélve arról, ha statikusan cache-elni akarod a tartalmat, a képek meg változhatnak közben.