Third-Party Web Services and Availability

Optimizing Performance

The performance of your Web service can significantly affect how well it scales. If your Web service can host only a handful of clients, scaling out to handle a large number of clients can quickly turn into an exercise in futility.

When you leverage a resource within your Web service, there is usually a right and a wrong way to program against it. You should follow recommended best practices for leveraging the particular resource. Here are a number of best practices for programming against a database using ADO.NET:

  • If a managed provider is available for your database, use it instead of its OLEDB and ODBC counterparts.

  • Use stored procedures instead of dynamically generated SQL statements whenever possible.

  • When you iterate through a large result set, use DataReader instead of DataSet.

You should also design the interface of your Web service with scalability in mind. For example, try to reduce the number of network round-trips. An interface that supports a small number of large requests is usually better than one that supports a large number of small requests.

Also, look for opportunities to reduce the amount of data that is sent across the wire. Avoid passing parameters by reference because doing so involves sending the parameter to the server and then back to the client. If the parameters do not need to be returned back to the client, consider passing instances of value types such as Int32, Boolean, and structures. Finally, if a value is only returned from the Web service, decorate the parameter with the out keyword.

Also consider stripping out formatting characters such as tabs, linefeeds, and spaces from the SOAP messages sent between the client and the server. ASP.NET does this for all SOAP messages generated by an .asmx file.

Caching

If used properly, caching can significantly increase the performance of your Web services. You can use caching to reduce the amount of work a Web service needs to perform to process a request. For example, you might cache a data structure that is expensive to initialize, such as a large hash table. You might also cache a set of previously calculated results.

You can also use caching to improve the locality of data used by your Web service. Whenever you access data over a network, the latency associated with the network can significantly affect the performance of your Web service. You can greatly reduce the cost of retrieving that data if you can retrieve it from an in-memory cache instead of going across the network.

Static read-only data is the best candidate for caching. If the data being cached is not read-only, you should implement a cache coherency strategy to ensure that the data in the cache does not become stale.

ASP.NET provides an easy-to-use mechanism for caching the response from a Web service for a period of time. If the Web service receives a similar request, it will return the cached response to the user instead of executing the particular Web method. This can significantly improve the performance of some Web services.

You can configure the cache using the CacheDuration property exposed by the WebMethod attribute. For example, recall that the Banking Web service exposes the GetStockQuote Web method. Suppose the quote received from the Web service can be delayed up to 20 minutes. The following example illustrates the use of the CacheDuration property:

using System; using System.Web.Services; public class Banking {     [WebMethod(CacheDuration=1200)]     public double GetStockQuote(string symbol)     {         double price = 0;         // Obtain the current price for the security....         return price;     } }

When a request for a particular security is received, the Web method returns the price of the security. Because the CacheDuration property is set, if a request for the same security is received in the next 20 minutes (1200 seconds), the ASP.NET runtime will not invoke the GetStockQuote Web method. Instead, it will return the cached response to the client.

Sometimes it is not practical to cache the entire SOAP response returned from a Web method. However, you might have opportunities to cache discrete pieces of data used within the implementation of a Web method. For these situations, the ASP.NET runtime provides a more generic caching mechanism.

Recall that the Banking Web service charges a fee based on the amount transferred. Suppose the fee charged to the customer for performing the wire transfer is maintained in an XML document similar to the one shown here:

<?xml version="1.0"?> <Fees>   <Fee minExclusive="0" maxInclusive="100">.50</Fee>   <Fee minExclusive="100" maxInclusive="250">1.00</Fee>   <Fee minExclusive="250" maxInclusive="500">1.50</Fee>   <Fee minExclusive="500" maxInclusive="1000">2.00</Fee>   <Fee minExclusive="1000" maxInclusive="999999">3.00</Fee> </Fees>

The document contains a list of fees based on the amount transferred. The implementation of the RequestWireTransfer method uses the data in the XML document to determine the fee that should be charged to the client.

using System; using System.Web.Services; using System.Xml; using System.Configuration; public class Banking {     [WebMethod]     public void RequestWireTransfer(int sourceAccount,         int destinationAccount, double amount)     {         // Obtain the Fees.xml document.         XmlDocument feesDocument= new XmlDocument();         string filePath =              (string)HttpContext.GetAppConfig("FeesXmlDocument");         feesDocument.Load(filePath);         // Obtain the fee that should be charged.         string xpathQuery = string.Format("//Fee[@minExclusive < {0}              and @maxInclusive >= {0}]", amount);         XmlNode result = feesDocument.SelectSingleNode(xpathQuery);         double fee = double.Parse(result.InnerText);         // The rest of the implementation....     } }

The RequestWireTransfer Web method first obtains the path for the XML document containing the fees from the web.config file. After the document is loaded, I issue an XPath query to retrieve the fee based on the amount to be transferred.

You can add application-specific configuration parameters to the web.config file by adding add child elements to the appSettings element. The following is a portion of the web.config file that contains the FeesXmlDocument configuration parameter:

<?xml version="1.0" encoding="utf-8" ?> <configuration>   <appSettings>     <add key="FeesXmlDocument" value="c:\Fees.xml"/>   </appSettings>   <!-- The rest of the configuration file... --> </configuration>

The problem with this implementation is that the Fees.xml document will be loaded every time the method is called. Because the XML document is relatively static, this is horribly inefficient.

One way to improve performance is to scope the instance of the XmlDocument class containing the Fees.xml document to the application. The Fees.xml document can be loaded into memory once and then used multiple times. The following code illustrates this technique:

using System; using System.Web.Services; using System.Xml; using System.Configuration; public class Banking {     private static feesDocument;     static Banking()     {         // Obtain the Fees.xml document.         feesDocument = new XmlDocument();         string filePath =              (string)HttpContext.GetAppConfig("FeesXmlDocument");         feesDocument.Load(filePath);     }     [WebMethod]     public void RequestWireTransfer(int sourceAccount,          int destinationAccount, double amount)     {         // Obtain the fee that should be charged.         string xpathQuery = string.Format("//Fee[@minExclusive < {0}             and @maxInclusive >= {0}]", amount);         XmlNode result = feesDocument.SelectSingleNode(xpathQuery);         double fee = double.Parse(result.InnerText);         // The rest of the implementation...     } }

I loaded the XML document within the class's constructor and set the instance of the XmlDocument class to a static variable. The .NET runtime will ensure that the class constructor is called prior to the creation of an instance of the Banking class. Once initialized, the feesDocument static variable can be referenced by all subsequent invocations of the RequestWireTransfer Web method.

The primary issue with this code is that if the rates are updated in the underlying XML document, they will not be reflected in the cache. What we need is a cache coherence strategy so that when the underlying data source changes, the entry in the cache will be updated.

ASP.NET provides a caching mechanism for caching data used by your Web service. Central to the caching mechanism is the Cache class. An instance of the Cache class is created within each application domain hosted by the ASP.NET runtime and is accessible via the HttpContext class.

The following example loads the XML document within the ASP.NET cache and then reloads the XML document each time it is modified:

using System; using System.Web.Caching; using System.Xml; using System.Configuration; class Banking {     [WebMethod]     public void RequestWireTransfer(int sourceAccount,          int destinationAccount, double amount)     {         double fee = Fees.GetFee(amount);         // The rest of the implementation...     } }

The RequestWireTransfer method calls the static GetFee method on the Fees class to obtain the fee for the amount. The Fees class encapsulates all interactions with the ASP.NET cache as well as the business logic used to determine the fee that should be charged to the client.

class Fees {     static Fees()     {         Fees.LoadFeesIntoCache();     }     private static void LoadFeesIntoCache()     {         // Obtain the Fees XML document.         string filePath =              (string)HttpContext.GetAppConfig("FeesXmlDocument");         filePath =              (string)ConfigurationSettings.AppSettings["FeesXmlDocument"];         XmlDocument feesDocument = new XmlDocument();         feesDocument.Load(filePath);         // Create a new cache dependency on the underlying XML file.         CacheDependency cacheDependency = new CacheDependency(filePath);         // Create a new callback object used to invoke the         // Fees_OnRemove method when the cache entry is invalidated.         CacheItemRemovedCallback callback =              new CacheItemRemovedCallback(Fees_OnRemoved);         // Load the Fees XML document into the cache.         HttpContext.Current.Cache.Insert("Fees", feesDocument,              cacheDependency, Cache.NoAbsoluteExpiration,              Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable,              cache);     }

The static constructor is responsible for initializing the cache. This is accomplished by calling the LoadFeesIntoCache static method.

The LoadFeesIntoCache static method obtains the Fees XML document and loads it into the ASP.NET cache. In doing so, it creates a dependency on the underlying XML file. If the Fees XML file is changed, the ASP.NET runtime can invoke a callback.

The CacheDependency class provides multiple overloaded constructors for creating dependencies on a number of different types of entities. You can create a dependency on a file or a directory. You can also create a dependency on a collection of files or directories. If any file or directory within the collection changes, the entry will be invalidated. Finally, you can create a dependency on other instances of the CacheDependency class as well as other cache entries.

In the preceding example, the Fees_OnRemoved method will be invoked when the Fees XML document is modified. This is accomplished by registering the Fees_OnRemoved method as a callback.

Finally I create a new entry in the cache by calling the Insert method. The Insert method has overloads that allow you to set either an exact time or a time window when the entry should expire.

You can also pass the priority of the cache entry as a parameter to the Insert method. If the server runs low on memory, the runtime will use the priority to determine which entries to delete from the cache first. Because I don't want the Fees XML document to be removed from memory, I set the priority to the NonRemovable static property exposed by the object.

    public static double GetFee(double amount)     {         // Obtain the Fees.xml document from the cache.         XmlDocument feesDocument = null;         while(feesDocument == null)         {             (XmlDocument)HttpContext.Current.Cache["Fees"];         }         // Obtain the fee that should be charged.         string xpathQuery = string.Format("//Fee[@minExclusive < {0}             and @maxInclusive >= {0}]", amount);         XmlNode result = feesDocument.SelectSingleNode(xpathQuery);         return double.Parse(result.InnerText);     }

The GetFee method obtains the Fees XML document from the ASP.NET cache and then executes an XPath query to determine the fee based on the amount transferred. Notice that I implemented a spin lock to ensure that I received the Fees XML document instead of a null reference. This is necessary to avoid a race condition with the code responsible for reloading the Fees XML document in the event it is changed.

    public static void Fees_OnRemoved(String k, Object v,          CacheItemRemovedReason r)     {         Fees.LoadFeesIntoCache();     } }

Finally I implement the Fees_OnRemoved event handler. If the Fees XML document is removed from the cache, the LoadFeesIntoCache method will be called to reload the updated document.

The Cache class exposes properties and methods that you can use to maintain the items within the cache. Table 12-1 lists the fields, properties, and methods that the Cache class supports.

Table 12-1  Fields, Properties, and Methods of the Cache Class 

Field

Description

NoAbsoluteExpiration

A static field intended to be passed to the Insert method. Indicates that the item in the cache should not expire by a particular date and time.

NoSlidingExpiration

A static field intended to be passed to the Insert method. Indicates that the item in the cache should not expire within a particular interval of time.

Property

Description

Count

Retrieves the number of items in the cache.

Item

Accesses an item in the cache at a specified index.

Method

Description

Add

Adds an item to the cache.

Get

Retrieves an item in the cache at a specified index.

GetEnumerator

Retrieves an enumerator used to iterate through the keys of the items in the cache.

Insert

Adds an item to the cache.

Remove

Removes a specified item from the cache.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net