2011-12-31 14 views
12

Napisałem kod w PHP, który zwraca treść HTML z domen .edu. Krótkie wprowadzenie jest tutaj: Errors regarding Web Crawler in PHPRozwiązywanie błędów "serwer MySQL zniknął"

Przeszukiwacz działa poprawnie, gdy liczba linków do przeszukiwania są małe (około 40 adresów URL), ale otrzymuję komunikat "Serwer MySQL zniknął" po tym numerze.

Przechowuję zawartość html jako longtext w tabelach MySQL i nie rozumiem, dlaczego błąd pojawia się po co najmniej 40-50 wstawkach.

Każda pomoc w tym zakresie jest wysoko ceniona.

Zwróć uwagę, że już zmieniłem wait_timeout i max_allowed_packet, aby pomieścić moje zapytania i kod php, a teraz nie wiem co robić. Proszę, pomóż mi w tym zakresie.

Odpowiedz

10

Możecie być skłonny do obsługi tego problemu „pingowanie” serwera mysql przed zapytania. To jest zły pomysł. Aby uzyskać więcej informacji o tym, dlaczego tak jest, sprawdź ten wpis SO: Should I ping mysql server before each query?

Najlepszym sposobem na poradzenie sobie z problemem jest zawijanie zapytań wewnątrz bloków try/catch i przechwytywanie wyjątków bazy danych, aby można było odpowiednio sobie z nimi poradzić. Jest to szczególnie ważne w skryptach typu long running i/lub demonów. Oto bardzo prosty przykład użycia "menedżera połączeń" do kontrolowania dostępu do połączeń DB:

class DbPool { 

    private $connections = array(); 

    function addConnection($id, $dsn) { 
     $this->connections[$id] = array(
      'dsn' => $dsn, 
      'conn' => null 
     ); 
    } 

    function getConnection($id) { 
     if (!isset($this->connections[$id])) { 
      throw new Exception('Invalid DB connection requested'); 
     } elseif (isset($this->connections[$id]['conn'])) { 
      return $this->connections[$id]['conn']; 
     } else { 
      try { 
       // for mysql you need to supply user/pass as well 
       $conn = new PDO($dsn); 

       // Tell PDO to throw an exception on error 
       // (like "MySQL server has gone away") 
       $conn->setAttribute(
        PDO::ATTR_ERRMODE, 
        PDO::ERRMODE_EXCEPTION 
       ); 
       $this->connections[$id]['conn'] = $conn; 

       return $conn; 
      } catch (PDOException $e) { 
       return false; 
      } 
     } 
    } 

    function close($id) { 
     if (!isset($this->connections[$id])) { 
      throw new Exception('Invalid DB connection requested'); 
     } 
     $this->connections[$id]['conn'] = null; 
    } 


} 


class Crawler { 

    private $dbPool; 

    function __construct(DbPool $dbPool) { 
     $this->dbPool = $dbPool; 
    } 

    function crawl() { 
     // craw and store data in $crawledData variable 
     $this->save($crawledData); 
    } 

    function saveData($crawledData) { 
     if (!$conn = $this->dbPool->getConnection('write_conn') { 
      // doh! couldn't retrieve DB connection ... handle it 
     } else { 
      try { 
       // perform query on the $conn database connection 
      } catch (Exception $e) { 
       $msg = $e->getMessage(); 
       if (strstr($msg, 'MySQL server has gone away') { 
        $this->dbPool->close('write_conn'); 
        $this->saveData($val); 
       } else { 
        // some other error occurred 
       } 
      } 
     } 
    } 
} 
+0

Czy DbException już istnieje w php? – Rafay

+2

Nie, jest to klasa wyjątków, którą sam określisz i wyrzucisz z funkcji 'saveData()'. Zaktualizowałem funkcję 'saveData' i dodałem niestandardową klasę DbException w mojej odpowiedzi, aby odzwierciedlić to ... – rdlowrey

3

Mam another answer, który zajmuje się moim zdaniem podobnym problemem i wymagałoby podobnej odpowiedzi. Zasadniczo można użyć funkcji mysql_ping(), aby przetestować połączenie przed wstawieniem. Zanim MySQL 5.0.14, mysql_ping() automatycznie połączy serwer, ale teraz musisz zbudować własną logikę ponownych połączeń. Coś podobnego do tego powinien pracować dla Ciebie:

function check_dbconn($connection) { 
    if (!mysql_ping($connection)) { 
     mysql_close($connection); 
     $connection = mysql_connect('server', 'username', 'password'); 
     mysql_select_db('db',$connection); 
    } 
    return $connection; 
} 

foreach($array as $value) { 
    $dbconn = check_dbconn($dbconn); 
    $sql="insert into collected values('".$value."')"; 
    $res=mysql_query($sql, $dbconn); 
    //then some extra code. 
} 
+1

Pingowanie nie jest dobrą strategią w tym przypadku ... aby dowiedzieć się więcej, dlaczego, sprawdź ten wpis SO: [Czy powinienem pingować serwer mysql przed każdym zapytaniem?] (Http: // stackoverflow.com/questions/3103969/powinno-i-ping-mysql-server-before-each-query) – rdlowrey

0

Czy otwierasz pojedyncze połączenie DB i korzystasz z niego ponownie? Czy to możliwe, że jest to prosty limit czasu? Możesz być lepiej obsługiwany przez otwarcie nowego połączenia DB dla każdej operacji odczytu/zapisu (kontakt IE .edu, pobierz tekst, otwórz DB, wpisz tekst, zamknij bazę, powtórz).

Także w jaki sposób korzystasz z uchwytu? Czy to możliwe, że popełnił błąd i "odszedł" z tego powodu?

+0

Czy powinienem otworzyć nowe połączenie dla każdego zapytania, a następnie zamknąć je po wykonaniu zapytania? I powtórz procedurę dla wszystkich zapytań ?? – Rafay

+4

Dla rekordu, otwarcie nowego połączenia dla każdego zapytania jest okropnie nieefektywne ... – rdlowrey

+0

Warto dodać, że jeśli wątek zostanie zabity na db (z 'KILL [id wątku]'), wtedy otrzymasz "serwer ma odszedł także "błąd. –

0

Cóż, to właśnie teraz robię na podstawie sugestii rdlowrey'a i myślę, że to również jest właściwe.

public function url_db_html($sourceLink = NULL, $source) { 
    $source = mysql_real_escape_string($source); 

    $query = "INSERT INTO html (id, sourceLink, sourceCode) 
      VALUES (NULL,('$sourceLink') , ('$source'))"; 

    try { 
     if(mysql_query($query, $this->connection)==FALSE) { 
      $msg = mysql_errno($this->connection) . ": " . mysql_error($this->connection); 
      throw new DbException($msg); 
     }   
    } catch (DbException $e) { 
     echo "<br><br>Catched!!!<br><br>"; 
     if(strstr($e->getMessage(), 'MySQL server has gone away')) { 
      $this->connection = mysql_connect("localhost", "root", ""); 
      mysql_select_db("crawler1", $this->connection); 
     } 
    } 
} 

Po tym, jak zapytanie nie zostanie wykonane, skrypt go pominie, ale upewni się, że połączenie zostało przywrócone.

Jednak mój robot internetowy ulega awarii, gdy napotkano takie pliki, jak .jpg, .bmp, .pdf, itp. Czy istnieje sposób na pomijanie adresów URL zawierających te rozszerzenia? Używam preg_match i dałem do dopasowania pdf i dokument. Jednak chcę, aby funkcja pomijała wszystkie linki zawierające rozszerzenia, takie jak mp3, pdf itp. Czy to możliwe?

+0

Jeśli twoje połączenie db jest zamykane, będzie to jeden z dwóch powodów: 1) twój kod go zamyka. 2) Twój system ma poważny problem. Nigdy nie widziałem tej strategii ponownego połączenia, ponieważ nigdy nie widziałem sytuacji, w której powinno to być wymagane. Zamiast ponownie łączyć się z blokiem catch, spróbuj zarejestrować szczegóły wyjątku i stamtąd usunąć problem. –

2

Miałem do czynienia z "Serwer Mysql odszedł" błąd podczas korzystania z Mysql connector 5.X, zastępując dll do ostatniej wersji rozwiązał problem.