|
2008-02-04
Обсудить на форуме
Принять участие в разработке библиотеки/утилиты можно на GitHub.
Dklab_SoapClient это расширенная версия стандартного PHP-класса SoapClient, предназначенная для параллельного удаленного вызова процедур в высоконагруженных проектах.
|
Удаленная процедура функция, расположенная на другой машине кластера. SOAP протокол обмена структурированными сообщениями в распределённой вычислительной среде, стандартизированный Консорциумом W3C.
|
По сравнению со встроенным в PHP SoapClient, поддерживаются дополнительные возможности:
- Одновременное, параллельное выполнение запросов к нескольким удаленным процедурам ключевая особенность библиотеки. Если страница на вашем сайте собирается из 5 удаленных блоков, каждый из которых генерируется по 100ms, их можно запустить параллельно и получить всю страницу целиком не за 500ms, а за те же самые 100ms.
- Реконнект при невозможности установления связи. К сожалению, мир несовершенен, и из-за случайной потери пакетов первая попытка соединения с SOAP-сервером может закончиться тайм-аутом. Это особенно часто происходит, когда проект располагается в нескольких датацентрах. Dklab_SoapClient позволяет задать тайм-аут на время открытия соединения (например, 1 секунду) и, в случае неудачи, повторить попытку указанное число раз. На практике это снижает вероятность итогового сбоя в тысячи раз, т.к. реконнект почти всегда помогает при утере пакета.
- Поддержка тайм-аута на получение данных. Если страница собирается из удаленных блоков, то в случае "подвисания" одного из них "зависает" и вся страница. В то же время, отсутствие одного из блоков при наличии остальных не такая большая беда. Вы можете указать, сколько времени Dklab_SoapClient должен ждать ответа от удаленной процедуры; если время превышено, возникает исключение PHP, которое вы можете обработать по своему усмотрению, не прерывая загрузку остальных блоков.
Код Dklab_SoapClient содержится в одном файле размером 20К. В нем поддерживаются также все возможности встроенного SoapClient (см. документацию):
- Работа с Cookies. Одна процедура может вызвать setcookie(), а другая прочитать значение этой cookie позже.
- Работа с PHP-сессиями. Одна процедура пишет данные в сессию, вторая читает.
- Поддержка WSDL-схем и передача сложных бизнес-объектов.
- Обработка исключений, возникающих в удаленной процедуре.
Совместимость и производительность
Говоря кратко, SOAP и класс SoapClient наиболее удобный и в то же время производительный инструмент вызова удаленных процедур в PHP. Почему?
|
Если в вашей версии PHP нет классов SoapClient и SoapServer, проверьте, подключены ли у вас стандартные расширения soap и curl в php.ini.
|
SOAP стандартный и популярный XML-протокол удаленного вызова процедур в Web. Для него имеется поддержка во всех языках программирования, поэтому вы можете писать SOAP-сервер на любом языке (например, C++, Java или, конечно же, на PHP), а в качестве клиента использовать Dklab_SoapClient.
Другое преимущество SOAP в PHP возможность передачи объектов любой структуры. Например, если удаленная процедура вернула массив массивов объектов некоторого класса, то в клиентском коде вы получите точно такой же массив массивов объектов.
Класс PHP SoapClient написан на Си, а значит, имеет хорошие показатели производительности, несмотря на использование XML-протокола обмена данными.
|
К минусам протокола можно отнести его многословность. Однако, если вы пользуетесь стандартными библиотеками SoapClient и SoapServer (а Dklab_SoapClient основана как раз на встроенном в PHP классе SoapClient), вы эту многословность не ощутите.
|
Примеры использования
Код SOAP-сервера весьма прост
Написать на PHP SOAP-сервер в типичной ситуации очень просто: достаточно создать объект класса SoapServer и запустить у него метод обработки.
|
Листинг 1: SOAP-сервер: файл http://example.com/soapserver.php
|
<?php
// SOAP class to be used for request handling.
class MyServer
{
public function getComplexData($some)
{
return array("obj" => (object)array("prop" => $some), "some" => "thing");
}
public function slowMethod($sleep)
{
sleep($sleep);
return "slept for $sleep seconds";
}
}
// Create and run the server.
$soapServer = new SoapServer(null, array('uri' => 'urn:myschema'));
$soapServer->setObject(new MyServer());
$soapServer->handle(); |
|
SOAP может еще работать в режиме использования WSDL. В нем вы должны явно специфицировать имена методов и типы параметров в специальном WSDL-файле. Этот режим более сложен в использовании, но в некоторых случаях дает определенные преимущества. См. документацию.
|
Пример: простой запрос через SOAP-клиент
|
Листинг 3: Простой запрос к SOAP-серверу {en: Simple query to SOAP server
|
<?php
require_once "../../lib/Dklab/SoapClient.php";
$client = new Dklab_SoapClient(null, array(
'location' => "http://dklab.ru/lib/Dklab_SoapClient/demo/test/Dklab_SoapClient/soapserver.php",
'uri' => 'urn:myschema',
'timeout' => 3,
));
$data = $client->getComplexData(array("abc")); // call MyServer::getComplexData()
$text = $client->slowMethod(1); // call MyServer::slowMethod()
echo "<pre>";
print_r($data);
print_r($text); |
|
Листинг 4: Результат: видно, что структура параметра и результата сохранилась.
|
Array
(
[obj] => stdClass Object
(
[prop] => Array
(
[0] => abc
)
)
[some] => thing
)
slept for 1 seconds |
Пример: параллельные запросы (client->async->method())
|
Листинг 5: Параллельные запросы, каждый по 1 секунде
|
<?php
require_once "../../lib/Dklab/SoapClient.php";
$client = new Dklab_SoapClient(null, array(
'location' => "http://dklab.ru/lib/Dklab_SoapClient/demo/test/Dklab_SoapClient/soapserver.php",
'uri' => 'urn:myschema',
'timeout' => 3,
));
// Send all the requests in parallel (note the "async" property).
$requests = array();
for ($i = 0; $i < 4; $i++) {
$requests[] = $client->async->slowMethod(1);
}
// Now - print all results in 1 second, not in 4 seconds.
$t0 = microtime(true);
echo "<pre>";
foreach ($requests as $request) {
echo $request->getResult() . "\n";
}
echo sprintf("Total time: %.2f seconds", microtime(true) - $t0); |
|
Листинг 6: Результат: суммарное время - около 1 секунды, а не 4
|
slept for 1 seconds
slept for 1 seconds
slept for 1 seconds
slept for 1 seconds
Total time: 1.09 seconds |
Пример: реконнект
|
Листинг 7: Три попытки реконнекта
|
<?php
require_once "../../lib/Dklab/SoapClient.php";
$client = new Dklab_SoapClient(null, array(
'location' => "http://microsoft.com:8876", // non-existed address
'uri' => 'urn:myschema',
'response_validator' => 'responseValidator',
'timeout' => 1,
));
echo "<pre>";
try {
$client->someMethod();
} catch (Exception $e) {
echo $e->getMessage() . "\n";
}
/**
* Must return true if the response is valid, false if not and we need
* to reconnect, or throw an exception if attemts limit is reached.
*/
function responseValidator($response, $numberOfAttempt)
{
if ($response['http_code'] != 200 || !strlen($response['body'])) {
if ($numberOfAttempt < 3) {
echo date("r") . ": Failed after $numberOfAttempt attempts, retrying...\n";
return false;
} else {
throw new SoapFault("Client", date("r") . ": Exception: failed after $numberOfAttempt attempts!");
}
}
return true;
} |
Т.к. мы использовали несуществующий адрес http://microsoft.com:8876, даже 3 попытки соединения не увенчаются успехом. О чем и сообщается в приведенном ниже листинге с результатами работы скрипта:
|
Листинг 8: Результат: так и не соединились. Но если бы соединились, было бы хорошо.
|
Wed, 03 Feb 2009 23:49:55 +0300: Failed after 1 attempts, retrying...
Wed, 03 Feb 2009 23:49:56 +0300: Failed after 2 attempts, retrying...
Wed, 03 Feb 2009 23:49:57 +0300: Exception: failed after 3 attempts! |
Пример: тайм-аут в данных
|
Листинг 9: Вызываем процедуру, работающую 4 секунды
|
<?php
require_once "../../lib/Dklab/SoapClient.php";
$client = new Dklab_SoapClient(null, array(
'location' => "http://dklab.ru/lib/Dklab_SoapClient/demo/test/Dklab_SoapClient/soapserver.php",
'uri' => 'urn:myschema',
'timeout' => 1,
));
try {
// 4 is greater than timeout, so an exception will happen.
$t0 = microtime(true);
$client->slowMethod(3);
} catch (Exception $e) {
echo $e->getMessage() . sprintf(" in %.2fs", microtime(true) - $t0) . "\n";
} |
|
Листинг 10: Результат: тайм-аут после 1 секунды.
|
Response is timed out in 1.03s |
Аналоги и похожие технологии
Apache Thrift
Apache Thrift используется, например, социальной сетью Facebook. Преимущества: версионирование структуры передаваемых данных, поддержка реконнектов и пула соединений с учетом "умерших" серверов. Недостатки:
- Нет поддержки параллельных запросов в PHP-клиенте.
- Весьма "тяжелый" клиент, написанный на чистом PHP (в частности, реализация протокола написана на PHP, а не на Си).
- Общая сложность системы. Например, требуется явно задавать схему данных и генерировать по ней PHP-код. (Правда, во многих случаях это преимущество, но в части лишние накладные расходы.)
cURL-multi
cURL-multi это инструмент для выполнения параллельных HTTP-запросов, который используется в Dklab_SoapClient в качестве транспорта для SoapClient. Передавать с его помощью объекты напрямую нельзя, но можно их сериализовать и обмениваться упакованными данными (конечно, это не очень удобно).
XML-RPС
XML-RPC еще один протокол удаленного вызова процедур. К сожалению, он не так богат возможностями, как SOAP. Например, вы не можете с его помощью отличить ассоциативный массив PHP от объекта PHP при передаче данных. Зато XML-представление данных в этом протоколе проще, чем в SOAP.
Резюме
При помощи библиотеки Dklab_SoapClient вы можете строить страницу вашего сайта из блоков, как из конструктора. Каждый блок запрашивается отдельно и независимо от других, при этом все запросы происходят параллельно. Если один из блоков не уложился в отведенное ему время (тайм-аут), то его можно не отображать на странице. (Кстати, по такому принципу изначально работает система XScript, разработанная в Яндексе. Однако она использует протокол CORBA.)
В отличие от Apache Thrift, класс Dklab_SoapClient не поддерживает возможность работы с пулом SOAP-серверов. Почему? Все очень просто: я верю, что организация пула серверов должна быть решена не на уровне клиента, а на уровне балансировщика запросов (например, HAProxy). В этом случае отключение "умерших" машин производит балансировщик, а не библиотека; это более надежно.
В настоящий момент библиотека имеет статус "бета" и еще недостаточно обкатана, хотя весь ее функционал (включая работу с параллельными запросами и тайм-аутами) покрыт тестами (около 20 сложных тестов). Имеется также плохо воспроизводимая проблема в Windows-версии стандартных PHP-модулей cURL-multi и SoapClient, приводящая иногда к падению PHP (в Unix-версии проблем не наблюдается).
Мы будем рады любым замечаниям и багрепортам, которые лучше оставлять на форуме.
|
|
Пользуетесь? Нравится?.. Пожертвуйте!
Пожертвование пойдет в качестве "спасибо" прямиком
на поддержание мотивации автора. А то он даже не знает, какое
количество людей пользуется той или иной библиотекой
или утилитой...
|