Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Jak zwiększyć czas wykonywania skryptu lub podzielić wykonywanie na kilka części
Forum PHP.pl > Forum > PHP
marcin909090
Witam, piszę wtyczką pod WooCommerce importującą produkty do sklepu internetowego z plików XML/CSV.
Problem nie dotyczy małej ilości produktów. Podczas gdy jest ich kilkaset lub kilka tysięcy skrypt potrzebuje określonego czasu, żeby je dodać.
Niestety w pewnym momencie się przerywa i wyrzuca komunikat "504 Gateway Time-out".
Działanie jakie wykonałem :
- Dodałem do moich plików php
  1. ini_set('memory_limit', '-1');
- Zwiększyłem limit pamięci w Wordpressie
- Zmieniłem max_execution_time na serwerze na 120

Zauważyłem, że jestem w stanie dodać około 300 produktów więcej co w całości daje około 700. Jest to zdecydowanie za mało.

Moje pytanie to, czy jest jakaś możliwość wydłużenia czasu działania skryptu bez ingerencji w serwer lub czy macie pomysł na inne rozwiązanie. Moja aplikacja to wtyczka więc różni ludzie na różnych serwerach będą z niej korzystali. Ma być uniwersalna i NIE może wymagać od użytkownika zmiany ustawień serwera.
Istnieje podobna amerykańska wtyczka (płatna), która oferuje takie działanie, a sposób importu produktów to dzielenie ich w części po 20 produktów, usuwanie informacji, import kolejnych 20, usuwanie informacji i tak do momentu zaimportowanie wszystkich produktów do sklepu WooCommerce.

Macie jakiś pomysł jak osiągnąć taki efekt lub sprawić, żeby nie było limitu czasowego przy działaniu skryptu nie ingerując w ustawienia serwera ?

markuz
Wywołuj skrypt przez AJAX który importuje np. 20 produktów jak opisałeś wyżej, następnie sprawdza odpowiedź i importuje kolejne 20 - wszystko można ładnie obudowac w jakiś progress bar.
marcin909090
Jakieś wskazówki jak się za to zabrać? Myślałem, że ominę problem zwiększając pamięć, ale jednak nic z tego jeśli wtyczka ma działać jako automat na różnych serwerach. Z Ajax'a jestem 0 wstydnis.gif
session
Cytat
Moje pytanie to, czy jest jakaś możliwość wydłużenia czasu działania skryptu bez ingerencji w serwer lub czy macie pomysł na inne rozwiązanie

Gdyby można było z poziomu kodu zmieniać czas działania skryptu (ograniczony ustawieniami serwera) to zaraz któryś z nieskończoną pętlą zająłby zasoby serwera, dlatego tego parametru nie ruszamy. Skrypty muszą wykonywać się w określonym czasie.

Cytat
Z Ajax'a jestem 0


Ojojoj nie odrobiło się zadania domowego tongue.gif laugh.gif Minęło już kilka miesięcy, a sytuację masz dokładnie taką samą jak w przypadku pobierania danych zamówień wink.gif

Właściwie do wykorzystania masz ten sam kod. Podmień tylko nazwy funkcji, identyfikatorów HTMLowych i dane w sekcji jQuery.ajax({ }); skryptu JSowego. Główną funkcję (tą którą będzie wywoływał AJAX, czyli apaczka_request() w starym kodzie) musisz podmienić na funkcję działającą logicznie w taki sposób:

0. Zakładam, że upload już sobie ogarniesz. Pamiętaj o sprawdzaniu poprawności wgranego pliku oraz przesłanych przez AJAX danych ($_POST)!
1. Otwórz zuploadowany plik.
2. $offset = $_POST['offset']; i korzystasz z fseek, aby ustawić wskaźnik pliku w miejscu ostatniego odczytu ( dlatego na początku = 0 )
3. while ( !feof($file) )
4. fgetc i doklejasz do stringa (konkatenacja)
5. Sprawdzasz czy wystąpił znak oznaczający koniec "paczki" danych, czyli takich które dotyczą jednego wpisu (w przypadku CSV jest to chyba znak nowej linii \n, zatem mógłbyś ułatwić sobie zadanie i w pkt 3. użyć fgets, ale w przypadku XMLa nie jest to takie oczywiste, bo zoptymalizowany może nie mieć znaków końca linii)
6. Jeśli odczytano "paczkę" danych to inkrementujesz jakiś licznik i: if( $counter == 20 ) break; która spowoduje przerwanie pętli i odczytywanie dalszych znaków/linii
7. Sprawdzasz, czy odczytano cały plik, np. ponownie sprawdzasz feof, albo korzystasz z jakiejś zmiennej logicznej
8. Jeśli koniec pliku to echo 0;
9. Jeśli nie to za pomocą ftell sprawdzasz aktualną pozycję wskaźnika pliku i przesyłasz ją jako odpowiedź serwera (echo)
10. Zamykasz plik
11. Robisz z danymi co potrzeba (powinno to być optymalne, żeby nie zajmowało cennego czasu)
12. die

W sekcji ajaxowej dajesz success : function(response){ offset=response; if(!isNaN(offset) && parseInt(offset) > 0){ jQuery.ajax(this); } } co spowoduje, że jeśli są jeszcze jakieś dane do odczytania AJAX wykona się kolejny raz, tym razem z powiększoną zmienną offset.

Ponadto jeśli gdziekolwiek wystąpi błąd to zadbaj, żeby wyświetlił jakąś liczbę ujemną inaczej ajax mógłby się zapętlić. Dodatkowo masz sprawdzanie w JS, czy skrypt zwrócił liczbę.

Generalnie skrypt będzie z każdym odpaleniem czytał jedynie fragment pliku, przekazywał miejsce, do którego doczytał i kończył wykonywanie, dlatego powinieneś dobrać tak ilość danych do odczytania, żeby zdążył przed timeoutem, ustawienie zbyt małej ilości może jest bezpieczne, ale wydłuży czas całego procesu. Później ten sam skrypt odpali się ponownie, ale zanim zacznie czytać przesunie wskaźnik tam gdzie poprzednio skończył i znowu odczyta część danych.

Najlepiej jeśli rozbijesz odczytywanie osobno dla plików XML, a osobno dla CSV, ponieważ różnią się one znacznie, a CSV można bardzo szybko rozkładać, co warto wykorzystać.
marcin909090
Przeanalizuję to co napisałeś. Dodam, również że w CRONie aktualizacja zaimportowanych produktów ma działać np. codziennie, wtedy następuje edycja gdy zmieni się np. cena, stan magazynowy czy dodano do hurtowni nowy produkt. W takim razie czy takie rozwiązanie ma sens i zadziała w CRONie? Te Ajax'y i Javascript'y się nie zgubią jako zadanie CRON? Zadań CRON może być nawet kilka dla plików z różnych hurtowni.

Sprawdziłoby się tu Twoje rozwiązanie ?
session
Cytat
wtedy następuje edycja

Edycja czego ? Tych plików CSV/XML czy rekordów w bazie ?

Generalnie masz z tego skrypt który czyta pliki fragmentarycznie i coś z odczytanym fragmentem będzie robił, a co to zależy tylko od Ciebie, czy będzie wstawiał je do bazy korzystając z funkcji WP, czy będzie tylko wyświetlał, czy cokolwiek potrzebujesz. Skrypt teoretycznie mógłby zostać uruchomiony z CRONa, ale blokada nastąpi w miejscu, w którym użyjesz jakiejś funkcji z WP, co jest raczej bardzo prawdopodobne (w innym przypadku znaczy, że zrobiłeś coś źle tongue.gif ). Tylko pytanie czy w ogóle potrzebujesz tego skryptu w CRONie, czy w ogóle CRON to dobre rozwiązanie w tej sytuacji, przecież możesz w momencie edycji produktu (i dodania nowego) od razu updateować wpisy w bazie. Generowanie całego pliku CSV/XML i analizowanie go dla 1 wpisu to trochę marnotrawstwo. Btw nie każdy serwer ma obsługę CRONa więc kolejny raz wprowadzasz ograniczenia.
marcin909090
Rekordów w bazie smile.gif pomyślę jeszcze nad rozwiązaniem problemu. Dzięki, widzę, że chyba się zajmujesz pisaniem wtyczek do WP thumbsupsmileyanim.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.