Неприятным следствием отсутствия сессий стала невозможность поддержки транзакций при работе с веб-службами. Для решения проблемы в рамках ООП необходим шаблон «Единица работы» ( unit of work ), суть которого в передаче веб-службе сразу всего упорядоченного множества объектов, подлежащих сохранению в управляемой сервером транзакции.
В общем случае шаблон является аналогом пакетной обработки транзакций, необходимой для сокращения времени жизни единичной транзакции. Например, в репликации данных между СУБД сиквел-операции передаются пачками. Но если раньше такой подход был разновидностью оптимизации и средством избавления от толстых транзакций, то теперь его необходимо было использовать всегда, вместо любой транзакции вообще.
Давайте сравним близкий к реальному псевдокод на сторонах клиента в рамках CORBA с псевдокодом в среде веб-служб. При поддержке сессии все достаточно прозрачно и не нуждается в комментариях.
Псевдокод транзакции в среде CORBA
CosTransactions.Current current = CosTransactions.CurrentHelper.Narrow(
orb.ResolveInitialReferences("TransactionCurrent"));
current.Begin();
try
{
store1.Remove(product, quantity);
store2.Append(product, quantity);
current.Commit();
}
catch (Exception e)
{
current.Rollback();
ShowError("Ошибка выполнения операции: " + e.toString());
}
В среде веб-служб в программе-клиенте приходится надстраивать абстракции DTO [81]. А в серверном приложении, где используются соединения, например, с СУБД или монитором транзакций, необходимо фактически дублировать предыдущий код с раскруткой объекта – единицы работы (unit of work) в реальную транзакцию.
Псевдокод транзакции в среде веб-служб
StoreServiceClient storeServiceClient = new StoreServiceClient(url);
StoreOperationDTO operation1 = storeServiceClient.CreateOperation(store1.Id);
operation1.Type = StoreOperations.Remove;
operation1.ProductId = product.Id;
operation1.Quantity = quantity;
StoreOperationDTO operation2 = storeServiceClient.CreateOperation(store2.Id);
operation2.Type = StoreOperations.Append;
operation2.ProductId = product.Id;
operation2.Quantity = quantity;
UnitOfWork uow = new UnitOfWork();
uof.RegisterDirty(operation1);
uof.RegisterDirty(operation2);
try
{
storeServiceClient.ProcessOperations(uow);
}
catch (Exception e)
{
ShowError("Ошибка выполнения операции: " + e.toString());
}
Вторым «упрощением» стал переход от понятных прикладному программисту деклараций интерфейсов объектов и служб на языке IDL [82]к WSDL [83]– описаниям, ориентированным, прежде всего, на обработку компьютером. Сравним декларации складской службы, возвращающей по запросу текущее количество товарных позиций.
Декларация службы в CORBA IDL
module StockServices
{
typedef float CurrentQuantity;
struct QuantityRequest
{
string stockSymbol;
};
interface StockInventoryService
{
CurrentQuantity getCurrent(in QuantityRequest request);
};
};
Декларация службы в WSDL
version="1.0" encoding="utf-8"?>
name="StockInventoryService"
xmlns: sqs="http://mycompany.com/stockinventoryservice.wsdl"
xmlns: sqsxsd="http://mycompany.com/stockinventoryservice.xsd"
xmlns: soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns: wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns: xsd="http://www.w3.org/2000/10/XMLSchema">
name="CurrentQuantity">
name="stockSymbol" type="string"/>
name="CurrentQuantity">
name="quantity" type="float"/>
name="CurrentQuantity">
name="quantity" type="float"/>
name="getCurrentInput">
name="body" element="sqsxsd: CurrentQuantity"/>
name="getCurrentOutput">
name="body" element="sqsxsd: CurrentQuantity"/>
name="StockInventoryServicePortType">
name="getCurrent">
message="sqs: getCurrentInput"/>
message="sqs: getCurrentOutput"/>
name="StockInventoryServiceSoapBinding"
type="sqs: StockInventoryServicePortType">
style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
name="getCurrent">
soapAction="http://mycompany.com/getCurrent"/>
use="literal"/>
use="literal"/>
name="StockInventoryService">
name="StockInventoryServicePort"
binding="sqs: StockInventoryServiceBinding">
location="http://mycompany.com/StockInventoryService"/>
Третьим «усовершенствованием» стал отказ от автоматической подгрузки связанных объектов [84]в пользу исключительно ручного управления процессом.
В CORBA объекты функционируют на сервере, тогда как на клиенте находится только соответствующая заглушка ( stub ). То есть вы в программе вызываете какой-то метод, а на самом деле происходит обращение к серверу, вызов соответствующего метода у серверного объекта и возврат результата на клиента с возможным обновлением состояния локальных полей заглушки. Аналогично со свойствами объектного типа: связанный объект подгружается по мере необходимости. Всё это происходит прозрачно для программиста, которому не нужно вмешиваться в процесс взаимодействия, но желательно знать, что стоит за манипуляциями с заглушкой на клиенте. Соответственно, существует соблазн вместо реализации на сервере новой функции службы написать код непосредственно на клиенте, благо сделать это легко. Тогда, например, обработка достаточно большой коллекции объектов в цикле может вызвать интенсивный обмен сообщениями с сервером и возникновение узкого места в системе. Аналогичная проблема плохой реализации имеется и при работе приложения напрямую с СУБД.
Читать дальше
Конец ознакомительного отрывка
Купить книгу