Pertanyaan Menangani timeout Sabun di PHP


Saya sedang mengerjakan proyek di mana saya memverifikasi informasi dari pengguna dengan layanan web SOAP. Saat ini saya sedang mengurus kesalahan dengan asumsi bahwa saya menerima tanggapan dari layanan web, tetapi juga perlu menangani kasus tepi batas waktu layanan atau tidak tersedianya.

Dalam kasus timeout atau layanan tidak tersedia, saya perlu berpura-pura bahwa permintaan itu berhasil (bahwa layanan web menyetujui info), tetapi saya tidak jelas tentang pengecualian apa yang dilemparkan.

Beberapa pseudo-code:

// $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
}

Apa yang sepertinya tidak dapat saya temukan adalah:

  1. Apakah timeout a SoapFault? Jika ya, apa cara terbaik untuk membedakan antara kesalahan waktu tunggu dan masalah layanan web (seperti kesalahan ketik, dll.)? Saya menemukan satu halaman yang menyebutkan kesalahan di mana pesan itu sesuatu untuk efek "Kesalahan memuat header", tetapi tidak menyebutkan apakah ini adalah kesalahan Sabun.
  2. Bagaimana tidak tersedianya layanan yang berpotensi akan terjadi? Pengecualian PHP tampaknya seperti itu akan masuk akal (SoapFault akan dikembalikan dari layanan web di mana tidak tersedianya akan menjadi masalah soket atau serupa)?
  3. Apakah ada layanan yang ada (mis. Contoh) yang dapat saya uji batas waktu lawan? Sebagian besar diskusi terkait timeout tampaknya terkait dengan mencegah timeout dengan memperpanjang pengaturan timeout default, yang tidak ideal dalam situasi ini.

32
2018-05-07 14:59


asal


Jawaban:


1) Dalam kasus timeout, PHP melempar pengecualian SoapFault dengan faultcode="HTTP" dan faultstring="Error Fetching http headers".

2) Menurut pendapat saya, cara terbaik untuk membedakan antara kesalahan waktu tunggu dan masalah layanan web adalah dengan melihat faultcode dan faultstring anggota dari Kelas SoapFault.
Secara khusus, faultcode elemen dimaksudkan untuk digunakan oleh perangkat lunak untuk menyediakan mekanisme algoritme untuk mengidentifikasi kesalahan.
Seperti yang Anda juga bisa baca di komentar dari manual PHP, tidak ada metode untuk membaca faultcode properti, jadi Anda harus mengaksesnya secara langsung (mis. $e->faultcode), karena getCode() metode tidak berfungsi.
Itu SOAP 1.1 Spec mendefinisikan empat nilai yang mungkin untuk faultcode bidang:

  • VersiMismatch: Pihak pengolah menemukan namespace yang tidak valid untuk elemen Envelope SOAP
  • MustUnderstand: Unsur anak langsung dari elemen Header SOAP yang entah tidak dipahami atau tidak dipatuhi oleh pihak yang memproses mengandung SOAP mustUnderstand atribut dengan nilai "1"
  • Klien: Kelas kesalahan Klien menunjukkan bahwa pesan salah dibentuk atau tidak berisi informasi yang tepat untuk berhasil. Misalnya, pesan itu mungkin tidak memiliki otentikasi atau informasi pembayaran yang tepat. Secara umum ini merupakan indikasi bahwa pesan tersebut tidak boleh dibenci tanpa perubahan.
  • Server: Kelas server kesalahan menunjukkan bahwa pesan tidak dapat diproses karena alasan yang tidak secara langsung terkait dengan isi pesan itu sendiri tetapi lebih kepada pemrosesan pesan. Misalnya, pemrosesan dapat mencakup komunikasi dengan prosesor hulu, yang tidak merespons. Pesan dapat berhasil pada titik waktu nanti.

Selain kode-kode itu, PHP menggunakan HTTP kode untuk mengidentifikasi kesalahan yang terjadi di tingkat protokol (mis .: kesalahan soket); misalnya, jika Anda mencari add_soap_fault dalam ext / soap / php_http.c kode sumber Anda dapat melihat ketika beberapa jenis kesalahan ini dihasilkan.
Dengan mencari add_soap_fault dan soap_server_fault fungsi dalam file sumber ekstensi PHP SOAP, saya telah membangun daftar PHP berikut SoapFault pengecualian:

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) Untuk mensimulasikan kondisi timeout, coba dengan kode berikut:

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();

?>

Perhatikan sleep panggilan di SoapServer.php skrip dengan waktu (20) lebih lama dari waktu (10) yang ditentukan untuk default_socket_timeout parameter dalam SoapClient.php naskah.
Jika Anda ingin mensimulasikan layanan tidak tersedia, Anda bisa misalnya mengubah location protokol dari http untuk https dalam soapclient.php skrip, dengan asumsi bahwa server web Anda tidak dikonfigurasi untuk SSL; dengan melakukan ini, PHP harus membuang "Tidak dapat terhubung ke host" SoapFault.


30
2018-01-11 22:18



Seperti default_socket_timeout tidak diperhitungkan ketika membuat panggilan SOAP melalui HTTPS:

Bug terbuka pada saat penulisan. Sebagai komentar di posting blog Robert Ludwick dirujuk dalam jawaban yang dihapus Timing Out PHP Soap Calls (21 Oktober 2009; oleh Diterbitkan oleh Robert F. Ludwick) menunjukkan, solusi yang dibahas dalam pos (mengesampingkan SoapClient::__doRequest() dengan permintaan curl) bekerja di sekitar bug ini juga.

Bug terkait lainnya adalah:


Kode yang disebutkan dalam posting blog telah mengalami beberapa perubahan dan dapat ditemukan dalam bentuk terbaru dengan dukungan otentikasi HTTP di sini di Github:

Dalam hal apapun, solusi tidak diperlukan lagi karena masalah ini telah diperbaiki dalam ekstensi SOAPClient PHP.


7
2017-09-08 19:41



Untuk menangani timeout dalam layanan

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

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

3
2018-05-07 16:53



Dari pengalaman saya, jika $e->getMessage adalah "Kesalahan Mengambil header http", Anda berurusan dengan waktu tunggu jaringan.

Jika $e->getMessage adalah sesuatu seperti "Tidak dapat terhubung ke host", layanan yang Anda coba capai sedang down.

Lalu ada "Sepertinya kami tidak punya dokumen XML", yang lebih samar dan bisa berarti hal yang berbeda.


2
2017-12-20 16:15



Saya menggunakan dua faktor untuk mendapatkan perpanjangan SoapClient saya untuk melemparkan pengecualian yang bagus. Pesan dan waktu yang diminta permintaan untuk kembali. Saya pikir pesan kesalahan "Kesalahan Mengambil header http" juga bisa terjadi dalam beberapa kasus lain, oleh karena itu waktu diperiksa.

Kode berikut seharusnya benar

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 {}

Saya kemudian menggunakan 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();
}

Untuk men-debug waktu layanan Anda. Tambahkan baris berikut sebelum memanggil layanan

ini_set('default_socket_timeout', 1);

1
2018-04-27 21:20



Cukup mengatur default_socket_timeout secara global melalui ini mungkin tidak melakukan apa yang Anda inginkan. Ini akan mempengaruhi permintaan SOAP, tetapi juga akan mempengaruhi koneksi keluar lainnya, termasuk koneksi DB. Alih-alih, timpa metode __doRequest () SoapClient untuk membuat koneksi HTTP sendiri. Anda kemudian dapat menetapkan batas waktu Anda sendiri pada soket, mendeteksinya, dan melemparkan pengecualian yang dapat Anda jebak dan tangani.

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
2018-02-19 22:23



Kira saya sedikit terlambat, tetapi jika seseorang masih mencari solusi untuk timeout di klien sabun php - inilah yang bekerja untuk saya:

Pada dasarnya mengganti PHP SoapClient dengan cURL dengan mengatur waktu habis. Hanya perlu diingat, terkadang WS mengharapkan tindakan yang ditentukan dalam header HTTP. Solusi asli yang dipasang di situs web itu tidak termasuk itu (periksa komentar).


0
2018-02-26 11:19



cukup gunakan "stream_context" untuk mengatur pengaturan timeout juga untuk pemuatan WSDL (Anda perlu mengatur opsi $ SoapClient ['connection_timeout'] sebelum):

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);
  }
}

0
2018-04-22 12:32