2009-05-07 11 views
31

Pracuję nad projektem, w którym sprawdzam informacje od użytkownika za pomocą usługi sieciowej SOAP. Obecnie zajmuję się błędami, zakładając, że otrzymuję odpowiedzi od usługi sieciowej, ale także zajmuję się skrajnymi przypadkami przekroczenia limitu czasu usługi lub niedostępności.Obsługa przekroczenia limitu czasu mydła w PHP

W przypadku niedostępności usługi lub czasu oczekiwania, muszę udawać, że żądanie się powiodło (że usługa internetowa zatwierdziła informacje), ale nie jestem pewien, jakie wyjątki są zgłaszane.

Niektóre pseudo-kod:

// $client is PHP's SoapClient class 
try { 
    $response = $client->SomeSoapRequest(); 
} 
catch(SoapFault $e){ 
    // handle issues returned by the web service 
} 
catch(Exception $e){ 
    // handle PHP issues with the request 
} 

Nie mogę wydawać się znaleźć to:

  1. Are limity czasu do SoapFault? Jeśli tak, jaki jest najlepszy sposób na rozróżnienie między błędem przekroczenia limitu czasu a usługami sieciowymi (np. Błąd typu itp.)? Znalazłem jedną stronę, która wspomniała o błędzie, gdzie wiadomość była czymś w rodzaju "Błąd ładowania nagłówków", ale nie wspomniałem, że to był błąd Soap.
  2. Jak może nastąpić niedostępność usługi? Wydaje się, że wyjątek PHP miałby sens (SoapFault zostałby zwrócony z serwisu WWW, w którym niedostępność byłaby problemem z gniazdem lub podobnym)?
  3. Czy istnieje usługa (np. Przykład), z której mogę przetestować limit czasu? Większość dyskusji związanych z przekroczeniem limitu czasu wydaje się być związana z zapobieganiem przekroczeniem limitu czasu przez wydłużenie domyślnego ustawienia limitu czasu, co nie jest idealne w tej sytuacji.
+0

kiedyś naprawić? –

+0

Niezupełnie. Muszę jeszcze spróbować rozwiązania Roberta Ludwicka, ale wydaje mi się, że najbardziej zbliżam się do tego, czego potrzebuję. Jeśli okaże się, że to rozwiązanie działa (lub robi to inaczej), proszę dać mi znać! – Rob

+0

Wciąż brak poprawki? :-( – Chris

Odpowiedz

3

Aby poradzić sobie z limity czasu w służbie

$client = new SoapClient($wsdl, array("connection_timeout"=>10)); 

// SET SOCKET TIMEOUT 
if(defined('RESPONSE_TIMEOUT') && RESPONSE_TIMEOUT != '') { 
ini_set('default_socket_timeout', RESPONSE_TIMEOUT); 
} 
+0

Pomoże to w zagwarantowaniu, że wolna usługa nie przekroczy limitu czasu. Bardziej interesuje mnie pozwolenie na przekroczenie limitu czasu usługi i złapanie tego błędu (w porównaniu z innymi błędami specyficznymi dla usługi). Zasadniczo usługa sieciowa może mieć okna serwisowe lub duży ruch, co spowalnia lub nie reaguje, ale moi użytkownicy końcowi nie muszą za to płacić. – Rob

+0

Ustawienie default_socket_timeout przez ini, niestety, wpływa na wszystkie połączenia wychodzące, nawet gniazda DB. Sprawdź rozwiązanie, które właśnie napisałem, co rozszerza SoapClient i zastępuje jego metodę __doRequest(), aby umożliwić ustawienie limitu czasu dla pojedynczego SoapClient. – Derek

6

wygląda default_socket_timeout nie jest brany pod uwagę przy wykonywaniu połączeń SOAP przez HTTPS:

Błąd otwarty w chwili pisania. Jako komentarz do wpisu na blogu, do którego odnosi się Robert Ludwick w usuniętej odpowiedzi, Timing Out PHP Soap Calls (21 Oct 2009; by Published by Robert F. Ludwick) wskazuje, obejście tego posta omawia (przesłonięcie SoapClient::__doRequest() z żądaniem curl) działa również wokół tego błędu.

Kolejny związany błąd jest:


Kod wspomniano w blogu przeszedł kilka zmian i można je znaleźć w to najnowsza forma z supp ort uwierzytelniania HTTP tutaj na Github:

W każdym razie obejście nie powinno być potrzebne dłużej jak ten problem został rozwiązany w rozszerzeniu PHP SoapClient.

+0

Wpis na blogu Roberta Ludwicka został przeniesiony do innego adresu URL: https://therobzone.wordpress.com/2009/10/21/timing-out-php-soap-calls/ – semmelbroesel

+0

W komentarzach do https: // bugs .php.net/bug.php? id = 48524 można przeczytać: _ Poprawka została zastosowana do gałęzi 5.4, 5.5, 5.6 i master._ Tak więc 'default_socket_timeout' powinno teraz działać z ssl. –

2

Z mojego doświadczenia wynika, że ​​jeśli $e->getMessage to "Błąd podczas pobierania nagłówków HTTP", oznacza to przekroczenie czasu sieci.

Jeśli $e->getMessage przypomina "Nie można połączyć się z hostem", usługa, którą próbujesz otworzyć, nie działa.

Następnie "Wygląda na to, że nie mamy dokumentu XML", co jest bardziej zagadkowe i może oznaczać różne rzeczy.

+0

Inne problemy z serwerem mogą powodować te same komunikaty o błędach, które sugerują powyższe testy. Sprawdź rozwiązanie, które właśnie napisałem. Wyraźnie testuje czas oczekiwania, a nie zgadywanie oparte na niejednoznacznych komunikatach z wieloma potencjalnymi przyczynami. – Derek

1

Użyłem dwóch czynników, aby moje rozszerzenie SoapClient rzucić miły wyjątek. Wiadomość i czas potrzebny na powrót. Myślę, że komunikat o błędzie "Error Fetching http headers" może również wystąpić w niektórych innych przypadkach, a zatem sprawdzić czas.

Poniższy kod powinien być o prawo

class SoapClientWithTimeout extends SoapClient { 
    public function __soapCall ($params, ---) { 
     $time_start = microtime(true); 
     try { 
      $result = parent::__soapCall ($params, ---); 
     } 
     catch (Exception $e) { 
      $time_request = (microtime(true)-$time_start); 
      if(
       $e->getMessage() == 'Error Fetching http headers' && 
       ini_get('default_socket_timeout') < $time_request 
      ) { 
       throw new SoapTimeoutException(
        'Soap request most likly timed out.'. 
        ' It took '.$time_request. 
        ' and the limit is '.ini_get('default_socket_timeout') 
       ); 
      } 

      // E: Not a timeout, let's rethrow the original exception 
      throw $e; 
     } 

     // All good, no exception from the service or PHP 
     return $result; 
    } 
} 

class SoapTimeoutException extends Exception {} 

Następnie używać SoapClientWithTimeout

$client = new SoapClientWithTimeout(); 
try { 
    $response = $client->SomeSoapRequest(); 
    var_dump($response); 
} 
catch(SoapTimeoutException $e){ 
    echo 'We experienced a timeout! '. $e->getMessage(); 
} 
catch(Exception $e) { 
    echo 'Exception: '.$e->getMessage(); 
} 

debugowania taktowanie usług na zewnątrz. Dodaj poniższą linię przed wywołaniem usługi

ini_set('default_socket_timeout', 1); 
+0

Inne problemy z serwerem mogą powodować te same komunikaty o błędach, które sugerują powyższe testy. Sprawdź rozwiązanie, które właśnie napisałem. Wyraźnie testuje czas oczekiwania, a nie zgadywanie oparte na niejednoznacznych komunikatach z wieloma potencjalnymi przyczynami. – Derek

+0

Ponadto, ustawienie default_socket_timeout przez ini, niestety, wpływa na wszystkie połączenia wychodzące, nawet gniazda DB. Ponownie, kontrastuję z rozwiązaniem, które właśnie napisałem, co rozszerza SoapClient i nadpisuje jego metodę __doRequest() w celu umożliwienia ustawienia limitu czasu gniazda dla pojedynczego SoapClient. – Derek

+0

Jak wspomniano w moim poście powyżej "ini_set (" default_socket_timeout ", 1);" służy do debugowania. Mój kod zgłasza wyjątek tylko w przypadku wykrycia przekroczenia limitu czasu. Nie sugeruję zmiany domyślnego czasu oczekiwania na gniazdo. Przesłanianie __doRequest() jest czymś, co zwykle kończy się, gdy potrzebuję większej kontroli nad wysyłanymi żądaniami. Trzeba jednak dużo pracy, aby to naprawić (na przykład utrzymuj połączenia z siecią i tak dalej). Wolę nie nadpisywać zbyt wiele. – HNygard

27

1) w przypadku przekroczenia limitu czasu, PHP zgłosi wyjątek SoapFault z faultcode="HTTP" i faultstring="Error Fetching http headers".
2) Moim zdaniem, najlepszym sposobem na rozróżnienie między błędem przekroczenia limitu czasu a usługami sieciowymi jest patrzenie na użytkowników faultcode i faultstring członków SoapFault class.
W szczególności element faultcode jest przeznaczony do użytku przez oprogramowanie w celu zapewnienia algorytmu do identyfikacji błędu.
Jak można również przeczytać w comment of the PHP manual, nie istnieje metoda odczytu właściwości faultcode, więc trzeba uzyskać do niej bezpośredni dostęp (np. $e->faultcode), ponieważ metoda getCode() nie działa.
SOAP 1.1 Spec definiuje cztery możliwe wartości dla pola faultcode:

  • VersionMismatch: Partia przetwarzanie znaleziono nieprawidłowy nazw dla elementu SOAP Envelope
  • MustUnderstand: natychmiastowy element podrzędny mydła Element nagłówka, który nie został zrozumiany lub nie został spełniony przez stronę przetwarzającą, zawierał atrybut SOAP mustUnderstand o wartości "1"
  • Klient: Klasa błędów klienta wskazuje, że wiadomość została utworzona nieprawidłowo lub nie zawierała odpowiednich informacji, aby odnieść sukces. Na przykład w wiadomości może brakować prawidłowego uwierzytelnienia lub informacji o płatności. Zasadniczo jest to wskazówka, że ​​wiadomość nie powinna być ponownie wysłana bez zmian.
  • Serwer: Klasa błędów serwera wskazuje, że wiadomość nie mogła zostać przetworzona z przyczyn niezwiązanych bezpośrednio z treścią wiadomości, a raczej z przetwarzaniem wiadomości. Na przykład przetwarzanie może obejmować komunikację z wyższym procesorem, który nie odpowiedział. Wiadomość może się powieść w późniejszym czasie.

Oprócz tych kodów, PHP wykorzystuje kod HTTP do identyfikacji błędów występujących na poziomie protokołu (np .: błędy gniazd); na przykład, jeśli szukasz add_soap_fault w kodzie źródłowym ext/soap/php_http.c, możesz zobaczyć, kiedy generowane są niektóre z tych błędów.
wyszukując add_soap_fault i soap_server_fault funkcji w plikach źródłowych PHP rozszerzeń SOAP, mam wbudowany poniższej listy PHP SoapFault wyjątkami:

HTTP 
---- 
Unable to parse URL 
Unknown protocol. Only http and https are allowed. 
SSL support is not available in this build 
Could not connect to host 
Failed Sending HTTP SOAP request 
Failed to create stream?? 
Error Fetching http headers 
Error Fetching http body: No Content-Length: connection closed or chunked data 
Redirection limit reached: aborting 
Didn't recieve an xml document 
Unknown Content-Encoding 
Can't uncompress compressed response 
Error build soap request 


VersionMismatch 
--------------- 
Wrong Version 


Client 
------ 
A SOAP 1.2 envelope can contain only Header and Body 
A SOAP Body element cannot have non Namespace qualified attributes 
A SOAP Envelope element cannot have non Namespace qualified attributes 
A SOAP Header element cannot have non Namespace qualified attributes 
Bad Request 
Body must be present in a SOAP envelope 
Can't find response data 
DTD are not supported by SOAP 
encodingStyle cannot be specified on the Body 
encodingStyle cannot be specified on the Envelope 
encodingStyle cannot be specified on the Header 
Error cannot find parameter 
Error could not find "location" property 
Error finding "uri" property 
looks like we got "Body" with several functions call 
looks like we got "Body" without function call 
looks like we got no XML document 
looks like we got XML without "Envelope" element 
Missing parameter 
mustUnderstand value is not boolean 
SoapClient::__doRequest() failed 
SoapClient::__doRequest() returned non string value 
Unknown Data Encoding Style 
Unknown Error 
DataEncodingUnknown 


MustUnderstand 
-------------- 
Header not understood 


Server 
------ 
Couldn't find WSDL 
DTD are not supported by SOAP 
Unknown SOAP version 
WSDL generation is not supported yet 

3) Aby symulować stan limitu czasu, spróbuj następujących Kod:

soapclient.php

<?php 

ini_set('default_socket_timeout', 10); 

$client = new SoapClient(null, 
    array(
    'location' => "http://localhost/soapserver.php", 
    'uri'  => "http://localhost/soapserver.php", 
    'trace' => 1 
) 
); 

try { 
    echo $return = $client->__soapCall("add",array(41, 51)); 
} catch (SoapFault $e) { 
    echo "<pre>SoapFault: ".print_r($e, true)."</pre>\n"; 
    //echo "<pre>faultcode: '".$e->faultcode."'</pre>"; 
    //echo "<pre>faultstring: '".$e->getMessage()."'</pre>"; 
} 

?> 

soapserver.php

<?php 

function add($a, $b) { 
    return $a + $b; 
} 

sleep(20); 

$soap = new SoapServer(null, array('uri' => 'http://localhost/soapserver.php')); 
$soap->addFunction("add"); 
$soap->handle(); 

?> 

Wskazówka wywołanie sleep w skrypcie SoapServer.php z czasem (20) Najdłuższy niż raz (10) określona dla parametru w skrypcie SoapClient.phpdefault_socket_timeout.
Jeśli chcesz zasymulować niedostępność usługi, możesz na przykład zmienić protokół location z http na https w skrypcie soapclient.php, zakładając, że Twój serwer internetowy nie jest skonfigurowany pod kątem protokołu SSL; w ten sposób PHP powinno rzucić "Nie można połączyć się z hostem" SoapFault.

+0

Ustawienie default_socket_timeout przez ini, niestety, wpływa na wszystkie połączenia wychodzące, nawet gniazda DB. Sprawdź rozwiązanie, które właśnie wysłałem, co rozszerza SoapClient i zastępuje jego metodę '__doRequest()', aby umożliwić ustawienie limitu czasu dla pojedynczego SoapClient. – Derek

0

Chyba jestem trochę późno, ale w przypadku, gdy ktoś wciąż szuka rozwiązania limity czasu w PHP klientem mydła - oto co pracował dla mnie:

Basicly zastępując PHP SoapClient z cURL z ustawionym czasem oczekiwania. Pamiętaj, że czasami WS oczekuje akcji określonej w nagłówku HTTP. Oryginalne rozwiązanie zamieszczone na tej stronie nie zawiera tego (sprawdź komentarze).

+0

To zostało odebrane wcześniej: http://stackoverflow.com/a/3671312/367456 - Jeśli uważasz, że istniejąca odpowiedź wymaga dodania, proszę zasugeruj ją i nie publikuj nowej odpowiedzi po latach z tą samą zawartością. – hakre

0

Po prostu ustawianie globalnie default_socket_timeout poprzez ini może nie robić tego, co chcesz. Wpłynie to na żądania SOAP, ale wpłynie również na inne połączenia wychodzące, w tym połączenia DB. Zamiast tego należy zastąpić metodę __doRequest() SoapClient, aby samodzielnie utworzyć połączenie HTTP. Następnie można ustawić własny limit czasu na gnieździe, wykryć go i wyrzucić wyjątki, które można pułapkować i obsługiwać.

class SoapClientWithTimeout extends SoapClient { 

    public function __construct ($wsdl, $options = null) { 
     if (!$options) $options = []; 

     $this->_connectionTimeout = 
      @$options['connection_timeout'] 
      ?: ini_get ('default_socket_timeout'); 
     $this->_socketTimeout = 
      @$options['socket_timeout'] 
      ?: ini_get ('default_socket_timeout'); 
     unset ($options['socket_timeout']); 

     parent::__construct($wsdl, $options); 
    } 

    /** 
    * Override parent __doRequest to add a timeout. 
    */ 
    public function __doRequest (
     $request, $location, $action, $version, $one_way = 0 
    ) { 
     // Extract host, port, and scheme. 
     $url_parts = parse_url ($location); 
     $host = $url_parts['host']; 
     $port = 
      @$url_parts['port'] 
      ?: ($url_parts['scheme'] == 'https' ? 443 : 80); 
     $length = strlen ($request); 

     // Form the HTTP SOAP request. 
     $http_req = "POST $location HTTP/1.0\r\n"; 
     $http_req .= "Host: $host\r\n"; 
     $http_req .= "SoapAction: $action\r\n"; 
     $http_req .= "Content-Type: text/xml; charset=utf-8\r\n"; 
     $http_req .= "Content-Length: $length\r\n"; 
     $http_req .= "\r\n"; 
     $http_req .= $request; 

     // Need to tell fsockopen to use SSL when requested. 
     if ($url_parts['scheme'] == 'https') 
      $host = 'ssl://'.$host; 

     // Open the connection. 
     $socket = @fsockopen (
      $host, $port, $errno, $errstr, $this->_connectionTimeout 
     ); 
     if (!$socket) 
      throw new SoapFault (
       'Client', 
       "Failed to connect to SOAP server ($location): $errstr" 
      ); 

     // Send the request. 
     stream_set_timeout ($socket, $this->_socketTimeout); 
     fwrite ($socket, $http_req); 

     // Read the response. 
     $http_response = stream_get_contents ($socket); 

     // Close the socket and throw an exception if we timed out. 
     $info = stream_get_meta_data ($socket); 
     fclose ($socket); 
     if ($info['timed_out']) 
      throw new SoapFault (
       'Client', 
       "HTTP timeout contacting $location" 
      ); 

     // Extract the XML from the HTTP response and return it. 
     $response = preg_replace (
      '/ 
       \A  # Start of string 
       .*?  # Match any number of characters (as few as possible) 
       ^  # Start of line 
       \r  # Carriage Return 
       $  # End of line 
      /smx', 
      '', $http_response 
     ); 
     return $response; 
    } 

} 
0

tylko użyć "stream_context" aby ustawić limit czasu również do załadunku WSDL (trzeba ustawić opcje SoapClient $ [ 'connection_timeout'] wcześniej):

class SoapClient2 extends SoapClient 
{ 
    public function __construct($wsdl, $options=null) 
    { 
    if(isset($options['connection_timeout'])) 
    { 
     $s_options = array(
      'http' => array(
       'timeout' => $options['connection_timeout'] 
      ) 
     ); 
     $options['stream_context'] = stream_context_create($s_options); 
    } 
    parent::__construct($wsdl, $options); 
    } 
} 
Powiązane problemy