Анализ потока данных SOAP в реальном времени с помощью PHP


У меня есть корпоративное приложение, которое предоставляет довольно надежный API для сбора данных. В настоящее время я запрашиваю каждого пользователя, которого я хочу обновлять в цикле каждую секунду. Однако новая документация по API теперь предоставляет прямую трансляцию всех изменений для всех пользователей. Мне интересно, как я могу проанализировать эти текущие данные, поскольку они поставляются с php. Вот некоторые подробности:

Данные запрашиваются с помощью запроса SOAP, и я использую PHP для выполнения таких запросов (пример сеанса инициализация с возвращенным уникальным идентификатором):

//Get a session ID for this user for the shoretel WEBAPI
$soap_url = 'http://11.11.11.11:8070/ShoreTelWebSDK?wsdl';
$client = new SOAPClient($soap_url, array( 'proxy_host' => '11.11.11.11', 'proxy_port' => 8070, 'trace' => 1 ) );
$client = new SoapClient($soap_url);

$header = new SoapHeader('http://www.ShoreTel.com/ProServices/SDK/Web');
$client->__setSoapHeaders($header);
$registered_string = $client->RegisterClient(array(
                'clientName' => '11.11.11.211'
            )); 
$registered_string = get_object_vars($registered_string);   
$session =  $registered_string['RegisterClientResult'];

Новый метод позволяет мне указать период времени. Например, если я хотел бы получить все события за 1 минуту, вызов начнется, подождите одну минуту, а затем верните все события, которые произошли в течение этой минуты.

Что я хотел бы сделать, так это захватить каждое событие по мере его возникновения и вставить его в базу данных. Это то, чего я могу достичь с помощью PHP, или мне нужно искать другой язык, чтобы это произошло?

 12
Author: Joel Lewis, 2015-09-04

5 answers

Хорошо, цель состоит в том, чтобы "транслировать ответ на МЫЛО" с "таймаутом" и/или в "интервале"?

Я предлагаю переопределить метод SoapClient __doRequest() и реализовать пользовательское соединение через fsockopen() и передавать данные с помощью stream_get_contents(). Теперь вы получаете поток XML-данных, и то, что вам нужно, находится в его середине. Вам придется извлечь XML-конверт или его части, возможно, с помощью строковых функций или с помощью preg_match для извлечения внутреннего содержимого.

Следующий код обеспечивает выход SoapClientTimeout класс, где тайм-аут устанавливается с помощью stream_set_timeout(). Это для случая использования, когда сервер медленно реагирует, и вы хотите убедиться, когда закончится прослушивание.

Я предлагаю поиграть с ним и настроить поведение тайм-аута в сокете. Потому что вы хотите прекратить прослушивание через некоторое время (интервальная выборка). Итак, вы пытаетесь совместить тайм-аут с блокировкой, чтобы через некоторое время прекратить чтение из потока:

$timeout = 60; // seconds
stream_set_blocking($socket, true);
stream_set_timeout($socket, $timeout); 

Когда у вас есть поток, который блокируется через 1 минуту и закрывается, вам нужен цикл (с твердым условием выхода), запускающий следующий запрос.


class SoapClientTimeout 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 and add "timeout" functionality.
     */
    public function __doRequest ($request, $location, $action, $version, $one_way = 0)
    {
        // fetch host, port, and scheme from url.
        $url_parts = parse_url($location);

        $host = $url_parts['host'];
        $port =  @$url_parts['port'] ?: ($url_parts['scheme'] == 'https' ? 443 : 80);
        $length = strlen ($request);

        // create 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;

        // switch to SSL, when requested
        if ($url_parts['scheme'] == 'https') $host = 'ssl://'.$host;

        // connect
        $socket = @fsockopen($host, $port, $errno, $errstr, $this->_connectionTimeout);

        if (!$socket) {
            throw new SoapFault('Client',"Failed to connect to SOAP server ($location): $errstr");
        }

        // send request with socket timeout
        stream_set_timeout($socket, $this->_socketTimeout);
        fwrite ($socket, $http_req);

        // start reading 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");
        }

        // the stream contains XML data
        // lets 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;
    }

}
 2
Author: Jens A. Koch, 2015-09-15 10:19:25

Привязать сокет

Функция bind может использоваться для привязки сокета к определенному адресу и порту. Для этого нужна структура sockaddr_in, аналогичная функции connect.

Краткий пример

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Couldn't create socket: [$errorcode] $errormsg \n");
}

echo "Socket created \n";

// Bind the source address
if( !socket_bind($sock, "127.0.0.1" , 5000) )
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not bind socket : [$errorcode] $errormsg \n");
}

echo "Socket bind OK \n";

Теперь, когда привязка завершена, пришло время заставить сокет прослушивать соединения. Мы привязываем сокет к определенному IP-адресу и определенному номеру порта. Делая это, мы гарантируем, что все входящие данные, которые направляются на этот номер порта, принимаются этим приложение.

Это делает очевидным, что у вас не может быть 2 сокета, привязанных к одному и тому же порту. Из этого правила есть исключения, но мы рассмотрим это в какой-нибудь другой статье.

Прослушивайте соединения

После привязки сокета к порту следующее, что нам нужно сделать, это прослушать соединения. Для этого нам нужно перевести сокет в режим прослушивания. Функция socket_listen используется для перевода сокета в режим прослушивания. Просто добавьте следующую строку после связывать.

//listen
socket_listen ($sock , 10)

Второй параметр функции socket_listen называется невыполненной работой. Он контролирует количество входящих подключений, которые остаются "ожидающими", если программа уже занята. Таким образом, указание 10 означает, что если 10 соединений уже ожидают обработки, то 11-й запрос на подключение должен быть отклонен. Это станет более понятным после проверки socket_accept.

Теперь начинается основная часть принятия новых подключений.

Принять соединение

Для этого используется функция socket_accept.

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Couldn't create socket: [$errorcode] $errormsg \n");
}

echo "Socket created \n";

// Bind the source address
if( !socket_bind($sock, "127.0.0.1" , 5000) )
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not bind socket : [$errorcode] $errormsg \n");
}

echo "Socket bind OK \n";

if(!socket_listen ($sock , 10))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not listen on socket : [$errorcode] $errormsg \n");
}

echo "Socket listen OK \n";

echo "Waiting for incoming connections... \n";

//Accept incoming connection - This is a blocking call
$client = socket_accept($sock);

//display information about the client who is connected
if(socket_getpeername($client , $address , $port))
{
    echo "Client $address : $port is now connected to us.";
}

socket_close($client);
socket_close($sock);

Выход

Запустите программу. Это должно показать $php /var/www/server.php Созданный сокет Привязка сокета в порядке Сокет слушает нормально Ожидание входящих подключений...

Итак, теперь эта программа ожидает входящих подключений на порту 5000. Не закрывайте эту программу, продолжайте ее запускать. Теперь клиент может подключиться к нему через этот порт. Мы будем использовать клиент telnet для тестирования этого. Откройте терминал и введите $телнет локальный хост 5000

Это сразу же покажет $telnet локальный хост 5000 Пытаюсь 127.0.0.1... Подключен к локальному хосту. Экранирующий символ - "^]". Соединение закрыто иностранным хостом.

И вывод сервера покажет Клиент 127.0.0.1: 36689 теперь подключен к нам.

Таким образом, мы можем видеть, что клиент подключен к серверу. Попробуйте выполнить описанные выше действия, пока не добьетесь идеальной работы.

Примечание

Функция socket_getpeername используется для получения подробной информации о клиент, который подключен к серверу через определенный сокет.

 0
Author: Navnish Bhardwaj, 2015-09-11 14:23:41

Я немного озадачен: SOAP (будучи простым протоколом доступа к объектам) полностью противоположен потоковой передаче в реальном времени. Каждое сообщение SOAP полностью инкапсулировано: у него есть начало и конец. Это просто невозможно передать в потоковом режиме.

Вы упомянули какой-то "новый метод", но не сообщили никаких подробностей. "Живые" обновления могут быть достигнуты с помощью повторяющихся запросов или это может быть фактический поток данных. Если это первое: вы можете уйти с МЫЛОМ, но звонить МЫЛУ каждую секунду - не очень хорошая идея: администратор удаленного сервера не будет доволен таким натиском. Если это последнее: это не может быть МЫЛО. Период.

Если вы не разъясните, что делает "новый метод" и как он вызывается, никто не сможет сказать вам, как решить вашу проблему. Но будьте уверены, PHP в наши дни способен практически на все. Поэтому с какой бы проблемой вы ни столкнулись, вам, безусловно, не нужно переходить на какой-либо другой язык программирования, если вам удобно работать с PHP. Я предлагаю вам изменить свой первоначальный вопрос и добавить соответствующие подробности. Я уверен, что вы найдете здесь множество людей, которые смогут дать вам точное решение.

 0
Author: Vladimir Bashkirtsev, 2015-09-12 20:26:15

Пара вещей с вашим кодом:

  1. Вы создаете экземпляр $client дважды, поэтому первый $client эффективно перезаписывается.
  2. Ваш заголовок SOAP имеет только аргумент пространства имен. Это может и не потребоваться.

Похоже, что ваш код регистрирует только идентификатор сеанса. Неясно, где находятся данные, может быть, в другом элементе массива $registered_string? Я подозреваю, что после этого вам нужно написать еще больше кода.

Обычно, по моему опыту, вы бы тогда использовали возвращенный идентификатор сеанса для создания URL-адреса. Затем вы будете использовать этот URL-адрес для доступа к потоку данных. Это будет НЕ вызов SOAP, а обычный веб-запрос с использованием любого метода, который вы предпочитаете (например, file_get_contents('http://example.com/blah?session=[SESSIONID]')). Сервер будет использовать идентификатор сеанса для аутентификации пользователя перед возвратом данных.

Если это так работает, то ответ - да, вы можете получить доступ к данным и вставить их в базу данных, но только пока действителен идентификатор сеанса. Когда истечет срок действия идентификатора сеанса, вам придется позвонить Снова зарегистрируйте client(). Это будет верно независимо от того, какой язык вы используете.

 0
Author: Kris Peeling, 2015-09-15 03:14:50

Простым способом может быть использование websockets или даже запрос ajax с таймаутом для вашего файла php soap.

 0
Author: Atul Mishra, 2015-09-15 07:38:42