konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Od niedawna zastanawiam się w jaki sposób wymusić na phpie pokazywanie wyników przetwarzanego skryptu za każdym przejściem pętli, który działa np. 2h. Żeby nie być gołosłownym:

<?php
for($i=0, $i<=10000, $i++){
$wynik = moja_długo_działająca funkcja(); //jedno przejście trwa 1 s.
echo $wynik;
}
?>

Z moich doświadczeń wynika, że skrypt dopisuje wartości funkcji echo mniej więcej co 4 minuty. Jeśli pętla ma powiedzmy 10 000 przejść, to co 400 przejść php dopisuje hurtem 400 wartości.
Czy da się wymusić (i jeśli tak to jak) na funkcji (a może na apaczu) przetwarzanie na żywo bez znacznego spadku wydajności??

Jeżeli ma to znaczenie: skrypt przetwarzany na localhoście na apache2.2.12/php5.3/win7, nie działa też na zewnętrznych serwerach.

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

php wykonuje skrypty standardowo 30s a ty chcesz 2h?

do tego celu się CGI używa. Albo coś z Cronem na serwerze.

Co do wyświetlania stopniowych wyników w samym PHP nie da się tego zrobić, bo wyniki zobaczysz jak PHP wygeneruje wynik czyli zakończy pętlę. Musisz to zrobić za pomocą Ajaxa, czyli JS do tego wkomponować.Mariusz M. edytował(a) ten post dnia 30.01.10 o godzinie 22:16

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo


<?php
for($i=0, $i<=10000, $i++){
$wynik = moja_długo_działająca funkcja(); //jedno przejście trwa 1 s.
echo str_pad($wynik . '<br />', 1024, ' ', STR_PAD_RIGHT) . "\n";
flush();
ob_flush();
}
?>
Michał Stachura

Michał Stachura Dedykowane serwisy i
strony www -
http://santri.eu

Temat: [php] pokazywanie wyników skryptu na żywo

Paweł, słuchaj Mariusza dobrze mówi.

Co do wypluwania wyniku pętli zaraz po jej wykonaniu... no cóż

if (ob_get_level() == 0) ob_start();
for($i=0, $i<=10000, $i++){
$wynik = moja_długo_działająca funkcja(); //jedno przejście trwa 1 s.
echo $wynik;

ob_flush();
flush();
}
// i na koniec
ob_end_flush();

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

no racja, o flush nie pomyślałem.
Michał Stachura

Michał Stachura Dedykowane serwisy i
strony www -
http://santri.eu

Temat: [php] pokazywanie wyników skryptu na żywo

Jeszcze jedno mi przyszło do głowy.
Oprócz flusza możesz pobawić się AJAXem. Po każdym wywołaniu pętli wywołujesz AJAXa wykonującego kolejną jej iterację i tak aż do zakończenia pętli. Na wejściu wywołania ajaxa podajesz która to już iteracja żeby się nie zapętliło.

Dłubanina, ale jeśli masz mieć dużo wyników możesz sobie odświeżyć dowolny element strony, flusz będzie ci dopisywał kolejne wyniki po kolei zostawiając stare, z ajaxem możesz po prostu pokazywać aktualne wywołanie.

Dodaj do tego obliczenie

$procent = ($aktualna_iteracja/$wszystkie_powtorzenia_petli)*100;
$echo $procent;

i masz procentowo pokazany postęp wykonywania pętli.
Jak bardzo się chcesz bawić to zrób z tego pasek postępu i pokaż skończone dzieło odbiorcy.

Odbiorcy lobią takie bajerki.

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Michał Stachura:
Dłubanina, ale jeśli masz mieć dużo wyników możesz sobie odświeżyć dowolny element strony, flusz będzie ci dopisywał kolejne wyniki po kolei zostawiając stare, z ajaxem możesz po prostu pokazywać aktualne wywołanie.

Dodam jeszcze od siebie (tu od razu wyjaśnienie skąd się wziął ten str_pad w moim powyższym przykładzie) - samo echo $wartosc może nie wystarczyć, wysłanie do klienta danych nie oznacza, że przeglądarka je wyświetli, bo też je często buforuje. Safari buforuje bodajże 1k a IE 256b, stąd dopełnianie poprzez str_pad wysyłanej wartości do 1k.Piotr Picheta edytował(a) ten post dnia 30.01.10 o godzinie 22:56
Wojciech Szwajkiewicz

Wojciech Szwajkiewicz Aplikacje Android,
iOS -
thedroidsonroids.com

Temat: [php] pokazywanie wyników skryptu na żywo

Prostsza metoda:

ob_implicit_flush() na początku skryptu, a każdy output pokaże się na ekranie od razu.

http://pl.php.net/manual/en/function.ob-implicit-flush...

Pozdrawiam

edit: literówka

edit2: Jeśli chodzi o wydajność, każdy output ją obniża, a w pętli znacznie.Wojciech Szwajkiewicz edytował(a) ten post dnia 31.01.10 o godzinie 00:25

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Dzięki wszystkim za odpowiedzi.
Mariusz M.:
php wykonuje skrypty standardowo 30s a ty chcesz 2h?

do tego celu się CGI używa. Albo coś z Cronem na serwerze.

Zastosowanie jakiegokolwiek innego języka będzie miało podobną wydajność, ponieważ prędkość wykonywania skryptu jest uzależniona od obcego serwera, nie oferującego żadnego API ani dojścia do DB, a sam php ogranicza się do zapisu pobranych danych do bazy i ich drobnej obróbce.
Michał S.:
Jak bardzo się chcesz bawić to zrób z tego pasek postępu i pokaż > skończone dzieło odbiorcy.

Sam problem pojawił się właśnie z powodu prób oszacowania prędkości wykonywania skryptu. Niestety przed samym wykonaniem, program nie jest w stanie ocenić ile skrypt będzie miał do przetworzenia przejść pętli, ponieważ np. dzisiaj było to pół miliona, a wczoraj 3 miliony. Dlatego też pojawił się pomysł samej informacji - ile rekordów zostało już przetworzonych, bo tabela w bazie danych ze względów wydajności na czas insertów jest zablokowana.

I jeszcze jedno: co z wydajnością? Przy tak dużej ilości przejść w pętli, wielkości rzędu milisekundy mogą dosyć opóźnić skrypt. Jest i na to jakiś sposób?

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Jezeli sie nie chce bawic we flushe, wystarczy ustawic w php.ini output_buffering na On i skrypt 'wypluwa' na bierzaco.
Przy tak duzym przemiale, mozna zwiekszyc standardowy czas wykonywania skryptu albo pobawic sie forkiem w PHP.
Wojciech K.

Wojciech K. realizator pomysłów
własnych

Temat: [php] pokazywanie wyników skryptu na żywo

Paweł Jędrasiewicz:
I jeszcze jedno: co z wydajnością? Przy tak dużej ilości przejść w pętli, wielkości rzędu milisekundy mogą dosyć opóźnić skrypt. Jest i na to jakiś sposób?

jeśli zależy Tobie na milisekundach, to - może to nieeleganckie - spóbuj zamiast funkcji, wkleić w pętle bezpośrednio jej bebechy - wywołanie funkcji, oraz przekazywanie parametrów zajmują trochę dodatkowego czasu.

a flush() daj tylko co np. 1000 wywołań (po co częściej? i tak nie zauważysz różnicy między trzema milionami, a trzema tysiącami flushów ;)
czyli:
if($i%1000==0) flush();

3mln przejść w 2h?
to 2,4ms na jedno :)

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Wojciech K.:
jeśli zależy Tobie na milisekundach, to - może to nieeleganckie - spóbuj zamiast funkcji, wkleić w pętle bezpośrednio jej bebechy - wywołanie funkcji, oraz przekazywanie parametrów zajmują trochę dodatkowego czasu.

Tak też jest zrobione :)
a flush() daj tylko co np. 1000 wywołań (po co częściej? i tak nie zauważysz różnicy między trzema milionami, a trzema tysiącami flushów ;)
czyli:
if($i%1000==0) flush();

Jest też if z flushem po przejściu w pętli nadrzędnej
3mln przejść w 2h?
to 2,4ms na jedno :)

Wiem, wiem. Z tym namieszałem. Dla ścisłości: w pierwszym poście jest przykład tylko, a w tym ostatnim ilość przejść w prawdziwym programie. W praktyce wstawienie flush'a z w.w if'em spowolniło skrypt niezauważalnie o wielkości rzędu 0.01 przejść na sekundę.
Łukasz Ważny

Łukasz Ważny winning doesn't
really matter as
long as you win

Temat: [php] pokazywanie wyników skryptu na żywo

Skrypt możesz uruchamiać z linii poleceń. Czas skryptu jest defaultowo faktycznie ustawiony na 30s, ale jeżeli masz uprawnienia to w skrypcie można wywołać funkcję:


set_time_limit(0);


... i skrypt może działać cały dzień.

Ewentualne info o ilości przetworzonych danych wypisujesz do loga. Jeśli chcesz sprawdzać jaki jest postęp, to w przeglądarce wyświetlasz np ostatnie wpisy z loga.Łukasz Ważny edytował(a) ten post dnia 01.02.10 o godzinie 09:26
Michał Jarosz

Michał Jarosz Frontend Developer &
Team Leader

Temat: [php] pokazywanie wyników skryptu na żywo

Domyślnie skrypty z linii poleceń mogą sobie chodzić ile chcą bez ustawiania set_time_limit();

http://www.php.net/manual/en/info.configuration.php#in...
When running PHP from the command line the default setting is 0.

I komentarz z php.ini


; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 300

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Taki proces lepiej napisać "transakcyjnie". Tzn skolejkować sobie listę adresów do odwiedzenia w bazie i sukcesywnie je odznaczać - dzięki temu bez problemu można restartować komputer.

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Piotr Likus:
Taki proces lepiej napisać "transakcyjnie". Tzn skolejkować sobie listę adresów do odwiedzenia w bazie i sukcesywnie je odznaczać - dzięki temu bez problemu można restartować komputer.

Skrypt o którym mowa jest spiderem chodzącym po serwisie, który czyta wygenerowane przez tę stronę pliki html i wyciąga z nich dane. Jak już wcześniej wspomniałem, niestety nie ma możliwości dostępu w inny, bardziej elegancki sposób do DB. Dlatego też wszelkie próby transakcji na samej bazie odpadają.

Pomysł Piotra dotyczący transakcji jest sam w sobie świetny, jeśli będzie się go dało zaimplementować w php. Wtedy skrypt będzie wiedział, że na wypadek utraty połączenia z netem nie przechodzić wszystkich przejść w pętli od nowa. Pytanie tylko czy jest to możliwe.

Przykładowe rozwiązanie:
Powiedzmy, że skrypt ma 2 pętle: producent i model. Podczas przejścia przez pętlę 'model' skrypt odczytuje dane i zapisuje insertem do DB. W chwili przejścia skryptu przez 'producenta' X, 'model' Y i wczytaniu pierwszych cen 10 pozycji modelu Y, połączenie z netem zostaje utracone. Najbezpieczniej by było wznowić skrypt od 'prod' X, 'modelu' Y ale nie od 11 pozycji tylko od samego początku, bo np. dane już się zmieniły i skrypt startując od 10 pozycji może pominąć albo co gorsza nadpisać pewne pozycje. W tym wypadku z naszej DB należałoby usunąć te poprzednie 10 rekordów i wystartować od początku 'modelu' Y.

Tylko, że samo kasowanie rekordów samo w sobie budzi już pewne obawy. Druga rzecz to tunning całego skryptu. Na razie przed zapisem przerwszego rekordu aż do końca skryptu tabela z pobranymi danymi jest blokowana (LOCK). Znalazłem w sieci głosy, że po pierwsze lepiej będzie zapisywać dane najpierw do csv a dopiero później importować csv do DB, a po drugie najlepiej zastosować w chwili obecnej silnik InnoDB, ponieważ jest on już szybszy od MyISAM. Co o tym tuningu sądzicie??

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

To czy plik czy SQL to już zależy od przypadku. Skrypt będzie działał raczej wolno, więc CSV ci tu nie pomoże, ale jeśli uruchomisz 1000 wątków naraz to być może baza zacznie być wąskim gardłem (czysto hipotecznie, praktycznie wątpliwe).

Forma restartu - to też w dużej mierze zależy od dynamiki danych i priorytetów konsumenta danych.

Robiłem coś takiego do agregacji RSS-a, tylko że w formie "poor man CRON". Program był uruchamiany cyklicznie, przy czym jego praca była ograniczona - nie robił wszystkiego naraz. Dlatego takie "odhaczanie" było musem - na SQLu.Piotr Likus edytował(a) ten post dnia 01.02.10 o godzinie 20:11
Wojciech K.

Wojciech K. realizator pomysłów
własnych

Temat: [php] pokazywanie wyników skryptu na żywo

czy lista linków do ściągnięcia jest zawsze taka sama, czy zmieniają się one za każdym razem?
czy pobierając dane musisz "zerować" swoją bazę, czy możesz po prostu nadpisywać istniejące rekordy nowymi wartościami?

jeśli jest to katalog statycznych stron, możesz pobierać nagłówki (HEAD) i sprawdzać Expiration itd. - żeby nie ściągać czegoś, co się i tak nie zmieniło.

jeśli boisz się przerw w połączeniu - najlepiej dla każdego "zakolejkowanego" linka dodać 2 wartości: czas_polaczenia (domyślnie wartość zerowa - potem wstawiasz tam aktualny timestamp tuż przed próbą połączenia), oraz czas_pobrania (wstawiasz tam aktualny timestamp po udanym pobraniu) - potem z crona co jakiś czas wykonujesz zapytanie w stylu:
update ... set czas_polaczenia=0 where czas_pobrania=0 and czas_polaczenia>0 and czas_polaczenia<POL_GODZINY_TEMU
(czyli strony, które miały być pobrane dawniej niż pół godziny temu, a nadal nie są, można uznać za "uwalone" i wracają do kolejki do ponownego ściągnięcia)

konto usunięte

Temat: [php] pokazywanie wyników skryptu na żywo

Wojciech K.:
czy lista linków do ściągnięcia jest zawsze taka sama, czy zmieniają się one za każdym razem?
czy pobierając dane musisz "zerować" swoją bazę, czy możesz po prostu nadpisywać istniejące rekordy nowymi wartościami?

Rozumiem, że chodzi o linki do stron html na których są pobierane dane. Otóż lista linków jak i same dane różnią się każdego dnia.
jeśli jest to katalog statycznych stron, możesz pobierać nagłówki (HEAD) i sprawdzać Expiration itd. - żeby nie ściągać czegoś, co się i tak nie zmieniło.

Niestety strony na których są dane są generowane dynamicznie. Dodatkowo jest to serwis transakcyjny więc jest prawie niemożliwe aby dane były te same na danej stronie w odstępie 1 dnia.
jeśli boisz się przerw w połączeniu - najlepiej dla każdego "zakolejkowanego" linka dodać 2 wartości: czas_polaczenia (domyślnie wartość zerowa - potem wstawiasz tam aktualny timestamp tuż przed próbą połączenia), oraz czas_pobrania (wstawiasz tam aktualny timestamp po udanym pobraniu) - potem z crona co jakiś czas wykonujesz zapytanie w stylu:
update ... set czas_polaczenia=0 where czas_pobrania=0 and czas_polaczenia>0 and czas_polaczenia<POL_GODZINY_TEMU
> (czyli strony, które miały być pobrane dawniej niż pół
godziny temu, a nadal nie są, można uznać za "uwalone" i wracają do kolejki do ponownego ściągnięcia)

Dzięki za pomysł, ale zostało już zaimplementowane rozwiązanie z mojego poprzedniego posta.Paweł Jędrasiewicz edytował(a) ten post dnia 02.02.10 o godzinie 17:37



Wyślij zaproszenie do