Improving Web Services Performance

Improving .NET Application Performance and Scalability

J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman
Microsoft Corporation

May 2004

Related Links

Home Page for Improving .NET Application Performance and Scalability

Chapter 5, Improving Managed Code Performance

Checklist: Web Services Performance

Send feedback to [email protected]

patterns & practices Library

Summary: This chapter focuses on design guidelines and techniques, such as state management, asynchronous invocation, serialization, threading, to help you develop efficient Web services. This chapter also presents a formula for reducing thread contention and HTTP connections to increase the throughput for your Web services.

Contents

Objectives
Overview
How to Use This Chapter
Architecture
Prescriptive Guidance for Web Services, Enterprise Services, and .NET Remoting
Performance and Scalability Issues
Design Considerations
Implementation Considerations
Connections
Threading
One-Way (Fire-and-Forget) Communication
Asynchronous Web Methods
Asynchronous Invocation
Timeouts
WebMethods
Serialization
Caching
State Management
Bulk Data Transfer
Attachments
COM Interop
Measuring and Analyzing Web Services Performance
Web Service Enhancements
Summary
Additional Resources

Objectives

  • Identify top Web services performance issues.
  • Design scalable Web services that meet your performance objectives.
  • Improve serialization performance.
  • Configure the HTTP runtime for optimum performance.
  • Improve threading efficiency.
  • Evaluate and choose the most appropriate caching mechanism.
  • Decide when to maintain state.
  • Evaluate and choose the most appropriate bulk data transfer mechanism.

Overview

Services are the ideal communication medium for distributed applications. You should build all of your services using Web services and then, if necessary, use Enterprise Services or Microsoft® .NET remoting within the boundaries of your service implementation. For example, you might need to use Enterprise Services for distributed transaction support or object pooling.

Web services are ideal for cross-platform communication in heterogeneous environments because of their use of open standards such as XML and Simple Object Access Protocol (SOAP). However, even in a closed environment where both client and server systems use the .NET Framework, ease of deployment and maintenance make Web services a very attractive approach.

This chapter begins by examining the architecture of ASP.NET Web services, and then explains the anatomy of a Web services request from both the client and server-side perspectives. You need a solid understanding of both client and server to help you identify and address typical Web services performance issues. An understanding of Web services architecture will also help you when you configure the HTTP runtime to optimize Web services performance. The chapter then presents a set of important Web services design considerations, followed by a series of sections that address the top Web services performance issues.

How to Use This Chapter

To get the most out of this chapter:

  • Jump to topics or read from beginning to end. The main headings in this chapter help you to locate the topics that interest you. Alternatively, you can read the chapter from beginning to end to gain a thorough appreciation of performance and scalability design issues.
  • Use the checklist. Use "Checklist: Web Services Performance" in the "Checklists" section of this guide to quickly view and evaluate the guidelines presented in this chapter.
  • Use the "Architecture" section of this chapter to learn how Web services work. By understanding Web services architecture, you can make better design and implementation choices.
  • Use the "Design Considerations" section of this chapter. This section helps you to understand the higher-level decisions that affect implementation choices for Web services code.
  • Read Chapter 6, "Improving ASP.NET Performance" Many of the performance optimizations described in Chapter 6, "Improving ASP.NET Performance" — such as tuning the thread pool and designing and implementing efficient caching — also apply to ASP.NET Web services development.
  • Read Chapter 13, "Code Review: .NET Application Performance" See the "Web Services" section of Chapter 13 for specific guidance.
  • Measure your application performance. Read the "Web Services" and ".NET Framework Technologies" sections of Chapter 15, "Measuring .NET Application Performance" to learn about key metrics that you can use to measure application performance. It is important that you are able to measure application performance so that you can target performance issues accurately.
  • Test your application performance. Read Chapter 16, "Testing .NET Application Performance" to learn how to apply performance testing to your application. It is important that you apply a coherent testing process and that you are able to analyze the results.
  • Tune your application performance. Read the "Web Services" section of Chapter 17, "Tuning .NET Application Performance" to learn how to resolve performance issues identified through the use of tuning metrics.

Architecture

The server-side infrastructure is based on ASP.NET and uses XML serialization. When the Web server processes an HTTP request for a Web service, Internet Information Services (IIS) maps the requested extension (.asmx) to the ASP.NET Internet server application programming interface (ISAPI) extension (Aspnet_isapi.dll). The ASP.NET ISAPI extension then forwards the request to the ASP.NET worker process, where it enters the request processing pipeline, which is controlled by the HttpRuntime object. See Figure 10.1 for an illustration of the Web services architecture and request flow.

Figure 10.1: ASP.NET Web services architecture and request flow

The request is initially passed to the HttpApplication object, followed by the series of registered HttpModule objects. HttpModule objects are registered in the system-wide Machine.config file or in the <httpModules> section of an application-specific Web.config file. HttpModule objects handle authentication, authorization, caching, and other services.

After passing through the HTTP modules in the pipeline, HttpRuntime verifies that the .asmx extension is registered with the WebServiceHandlerFactory handler. This creates an HTTP handler, an instance of a type that derives from WebServiceHandler, which is responsible for processing the Web services request. The HTTP handler uses reflection to translate SOAP messages into method invocations. WebServiceHandler is located in the System.Web.Services.Protocols namespace.

Client-Side Proxy Classes

On the client side, proxy classes provide access to Web services. Proxy classes use XML serialization to serialize the request into a SOAP message, which is then transported using functionality provided by the System.Net namespace.

You can use the Wsdl.exe tool to automatically generate the proxy class from the Web Services Description Language (WSDL) contract file. Depending on the bindings specified in the WSDL, the request issued by the proxy may use the HTTP GET, HTTP POST, or HTTP SOAP protocols.

The proxy class is derived from one of the following base classes:

  • System.Web.Services.Protocols.HttpGetClientProtocol
  • System.Web.Services.Protocols.HttpPostClientProtocol
  • System.Web.Services.Protocols.SoapHttpClientProtocol

These all derive from System.Web.Services.Protocols.HttpWebClientProtocol, which in turn derives from the System.Web.Services.Protocols.WebClientProtocol base class in the inheritance chain. WebClientProtocol is the base class for all automatically generated client proxies for ASP.NET Web services, and, as a result, your proxy class inherits many of its methods and properties.

Prescriptive Guidance for Web Services, Enterprise Services, and .NET Remoting

Services are the preferred communication technique to use across application boundaries, including platform, deployment, and trust boundaries. You can implement services today by using Web services or Web Services Enhancements (WSE). Although WSE provides a rich set of features, you should evaluate whether or not you can accept the WSE support policy. Enterprise Services provides component services such as object pooling, queued components, a role-based security model and distributed transactions, and should be used as an implementation detail within your service when you need those features. .NET remoting is preferred for cross-application communication within the same process.

Object Orientation and Service Orientation

When you design distributed applications, use the services approach whenever possible. Although object orientation provides a pure view of what a system should look like and is effective for producing logical models, an object-based approach can fail to consider real-world factors, such as physical distribution, trust boundaries, and network communication, as well as nonfunctional requirements, such as performance and security.

Table 10.1 summarizes some key differences between object orientation and service orientation.

Table 10.1: Object Orientation vs. Service Orientation

Object Orientation Service Orientation
Assumes a homogeneous platform and execution environment. Assumes a heterogeneous platform and execution environment.
Shares types, not schemas. Shares schemas, not types.
Assumes cheap, transparent communication. Assumes variable cost, explicit communication.
Objects are linked: object identity and lifetime are maintained by the infrastructure. Services are autonomous: security and failure isolation are a must.
Typically requires synchronized deployment of both client and server. Allows continuous, separate deployment of client and server.
Is easy to conceptualize and thus provides a natural model to follow. Builds on ideas from component software and distributed objects. Dominant theme is to manage/reduce sharing between services.
Provides no explicit guidelines for state management and ownership. Owns and maintains state or uses the reference state.
Assumes a predictable sequence, timeframe, and outcome of invocations. Assumes message-oriented, potentially asynchronous, and long-running communications.
Goal is to transparently use functions and types remotely. Goal is to provide inter-service isolation and wire interoperability based on standards.

Application Boundaries

Common application boundaries include platform, deployment, trust, and evolution. (Evolution refers to whether or not you develop and upgrade applications together.) When you evaluate architecture and design decisions that affect your application boundaries, consider the following:

  • Objects and remote procedure calls (RPC) are appropriate within boundaries.
  • Services are appropriate across and within boundaries.

Recommendations for Web Services, Enterprise Services, and .NET Remoting

When you are working with ASP.NET Web services, Enterprise Services, and .NET remoting, Microsoft recommends that you:

  • Build services by using ASP.NET Web Services.
  • Enhance your ASP.NET Web services with WSE if you need the WSE feature set and you can accept the support policy.
  • Use object technology, such as Enterprise Services or .NET remoting, within a service implementation.
  • Use Enterprise Services inside your service boundaries in the following scenarios:
    • You need the Enterprise Services feature set (such as object pooling; declarative, distributed transactions; role-based security; and queued components).
    • You are communicating between components on a local server and you have performance issues with ASP.NET Web services or WSE.
  • Use .NET remoting inside your service boundaries in the following scenarios:
    • You need in-process, cross-application domain communication. Remoting has been optimized to pass calls between application domains extremely efficiently.
    • You need to support custom wire protocols. Understand, however, that this customization will not port cleanly to future Microsoft implementations.

Caveats

When you work with ASP.NET Web services, Enterprise Services, or .NET remoting, consider the following caveats:

  • If you use ASP.NET Web services, avoid or abstract your use of low-level extensibility features such as the HTTP Context object.
  • If you use .NET remoting, avoid or abstract your use of low-level extensibility such as .NET remoting sinks and custom channels.
  • If you use Enterprise Services, avoid passing object references inside Enterprise Services. Also, do not use COM+ APIs. Instead, use types from the System.EnterpriseServices namespace.

More Information

  • For guidelines on how to make .NET Enterprise Services components execute as quickly as C++ COM components, see the MSDN® article, ".NET Enterprise Services Performance," at http://msdn.microsoft.com/library/en-us/dncomser/html/entsvcperf.asp.
  • For more information on Enterprise Services, see Chapter 8, "Improving Enterprise Services Performance"
  • For more information on remoting, see Chapter 11, "Improving Remoting Performance"

Performance and Scalability Issues

The main issues that can adversely affect the performance and scalability of your Web services are summarized in the following list. Subsequent sections in this chapter provide strategies and technical information to prevent or resolve each of these issues.

  • Incorrect communication mechanism. Currently, there are three main technologies for remoting a method call: Enterprise Services, .NET remoting, and ASP.NET Web services. The best choice depends upon various factors, including the source and target platforms, whether you need to communicate across an intranet or the Internet, whether you require additional services such as distributed transactions, your security requirements, deployment considerations (such as whether your communication must pass through a firewall), other port limitations, and so on.
    • Web services. Use Web services to build your services.
    • Enterprise Services. If you use Web services to build your services, you may still need to use Enterprise Services within your service implementation. For example, you may need it to support distributed transactions or if you want to use object pooling.
    • .NET remoting. Use remoting for same-process, cross-application domain communication or for remote communication if you need to integrate with a legacy protocol. If you use remoting, avoid custom proxies, custom sinks, and using contexts. This helps to avoid compatibility issues with future communication technologies.
  • Chatty calls. Network round trips to and from a Web service can be expensive. This issue is magnified if clients need to issue multiple requests to a Web service to complete a single logical operation.
  • Improper choice of parameters. Your choice of parameters depends upon various factors, such as interoperability, the varying platforms used by the clients, maintainability, the type of encoding format used, and so on. Improper choice of parameters can lead to a number of issues, including increased serialization costs and potential versioning problems for the Web service (for example where a custom type is updated). Where possible, you should use primitive types. If interoperability is an issue, consider using the XmlElement and XmlDocument types and choose types specific to your application, such as an Employee or Person class.
  • Serialization. Serializing large amounts of data and passing it to and from Web services can cause performance-related issues, including network congestion and excessive memory and processor overhead.

    Other issues that affect the amount of data passed across the wire include improper data transfer strategies for large amounts of data. Selecting an appropriate data transfer strategy — such as using a SOAP extension that performs compression and decompression or offloading data transfer to other services — is critical to the performance of your Web services solution.

  • Improper choice of encoding format. You can use either literal or SOAP encoding. SOAP encoding involves more SOAP-processing overhead as compared to literal encoding.
  • Lack of caching or inefficient caching. In many situations, application or perimeter caching can improve Web services performance. Caching-related issues that can significantly affect Web services performance include failure to use caching for Web methods, caching too much data, caching inappropriate data, and using inappropriate expiration settings.
  • Inefficient state management. Inefficient state management design in Web services can lead to scalability bottlenecks because the server becomes overloaded with state information that it must maintain on a per-user basis. Common pitfalls for Web services state management include using stateful Web methods, using cookie container–based state management, and choosing an inappropriate state store. The most scalable Web services maintain no state.
  • Misuse of threads. It is easy to misuse threads. For example, you might create threads on a per-request basis or you might write code that misuses the thread pool. Also, unnecessarily implementing a Web method asynchronously can cause more worker threads to be used and blocked, which affects the performance of the Web server.

    On the client side, consumers of Web services have the option of calling Web services asynchronously or synchronously. Your code should call a Web service asynchronously only when you want to avoid blocking the client while a Web service call is in progress. If you are not careful, you can use a greater number of worker and I/O threads, which negatively affects performance. It is also slower to call a service asynchronously; therefore, you should avoid doing so unless your client application needs to do something else while the service is invoked.

  • Inefficient Web method processing. A common example of inefficient processing is not using a schema to validate input upfront. This issue can be significant because the Web method may de-serialize the incoming message and then throw exceptions later on while processing the input data.

Design Considerations

To help ensure that you create efficient Web services, there are a number of issues that you must consider and a number of decisions that you must make at design time. The following are major considerations:

  • Design chunky interfaces to reduce round trips.
  • Prefer message-based programming over RPC style.
  • Use literal message encoding for parameter formatting.
  • Prefer primitive types for Web services parameters.
  • Avoid maintaining server state between calls.
  • Consider input validation for costly Web methods.
  • Consider your approach to caching.
  • Consider approaches for bulk data transfer and attachments.
  • Avoid calling local Web services.

Design Chunky Interfaces to Reduce Round Trips

Design chunky interfaces by exposing Web methods that allow your clients to perform single logical operations by calling a single Web method. Avoid exposing properties. Instead, provide methods that accept multiple parameters to reduce roundtrips.

Do not create a Web service for each of your business objects. A Web service should wrap a set of business objects. Use Web services to abstract these objects and increase the chunkiness of your calls.

Prefer Message-Based Programming Over RPC Style

You can design Web services by using either of two programming models: messaging style and RPC style. The RPC style is based on the use of objects and methods. Web methods take object parameters to do the processing, and then return the results. This style generally relies on making multiple Web method calls to complete a single logical operation, as shown in the following code snippet.

//client calling a Web service
Serv.SendItemsToBePurchased(Array[] items);
Serv.ShippingAddress(string Address);
Serv.CheckOut();

The messaging style does not focus on objects as parameters. It is based on a data contract (schema) between the Web service and its clients. The Web service expects the message to be XML that conforms to the published data contract.

//Client
string msg = "<Items>…</Items>";
MyMethod(msg);

//Server
[WebMethod]
void MyMethod(string msg){ . . . }

This approach allows you to package and send all parameters in a single message payload and complete the operation with a single call, thus reducing chatty communication. The Web service may or may not return results immediately; therefore, the clients do not need to wait for results.

Use Literal Message Encoding for Parameter Formatting

The encoded formatting of the parameters in messages creates larger messages than literal message encoding (literal message encoding is the default). In general, you should use literal format unless you are forced to switch to SOAP encoding for interoperability with a Web services platform that does not support the literal format.

Prefer Primitive Types for Web Services Parameters

There are two broad categories of parameter types that you can pass to Web services:

  • Strongly typed. These include .NET types such as double and int, and custom objects such as Employee, Person, and so on. The advantage of using strongly typed parameters is that .NET automatically generates the schema for these types and validates the incoming values for you. Clients use the schema to construct appropriately formatted XML messages before sending them.
  • Loosely typed. These parameters use the string type. Note that you should not pass XML documents as string parameters because the entire string then needs to be XML encoded. For example, < and > needs to be converted to &lt and &gt and so on. Instead, you should use either an XmlElement parameter or implement IXmlSerializable. The latter is the most efficient and works well for large data sizes, regardless of which encoding style you use, you should prefer simple primitive types like int, double, and string for Web services parameters. Use of primitive types leads to reduced serialization and automatic and efficient validation by the .NET Framework.

Avoid Maintaining Server State Between Calls

Maintaining per-caller state in memory on the server limits scalability because the state consumes server resources. As an alternative, you can pass state back and forth between the client and Web service. Although this approach enables you to scale your service, it does add performance overhead — including the time taken to serialize, transmit, parse, and de-serialize the state with each call.

Consider Input Validation for Costly Web Methods

If you have a Web method that performs costly and time-consuming processing, consider validating the Web method input before processing it. It can be more efficient to accept the validation overhead to eliminate unnecessary downstream processing. However, unless you are likely to receive invalid input frequently, you should probably avoid schema validation due to the significant overhead that it introduces. You need to assess your specific situation to determine whether or not schema validation is appropriate.

You can validate input data either by using SOAP extensions or by using separate internal helper methods that your Web methods call. The advantage of using SOAP extensions is that they permit you to separate your validation code from your business logic. If there is any schema change in the future, the extension can change independently of the Web method.

Another option is to use the XmlValidatingReader class to perform schema-based validation, as shown in the following code snippet.

[WebMethod]
public void ValidateCreditCard(string xmlCardInfo){
  try
  {
    // Create and load a validating reader
    XmlValidatingReader reader = new XmlValidatingReader(xmlCardInfo,
                                                       
XmlNodeType.Element, null);

    // Attach the XSD schema to the reader
    reader.Schemas.Add(
           "urn:CardInfo-schema",@"http://localhost/Card/Cardschema.xsd");

    // Set the validation type for XSD schema.
    // XDR schemas and DTDs are also supported
    reader.ValidationType = ValidationType.Schema;

    // Create and register an event handler to handle validation errors
    reader.ValidationEventHandler += new ValidationEventHandler(
                                                        ValidationErrors );

    // Process the input data
    while (reader.Read())
    {
    . . .
    }

    // Validation completed successfully
  }
  catch

  { . . .}
}
// Validation error event handler
private static void ValidationErrors(object sender, ValidationEventArgs args)
{
  // Error details available from args.Message
  . . .
}

Consider Your Approach to Caching

You can greatly enhance Web services performance by caching data. With ASP.NET Web services, you can use many of the same caching features that are available to ASP.NET applications. These include ASP.NET output caching, HTTP response caching, and ASP.NET application caching.

In common with any caching solution, your caching design for a Web service must consider issues such as how frequently the cached data needs to be updated, whether or not the data is user-specific or application-wide, what mechanism to use to indicate that the cache needs updating, and so on. For more information about caching with Web services, see the "Caching" section later in this chapter.

Consider Approaches for Bulk Data Transfer and Attachments

You can use the following approaches to optimize the performance of bulk data transfer:

  • Chunking. With this approach, you use fixed-size byte arrays to send the data one chunk at a time.
  • Offloading the transfer. With this approach, you return a URL from your Web service which points to the file to be downloaded.
  • Compression. You can use a SOAP extension to compress the SOAP messages before transmitting them. This helps when you are constrained primarily by network bandwidth or latency.

To handle attachments, your options include:

  • WS-Attachments
  • Base 64 encoding
  • SOAP Message Transmission Optimization Mechanism (MTOM)

More Information

For more information about these approaches, see the "Bulk Data Transfer" and "Attachments" sections later in this chapter.

Avoid Calling Local Web Services

Web services located on the same computer as a client ASP.NET application share the same thread pool with the ASP.NET application. Therefore, the client application and the Web service share the same threads and other related resources, such as CPU for request processing. Calling a local Web service also means that your request travels through the entire processing pipeline and incurs overhead, including serialization, thread switching, request queuing, and de-serialization.

In addition, the maxconnection attribute of Machine.config has no affect on the connection limit for making calls to local Web services. Therefore, local Web services always tend to give preference to the requests that come from the local computer over requests that come from other machines. This degrades the throughput of the Web service for remote clients.

There are two main approaches to solving this problem:

  • Factor out the Web services business logic into a separate assembly, and call the assembly from the client application as well as the Web service.
  • Load the Web services assembly directly and call its methods. This approach is not as intuitive as the first.

More Information

  • For IIS 6.0 – specific deployment mitigation, refer to "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance"
  • For more information about how to structure your application properly, refer to "Application Architecture for .NET: Designing Applications and Services" on MSDN at http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp.

Implementation Considerations

When you move from application design to development, consider the implementation details of your Web services. Important Web services performance measures include response times, speed of throughput, and resource management:

  • You can reduce request times and reduce server load by caching frequently used data and SOAP responses.
  • You can improve throughput by making effective use of threads and connections, by optimizing Web method serialization, and by designing more efficient service interfaces. Tune thread pooling to reduce contention and increase CPU utilization. To improve connection performance, configure the maximum limit of concurrent outbound calls to a level appropriate for the CPU performance.
  • You can improve resource management by ensuring that shared resources, such as connections, are opened as late as possible and closed as soon as possible, and also by not maintaining server state between calls.

By following best practice implementation guidelines, you can increase the performance of Web services. The following sections highlight performance considerations for Web services features and scenarios.

Connections

When you call Web services, transmission control protocol (TCP) connections are pooled by default. If a connection is available from the pool, that connection is used. If no connection is available, a new connection is created, up to a configurable limit. There is always a default unnamed connection pool. However, you can use connection groups to isolate specific connection pools used by a given set of HTTP requests. To use a separate pool, specify a ConnectionGroupName when you make requests. If you don't specify a connection group, the default connection pool is used. To use connections efficiently, you need to set an appropriate number of connections, determine whether connections will be reused, and factor in security implications.

The following recommendations improve connection performance:

  • Configure the maxconnection attribute.
  • Prioritize and allocate connections across discrete Web services.
  • Use a single identity for outbound calls.
  • Consider UnsafeAuthenticatedConnectionSharing with Windows Integrated Authentication.
  • Use PreAuthenticate with Basic authentication.

Configure The maxconnection Attribute

The maxconnection attribute in Machine.config limits the number of concurrent outbound calls.

Note   This setting does not apply to local requests (requests that originate from ASP.NET applications on the same server as the Web service). The setting applies to outbound connections from the current computer, for example, to ASP.NET applications and Web services calling other remote Web services.

The default setting for maxconnection is two per connection group. For desktop applications that call Web services, two connections may be sufficient. For ASP.NET applications that call Web services, two is generally not enough. Change the maxconnection attribute from the default of 2 to (12 times the number of CPUs) as a starting point.

<connectionManagement>
  <add address="*" maxconnection="12"/>
</connectionManagement> 

Note that 12 connections per CPU is an arbitrary number, but empirical evidence has shown that it is optimal for a variety of scenarios when you also limit ASP.NET to 12 concurrent requests (see the "Threading" section later in this chapter). However, you should validate the appropriate number of connections for your situation.

Increasing the maxconnection attribute results in increased thread pool and processor utilization. With the increase in the maxconnection value, a higher number of I/O threads will be available to make outbound concurrent calls to the Web service. As a result, you process incoming HTTP requests more quickly.

Before Making the Change

You should consider increasing the connections only if you have available CPU. You should always check processor utilization before increasing the attribute because increasing the attribute results in more work for the processor, as described above. For this reason, increasing this attribute makes sense only when your processor utilization is below the threshold limits (usually less than 75 percent utilization).

For more information, see the "Threading" section later in this chapter.

Evaluating the Change

Changing the attribute may involve multiple iterations for tuning and involves various trade-offs with respect to thread pool utilization. Therefore, the changes in the maxconnection attribute may require changes to other thread pool – related configuration attributes, such as maxWorkerThreads and maxIoThreads.

When you load test your application after making the configuration changes, you should monitor CPU utilization and watch the ASP.NET Applications/Requests/Sec and ASP.NET Applications/Requests in Application Queue performance counters. Requests in Application Queue should decrease while Requests/Sec and CPU utilization should increase.

Prioritize and Allocate Connections Across Discrete Web Services

Enumerate and prioritize the Web services you call. Allocate more connections to your critical Web services. You specify each Web service by using the address attribute as follows.

<connectionManagement>
    <add address="WebServiceA" maxconnection="8">
    <add address="WebServiceB" maxconnection="4">
</connectionManagement>

For example, if your application typically makes more requests to WebServiceA than WebServiceB, you can dedicate more connections, as shown in the example above.

Use a Single Identity for Outbound Calls

Use a single trusted identity for making Web services calls where you can. This helps limit the number of separate connection pools. Although you may need to create separate pools of connections for different discrete Web services, avoid creating pools per user. If you need to create pools per user, then specify a ConnectionGroupName when you call the Web service, but be aware that this hurts performance and leads to a large number of pools.

The connection pool your call uses is not determined by the identity of the caller. The ConnectionGroupName determines which connection pool is used. If separate identities use the same ConnectionGroupName, they use the same pool of connections, as shown in the following code snippet.

// Create a secure group name.
….
serv = new WebService1();

// Set the PreAuthenticate property to send the authenticate request in 
first go
serv.PreAuthenticate=true;

// Set the client side credentials
ICredentials conCredentials = 
                 new NetworkCredential("UserId","Password","NPSTest" );
serv.Credentials = conCredentials;

// Do not allow the server to auto redirect as this may compromise security
serv.AllowAutoRedirect=false;
// Use the same connectionGroup Name for all the calls
serv.ConnectionGroupName = "SameForAllUsers";

You may need to create separate pools of connections for different discrete Web services or if you flow the identity of the original caller.

If ASP.NET calls Web services that allow anonymous callers, connections from the default connection pool are used. This is the default behavior unless you specify a ConnectionGroupName, as shown in above example.

Consider UnsafeAuthenticatedConnectionSharing with Windows Integrated Authentication

If your ASP.NET application calls a Web service that uses Microsoft Windows® integrated authentication, consider enabling UnsafeAuthenticatedConnectionSharing. By default, when you connect using Windows Integrated authentication, connections are opened and closed per request. This means that connections are not pooled by default. By enabling UnsafeAuthenticatedConnectionSharing, you keep connections open so they can be reused.

Consider the following guidelines:

  • If you are in a trusted environment and you connect using a single trusted identity, consider improving performance by setting UnsafeAuthenticatedConnectionSharing to true.
    //set the UnsafeAuthenticatedConnectionSharing to true
    myWebService.UnsafeAuthenticatedConnectionSharing = true;
    NetworkCredential myCred = new 
    NetworkCredential("UserA","PasswordA","DomainA");
    CredentialCache myCache = new CredentialCache();
    myCache.Add(new Uri("http://Someserver/WS/service1.asmx"), "NTLM", 
    myCred);
    myWebService.Credentials = myCache;
    myWebService.ConnectionGroupName = "SameName";
    string result = myWebService.HelloWorld();
    
    //as the ConnectionGroupName property is same for different client requests //only the first connection from above gets authenticated
    // the request below reuses the connection from above
    myCred = new NetworkCredential("UserB","PasswordB","DomainB");
    CredentialCache myCache = new CredentialCache();
    myCache.Add(new Uri("http://Someserver/WS/service1.asmx"), "NTLM", 
    myCred);
    myWebService.Credentials = myCache;
    myWebService.ConnectionGroupName = "SameName";
    result = myWebService.HelloWorld();
    
  • If you call a Web service by using the ASP.NET application original caller's identity, then you should avoid enabling UnsafeAuthenticatedConnectionSharing because connections would be shared across calls. Alternatively, you can enable UnsafeAuthenticatedConnectionSharing, and then assign users to individual connection groups by using a ConnectionGroupName. Either option is inefficient and results in a high number of pools.
  • If you need to connect to separate discrete Web services, assign calls to separate pools using ConnectionGroupName.
  • If you need to connect to a server that uses AuthPersistence (it authenticates a whole connection and not a single request), then you should set UnsafeAuthenticatedConnectionSharing to true, and specify some random connection group name. You will need to determine which application requests will go to that connection group because the server will no longer challenge for authentication on that connection.

More Information

  • For more information about AuthPersistence, see Microsoft Knowledge Base article 318863, "HOW TO: Modify the AuthPersistence Metabase Entry Controls When Clients Are Authenticated," at http://support.microsoft.com/default.aspx?scid=kb;en-us;318863.
  • For more information on UnsafeAuthenticatedConnectionSharing, see the .NET Framework documentation.

Use PreAuthenticate with Basic Authentication

If you use Basic authentication, the proxy's PreAuthenticate property can be set to true or false. Set it to true to supply specific authentication credentials to cause a WWWauthenticate HTTP header to be passed with the Web request. This prevents the Web server from denying access to the request and performing authentication on the subsequent retry request.

Note   Pre-authentication only applies after the Web service successfully authenticates the first time. Pre-authentication has no impact on the first Web request.
private void ConfigureProxy( WebClientProtocol proxy, 
                             string domain, string username, 
                             string password )
{
  // To improve performance, force pre-authentication
  proxy.PreAuthenticate = true;
  // Set the credentials
  CredentialCache cache = new CredentialCache();
  cache.Add( new Uri(proxy.Url),
             "Negotiate",     
             new NetworkCredential(username, password, domain) );
  proxy.Credentials = cache;
  proxy.ConnectionGroupName = username;
}

Threading

Web Services use ASP.NET thread pooling to process requests. To ensure that your Web Services use the thread pool most effectively, consider the following guidelines:

  • Tune the thread pool using the Formula for Reducing Contention.
  • Consider minIoThreads and minWorkerThreads for intermittent burst load.

Tune the Thread Pool by Using the Formula for Reducing Contention

The Formula for Reducing Contention can give you a good starting point for tuning the ASP.NET thread pool. Consider using the Microsoft product group recommended settings (shown in Table 10.2) if you have available CPU, your application performs I/O bound operations (such as calling a Web method or accessing the file system), and you have queued requests as indicated by the ASP.NET Applications/Requests in Application Queue performance counter.

Table 10.2: Recommended Threading Settings for Reducing Contention

Configuration setting Default (.NET 1.1) Recommended value
maxconnection 2 12 * #CPUs
maxIoThreads 20 100
maxWorkerThreads 20 100
minFreeThreads 8 88 * #CPUs
minLocalRequestFreeThreads 4 76 * #CPUs

To address this issue, you need to configure the following items in Machine.config. The changes described in the following list should be applied across the settings and not in isolation. For a detailed description of each of these settings, see "Thread Pool Attributes" in Chapter 17, "Tuning .NET Application Performance."

  • Set maxconnection to 12 * # of CPUs. This setting controls the maximum number of outgoing HTTP connections allowed by the client, which in this case is ASP.NET. The recommendation is to set this to 12 times the number of CPUs.
  • Set maxIoThreads to 100. This setting controls the maximum number of I/0 threads in the common language runtime (CLR) thread pool. This number is then automatically multiplied by the number of available CPUs. The recommendation is to set this to 100.
  • Set maxWorkerThreads to 100. This setting controls the maximum number of worker threads in the CLR thread pool. This number is then automatically multiplied by the number of available CPUs. The recommendation is to set this to 100.
  • Set minFreeThreads to 88 * # of CPUs. The worker process uses this setting to queue up all the incoming requests if the number of available threads in the thread pool falls below the value for this setting. This setting effectively limits the number of concurrently executing requests to maxWorkerThreadsminFreeThreads. The recommendation is to set this to 88 times the number of CPUs. This limits the number of concurrent requests to 12 (assuming maxWorkerThreads is 100).
  • Set minLocalRequestFreeThreads to 76 * # of CPUs. This worker process uses this setting to queue up requests from localhost (where a Web application calls a Web service on the same server) if the number of available threads in the thread pool falls below this number. This setting is similar to minFreeThreads, but it only applies to requests that use localhost. The recommendation is to set this to 76 times the number of CPUs.
Note   The above recommendations are starting points rather than strict rules. You should perform appropriate testing to determine the correct settings for your environment.

If the formula has worked, you should see improved throughput and less idle CPU time:

  • CPU utilization should go up.
  • Throughput should increase (ASP.NET Applications/Requests/Sec should go up),
  • Requests in the application queue (ASP.NET Applications/Requests in Application Queue) should go down.

If this does not improve your performance, you may have a CPU-bound situation. If this is the case, by adding more threads you increase thread context switching. For more information, see "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance."

More Information

For more information, see Microsoft Knowledge Base article 821268, "PRB: Contention, Poor Performance, and Deadlocks When You Make Web Service Requests from ASP.NET Applications," at http://support.microsoft.com/default.aspx?scid=kb;en-us;821268.

Consider minIoThreads and minWorkerThreads for Intermittent Burst Load

If you have burst load scenarios that are intermittent and short (0 to 10 minutes), then the thread pool may not have enough time to reach the optimal level of threads. The use of minIoThreads and minWorkerThreads allows you to configure a minimum number of worker and I/O threads for load conditions.

At the time of this writing, you need a supported fix to configure the settings. For more information, see the following Microsoft Knowledge Base articles:

  • 810259, "FIX: SetMinThreads and GetMinThreads API Added to Common Language Runtime ThreadPool Class," at http://support.microsoft.com/default.aspx?scid=kb;en-us;810259.
  • 827419, "PRB: Sudden Requirement for a Larger Number of Threads from the ThreadPool Class May Result in Slow Computer Response Time," at http://support.microsoft.com/default.aspx?scid=kb;en-us;827419.

More Information

For more information about threading and Web services, see:

  • "ASP.NET Tuning" in Chapter 17, "Tuning .NET Application Performance"
  • Microsoft Knowledge Base article 821268, "PRB: Contention, Poor Performance, and Deadlocks When You Make Web Service Requests from ASP.NET Applications," at http://support.microsoft.com/default.aspx?scid=kb;en-us;821268.

One-Way (Fire-and-Forget) Communication

Consider using the OneWay attribute if you do not require a response. Using the OneWay property of SoapDocumentMethod and SoapRpcMethod in the System.Web.Services.Protocols namespace frees the client immediately instead of forcing it to wait for a response.

For a method to support fire-and-forget invocation, you must decorate it with the OneWay attribute, as shown in the following code snippet.

[SoapDocumentMethod(OneWay=true)]
[WebMethod(Description="Returns control immediately")]
public void SomeMethod() 
{…}

This is useful if the client needs to send a message, but does not expect anything as return values or output parameters. Methods marked as OneWay cannot have output parameters or return values.

Asynchronous Web Methods

You can call a Web service asynchronously regardless of whether or not the Web service has been implemented synchronously or asynchronously. Similarly, you can implement a synchronous or asynchronous Web service, but allow either style of caller. Client-side and server-side asynchronous processing is generally performed to free up the current worker thread to perform additional work in parallel.

The asynchronous implementation of a Web method frees up the worker thread to handle other parallel tasks that can be performed by the Web method. This ensures optimal utilization of the thread pool, resulting in throughput gains.

For normal synchronous operations, the Web services asmx handler uses reflection on the assembly to find out which methods have the WebMethod attribute associated with them. The handler simply calls the appropriate method based on the value of the SOAP-Action HTTP header.

However, the Web services asmx handler treats asynchronous Web methods differently. It looks for methods that adhere to the following rules:

  • Methods adhere to the asynchronous design pattern:
    • There are BeginXXX and EndXXX methods for the actual XXX method that you need to expose.
    • The BeginXXX method returns an IAsyncResult interface, takes whatever arguments the Web method needs, and also takes two additional parameters of type AsyncCallback and System.Object, respectively.
    • The EndXXX method takes an IAsyncResult as a parameter and returns the return type of your Web method.
  • Both methods are decorated with the WebMethod attribute.

The Web services asmx handler then exposes the method, as shown in the following code snippet.

[WebMethod]
IAsyncResult  BeginMyProc(…)

[WebMethod]
EndMyProc(…)

//the WSDL will show the method as
MyProc(…)

The Web services asmx handler processes incoming requests for asynchronous methods as follows:

  • Call the BeginXXX method.
  • Pass the reference to an internal callback function as a parameter to the BeginXXX method, along with the other in parameters. This frees up the worker thread processing the request, allowing it to handle other incoming requests. The asmx handler holds on to the HttpContext of the request until processing of the request is complete and a response has been sent to the client.
  • Once the callback is called, call the EndXXX function to complete the processing of the method call and return the response as a SOAP response.
  • Release the HttpContext for the request.

Consider the following guidelines for asynchronous Web methods:

  • Use asynchronous Web methods for I/O operations.
  • Do not use asynchronous Web methods when you depend on worker threads.

Use Asynchronous Web Methods for I/O Operations

Consider using asynchronous Web methods if you perform I/O-bound operations such as:

  • Accessing streams
  • File I/O operations
  • Calling another Web service

The .NET Framework provides the necessary infrastructure to handle these operations asynchronously, and you can return an IAsyncResult interface from these types of operations. The .NET Framework exposes asynchronous methods for I/O-bound operations using the asynchronous design pattern. The libraries that use this pattern have BeginXXX and EndXXX methods.

The following code snippet shows the implementation of an asynchronous Web method calling another Web service.

// The client W/S 
public class AsynchWSToWS
{
  WebServ asyncWs = null;
  public AsynchWSToWS(){
    asyncWs = new WebServ();
  }
  
  [System.Web.Services.WebMethod]
  public IAsyncResult BeginSlowProcedure(int milliseconds,AsyncCallback cb, 
                                         object s){
    // make call to other web service and return the IAsyncResult
    return asyncWs.BeginLengthyCall(milliseconds,cb,s);
  }
  
  [System.Web.Services.WebMethod ]
  public string EndSlowProcedure(IAsyncResult call) {
    return asyncWs.EndLengthyCall(call);
  }
}

// The server W/S
public class WebServ
{
  [WebMethod]
  public string LengthyCall(int milliseconds){
    Thread.Sleep(milliseconds);
    return "Hello World";
  }
}

Asynchronous implementation helps when you want to free up the worker thread instead of waiting on the results to return from a potentially long-running task. For this reason, you should avoid asynchronous implementation whenever your work is CPU bound because you do not have idle CPU to service more threads. In this case, an asynchronous implementation results in increased utilization and thread switching on an already busy processor. This is likely to hurt performance and overall throughput of the processor.

Note   You should not use asynchronous Web methods when accessing a database. ADO.NET does not provide asynchronous implementation for handling database calls. Wrapping the operation in a delegate is not an option either because you still block a worker thread.
You should only consider using an asynchronous Web method if you are wrapping an asynchronous operation that hands back an IAsyncResult reference.

Do Not Use Asynchronous Web Methods When You Depend on Worker Threads

You should not implement Web methods when your asynchronous implementation depends upon callbacks or delegates because they use worker threads internally. Although the delegate frees the worker thread processing the request, it uses another worker thread from the process thread pool to execute the method. This is a thread that can be used for processing other incoming requests to the Web service. The result is that you consume a worker thread for the delegate-based operation and you increase context switching.

Alternatively, you can use synchronous Web methods and decrease the minFreeThreads setting so that the worker threads can take requests and execute them directly.

In this scenario, you could block the original worker thread by implementing the Web method to run synchronously. An example of the delegate-based implementation is shown in the following code snippet.

// delegate
public delegate string LengthyProcedureAsyncStub(int milliseconds);

//actual method which is exposed as a web service
[WebMethod]
public string LengthyCall(int milliseconds) { 
  System.Threading.Thread.Sleep(milliseconds);
  return "Hello World";
}

[WebMethod]
public IAsyncResult BeginLengthyCall(int milliseconds,AsyncCallback cb, 
object s) {
  LengthyProcedureAsyncStub stub = new LengthyProcedureAsyncStub(LengthyCall);
 //using delegate for asynchronous implementation   
  return stub.BeginInvoke(milliseconds, cb, null); }

[System.Web.Services.WebMethod]
public string EndLengthyCall(IAsyncResult call) {
  return ms.asyncStub.EndInvoke(call);
}

Asynchronous Invocation

Web services clients can call a Web service either synchronously or asynchronously, independently of the way the Web service is implemented.

For server applications, using asynchronous calls to a remote Web service is a good approach if the Web service client can either free the worker thread to handle other incoming requests or perform additional parallel work before blocking for the results. Generally, Windows Forms client applications call Web services asynchronously to avoid blocking the user interface.

Note   The HTTP protocol allows at most two simultaneous outbound calls from one client to one Web service.

The WSDL-generated proxy contains support for both types of invocation. The proxy supports the asynchronous call by exposing BeginXXX and EndXXX methods.

The following guidelines help you decide whether or not calling a Web service asynchronously is appropriate:

  • Consider calling Web services asynchronously when you have additional parallel work.
  • Use asynchronous invocation to call multiple unrelated Web services.
  • Call Web services asynchronously for UI responsiveness.

Consider Calling Web Services Asynchronously When You Have Additional Parallel Work

Asynchronous invocation is the most useful when the client has additional work that it can perform while the Web method executes. Asynchronous calls to Web services result in performance and throughput gains because you free the executing worker thread to do parallel work before it is blocked by the Web services call and waits for the results. This lets you concurrently process any work that is not dependent on the results of the Web services call. The following code snippet shows the approach.

private void Page_Load(object sender, System.EventArgs e) 
{
  serv = new localhost.WebService1();
  IAsyncResult result = serv.BeginLengthyProcedure(5000,null,null);
  // perform some additional processing here before blocking 

  // wait for the asynchronous operation to complete
  result.AsyncWaitHandle.WaitOne();
  string retStr = serv.EndLengthyProcedure(result);
}

Use Asynchronous Invocation to Call Multiple Unrelated Web Services

Consider asynchronous invocation if you need to call multiple Web services that do not depend on each other's results. Asynchronous invocation lets you call the services concurrently. This tends to reduce response time and improve throughput. The following code snippet shows the approach.

private void Page_Load(object sender, System.EventArgs e){
  serv1 = new WebService1();
  serv2 = new WebService2();

  IAsyncResult result1 = serv1.BeginLengthyProcedure(1000,null,null);
  IAsyncResult result2 = serv2.BeginSlowProcedure(1000,null,null);

  //wait for the asynchronous operation to complete
  WaitHandle[] waitHandles = new WaitHandle[2];

  waitHandles[0] = result1.AsyncWaitHandle;
  waitHandles[1] = result2.AsyncWaitHandle;

  WaitHandle.WaitAll(waitHandles);  //depending upon the scenario you can
                                    //choose between WaitAny and WaitAll
  string retStr1 = serv1.EndLengthyProcedure(result1);
  string retStr2 = serv2.EndSlowProcedure(result2);
}

Call Web Services Asynchronously for UI Responsiveness

By calling a Web service asynchronously from a Windows Forms application, you free the main user interface thread. You can also consider displaying a progress bar while the call progresses. This helps improve perceived performance.

However, you need to perform some additional work to resynchronize the results with the user interface thread because the Web service call is handled by a separate thread. You need to call the Invoke method for the control on which you need to display the results.

More Information

For more information, see the MSDN article, "At Your Service: Performance Considerations for Making Web Service Calls from ASPX Pages," at http://msdn.microsoft.com/library/en-us/dnservice/html/service07222003.asp.

Timeouts

It is very common for an ASP.NET application to call a Web service. If your application's Web page times out before the call to the Web service times out, this causes an unmanaged resource leak and a ThreadAbortException. This is because I/O completion threads and sockets are used to service the calls. As a result of the exception, the socket connection to the Web service is not closed and cannot be reused by other outbound requests to the Web service. The I/O thread continues to process the Web service response.

To avoid these issues, set timeouts appropriately as follows:

  • Set your proxy timeout appropriately.
  • Set your ASP.NET timeout greater than your Web service timeout.
  • Abort connections for ASP.NET pages that timeout before a Web services call completes.
  • Consider the responseDeadlockInterval attribute.

Set Your Proxy Timeout Appropriately

When you call a Web service synchronously, set the Timeout property of the Web service proxy. The default value is 100 seconds. You can programmatically set the value before making the call, as shown in the following code snippet.

MyWebServ obj = new MyWebServ();
obj.Timeout = 15000; // in milliseconds

For ASP.NET applications, the Timeout property value should always be less than the executionTimeout attribute of the httpRuntime element in Machine.config. The default value of executionTimeout is 90 seconds. This property determines the time ASP.NET continues to process the request before it returns a timed out error. The value of executionTimeout should be the proxy Timeout, plus processing time for the page, plus buffer time for queues.

  • Consider reducing the Proxy Timeout value from its default of 100 seconds if you do not expect clients to wait for such a long time. You should do this even under high load conditions when the outbound requests to the Web service could be queued on the Web server. As a second step, reduce the executionTimeout also.
  • You might need to increase the value if you expect the synchronous call to take more time than the default value before completing the operation. If you send or receive large files, you may need to increase the attribute value. As a second step, increase the executionTimeout attribute to an appropriate value.

Set Your ASP.NET Timeout Greater Than Your Web Service Timeout

The Web service timeout needs to be handled differently, depending upon whether you call the Web service synchronously or asynchronously. In either case, you should ensure that the timeouts are set to a value less than the executionTimeout attribute of the httpRuntime element in Machine.config. The following approaches describe the options for setting the timeouts appropriately:

  • Synchronous calls to a Web service. Set the proxy Timeout to an appropriate value, as shown in the following code snippet.
    MyWebServ obj = new MyWebServ();
    obj.Timeout = 15000; // in milliseconds
    

    You can also set the value in the proxy class generated by the WSDL for the Web service. You can set it in the class constructor, as shown in the following code snippet.

    public MyWebServ() {
        this.Url = "http://someMachine/mywebserv/myserv.asmx";
        this.Timeout = 10000;  //10 seconds
    }
    

    Or you can set it at the method level for a long-running call.

    public string LengthyProc(int sleepTime) {
      this.Timeout = 10000; //10 seconds
      object[] results = this.Invoke("LengthyProc", new object[] {sleepTime});
      return ((string)(results[0]));  
    }
    
  • Asynchronous calls to a Web service. In this case, you should decide on the number of seconds you can wait for the Web service call to return the results. When using a WaitHandle, you can pass the number of milliseconds the executing thread is blocked on the WaitHandle before it aborts the request to the Web service. This is shown in the following code snippet.
    MyWebServ obj = new MyWebServ();
    IAsyncResult ar = obj.BeginFunCall(5,5,null,null);
    
    // wait for not more than 2 seconds
    ar.AsyncWaitHandle.WaitOne(2000,false);
    if (!ar.IsCompleted) //if the request is not completed  { 
      WebClientAsyncResult wcar = (WebClientAsyncResult)ar;
      wcar.Abort();//abort the call to web service 
    }
    else
    { //continue processing the results from web service }
    

Abort Connections for ASP.NET Pages That Timeout Before a Web Services Call Completes

After you make the configuration changes described in the previous section, if your Web pages time out while Web services calls are in progress, you need to ensure that you abort the Web services calls. This ensures that the underlying connections for the Web services calls are destroyed.

To abort a Web services call, you need a reference to the WebRequest object used to make the Web services call. You can obtain this by overriding the GetWebRequest method in your proxy class and assigning it to a private member of the class before returning the WebRequest. This approach is shown in the following code snippet.

private WebRequest _request;
protected override WebRequest GetWebRequest(Uri uri){
  _request = base.GetWebRequest(uri);
  return _request;
}

Then, in the method that invokes the Web service, you should implement a finally block that aborts the request if a ThreadAbortException is thrown.

[System.Web.Services.Protocols.SoapDocumentMethodAttribute(…)]
public string GoToSleep(int sleepTime) {
  bool timeout = true;
  try {
    object[] results = this.Invoke("GoToSleep", new object[] {sleepTime});
    timeout = false;
    return ((string)(results[0]));  
  } 
  finally {
    if(timeout && _request!=null)
      _request.Abort();
  }
}
Note   Modifying generated proxy code is not recommended because the changes are lost as soon as the proxy is regenerated. Instead, derive from the proxy class and implement new functionality in the subclass whenever possible.

Consider the responseDeadlockInterval Attribute

When you make Web services calls from an ASP.NET application, if you are increasing the value of both the proxy timeout and the executionTimeout to greater than 180 seconds, consider changing the responseDeadlockInterval attribute for the processModel element in the Machine.config file. The default value of this attribute is 180 seconds. If there is no response for an executing request for 180 seconds, the ASP.NET worker process will recycle.

You must reconsider your design if it warrants changing the attributes to a higher value.

WebMethods

You add the WebMethod attribute to those public methods in your Web services .asmx file that you want to be exposed to remote clients. Consider the following Web method guidelines:

  • Prefer primitive parameter types. When you define your Web method, try to use primitive types for the parameters. Using primitive types means that you benefit from reduced serialization, in addition to automatic validation by the .NET Framework.
  • Consider buffering. By default, the BufferResponse configuration setting is set to true, to ensure that the response is completely buffered before returning to the client. This default setting is good for small amounts of data. For large amounts of data, consider disabling buffering, as shown in the following code snippet.
    [WebMethod(BufferResponse=false)]
    public string GetTextFile() {
      // return large amount of data
    }
    

    To determine whether or not to enable or disable buffering for your application, measure performance with and without buffering.

  • Consider caching responses. For applications that deal with relatively static data, consider caching the responses to avoid accessing the database for every client request. You can use the CacheDuration attribute to specify the number of seconds the response should be cached in server memory, as shown in the following code snippet.
    [WebMethod(CacheDuration=60)]
    public string GetSomeDetails() {
      // return large amount of data
    }
    

    Note that because caching consumes server memory, it might not be appropriate if your Web method returns large amounts of data or data that frequently changes

  • Enable session state only for Web methods that need it. Session state is disabled by default. If your Web service needs to maintain state, then you can set the EnableSession attribute to true for a specific Web method, as shown in the following code snippet.
    [WebMethod(EnableSession=true)]
    public string GetSomeDetails() {
      // return large amount of data
    }
    

    Note that clients must also maintain an HTTP cookie to identify the state between successive calls to the Web method.

    For more information, see "WebMethodAttribute.EnableSession Property" on MSDN at http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemWebServicesWebMethodAttributeClassEnableSessionTopic.asp.

Serialization

The amount of serialization that is required for your Web method requests and responses is a significant factor for overall Web services performance. Serialization overhead affects network congestion, memory consumption, and processor utilization. To help keep the serialization overhead to a minimum:

  • Reduce serialization with XmlIgnore.
  • Reduce round trips.
  • Consider XML compression.

Reduce Serialization with XmlIgnore

To limit which fields of an object are serialized when you pass the object to or from a Web method and to reduce the amount of data sent over the wire, use the XmlIgnore attribute as shown in the following code snippet. The XmlSerializer class ignores any field annotated with this attribute.

Note   Unlike the formatters derived from the IFormatter interface, XmlSerializer serializes only public members.
// This is the class that will be serialized. 
public class MyClass
{
   // The str1 value will be serialized.
   public string str1;

   /* This field will be ignored when serialized--
      unless it's overridden. */
   [XmlIgnoreAttribute]
   public string str2;
} 

Reduce Round Trips

Reducing round trips to a Web service reduces the number of times that messages need to cross serialization boundaries. This helps reduce the overall serialization cost incurred. Design options that help to reduce round trips include the following:

  • Use message-based interaction with a message-based programming model, rather than an RPC style that requires multiple object interactions to complete a single logical unit of work.
  • In some cases, split a large payload into multiple calls to the Web service. Consider making the calls in parallel using asynchronous invocation instead of in series. This does not technically reduce the total number of round trips, but in essence the client waits for only a single round trip.

Consider XML Compression

Compressing the XML payload sent over the wire helps reduce the network traffic significantly. You can implement XML compression by using one of the following techniques:

  • Use SOAP extensions on the server and client for the compression and decompression of requests and responses.
  • Use a custom HTTP module on the server and override the proxy for the Web service on the client.
  • Use HTTP compression features available in IIS 5.0 and later versions for compressing the response from the Web services. Note that you need a decompression mechanism on the client.

More Information

For more information about serialization, see:

  • "XmlSerializer Architecture" in the November 2001 edition of MSDN magazine, at http://msdn.microsoft.com/msdnmag/issues/01/11/webserv/default.aspx.
  • Microsoft Knowledge Base article 314150, "INFO: Roadmap for XML Serialization in the .NET Framework," at http://support.microsoft.com/default.aspx?scid=kb;en-us;314150.
  • Microsoft Knowledge Base article 313651, "INFO: Roadmap for XML in the .NET Framework," at http://support.microsoft.com/default.aspx?scid=kb;en-us;313651.
  • Microsoft Knowledge Base article 317463, "HOW TO: Validate XML Fragments Against an XML Schema in Visual Basic .NET," at http://support.microsoft.com/default.aspx?scid=kb;en-us;317463.

Caching

Caching is a great way to improve Web services performance. By reducing the average request time and easing server load, caching also helps scalability. You can cache frequently used data applicable to all users, or you can cache SOAP response output. You can cache application data by using ASP.NET caching features. You can cache SOAP output by using either the ASP.NET output cache or by employing perimeter caching. When designing a caching strategy for your Web services, consider the following guidelines:

  • Consider output caching for less volatile data.
  • Consider providing cache-related information to clients.
  • Consider perimeter caching.

Consider Output Caching for Less Volatile Data

If portions of your output are static or nearly static, use ASP.NET output caching. To use ASP.NET output caching with Web services, configure the CacheDuration property of the WebMethod attribute. The following code snippet shows the cache duration set to 30 seconds.

[WebMethod(CacheDuration=30)]
public string SomeMethod() {  ... . }

For more information, see Microsoft Knowledge Base article 318299, "HOW TO: Perform Output Caching with Web Services in Visual C# .NET," at http://support.microsoft.com/default.aspx?scid=kb;en-us;318299.

Consider Providing Cache-Related Information to Clients

Web services clients can implement custom caching solutions to cache the response from Web services. If you intend that clients of your Web services should cache responses, consider providing cache expiration–related information to the clients so that they send new requests to the Web service only after their cached data has expired. You can add an additional field in the Web service response that specifies the cache expiration time.

Consider Perimeter Caching

If the output from your Web services changes infrequently, use hardware or software to cache the response at the perimeter network. For example, consider ISA firewall-based caching. Perimeter caching means that a response is returned before the request even reaches the Web server, which reduces the number of requests that need to be serviced.

For more information about ISA caching, see the white paper, "Scale-Out Caching with ISA," at http://www.microsoft.com/isaserver/techinfo/deployment/ScaleOutCachingwithISA.asp.

State Management

Web services state can be specific to a user or to an application. Web services use ASP.NET session state to manage per-user data and application state to manage application-wide data. You access session state from a Web service in the same way you do from an ASP.NET application — by using the Session object or System.Web.HttpContext.Current. You access application state using the Application object, and the System.Web.HttpApplicationState class provides the functionality.

Maintaining session state has an impact on concurrency. If you keep data in session state, Web services calls made by one client are serialized by the ASP.NET runtime. Two concurrent requests from the same client are queued up on the same thread in the Web service — the second request waits until the first request is processed. If you do not use session data in a Web method, you should disable sessions for that method.

Maintaining state also affects scalability. First, keeping per-client state in-process or in a state service consumes memory and limits the number of clients your Web service can serve. Second, maintaining in-process state limits your options because in-process state is not shared by servers in a Web farm.

If your Web service needs to maintain state between client requests, you need to choose a design strategy that offers optimum performance and at the same time does not adversely affect the ability of your Web service to scale. The following guidelines help you to ensure efficient state management:

  • Use session state only where it is needed.
  • Avoid server affinity.

Use Session State Only Where It Is Needed

To maintain state between requests, you can use session state in your Web services by setting the EnableSession property of the WebMethod attribute to true, as shown in the following code snippet. By default, session state is disabled.

[WebMethod(EnableSession=true)]
YourWebMethod() { ... }

Since you can enable session state at the Web method level, apply this attribute only to those Web methods that need it.

Note   Enabling session state pins each session to one thread (to protect session data). Concurrent calls from the same session are serialized once they reach the server, so they have to wait for each other, regardless of the number of CPUs.

Avoid Server Affinity

If you do use session state, in-process session state offers the best performance, but it prevents you from scaling out your solution and operating your Web services in a Web farm. If you need to scale out your Web services, use a remote session state store that can be accessed by all Web servers in the farm.

Bulk Data Transfer

You have the following basic options for passing large amounts of data including binary data to and from Web methods:

  • Using a byte array Web method parameter.
  • Returning a URL from the Web service.
  • Using streaming.

Using a Byte Array Web Method Parameter

With this approach, you pass a byte array as a method parameter. An additional parameter typically specifies the transfer data size. This is the easiest approach, and it supports cross-platform interoperability. However, it has the following issues:

  • If a failure occurs midway through the transfer, you need to start again from the beginning.
  • If the client passes an arbitrary amount of data that exceeds your design limitations, you run the risk of running out of memory or exceeding your CPU thresholds on the server. Your Web service is also susceptible to denial of service attacks.

Note that you can limit the maximum SOAP message size for a Web service by using the maxRequestLength setting in the <httpRuntime> section of the Web.config file. In the following example, the limit is set to 8 KB.

<configuration>
  <system.web>
    <httpRuntime maxRequestLength="8096"
        useFullyQualifiedRedirectUrl="true"
        executionTimeout="45"/>
  </system.web>
</configuration>

Base 64 Encoding

For binary data transfer, you can use Base 64 to encode the data. Base 64 encoding is suitable for cross-platform interoperability if your Web service has a heterogeneous client audience.

This approach is more suitable if your data isn't large and the encoding/decoding overhead and size of the payload are not of significant concern. For large-sized data, you can implement a WSE filter and various compression tools to compress the message before sending it over the wire.

For more information about Base 64 encoding and decoding, see:

  • Microsoft Knowledge Base article 191239, "Sample Base 64 Encoding and Decoding," at http://support.microsoft.com/default.aspx?scid=kb;EN-US;q191239.
  • MSDN article, "Web Methods Make It Easy to Publish Your App's Interface over the Internet" by Paula Paul, at http://msdn.microsoft.com/msdnmag/issues/02/03/WebMethods/.

Returning a URL from the Web Service

Returning a URL from the Web service is the preferred option for large file downloads. With this approach, you return a URL to the client, and the client then uses HTTP to download the file.

You can consider using the Background Intelligent Transfer Service (BITS), a Windows service, for this purpose. For more information about BITS, see the MSDN article, "Write Auto-Updating Apps with .NET and the Background Intelligent Transfer Service API" by Jason Clark, at http://msdn.microsoft.com/msdnmag/issues/03/02/BITS/.

If you need to use BITS for your .NET application for uploading and downloading of files, you can use the Updater Application Block. For more information, see the MSDN article "Updater Application Block for .NET" at http://msdn.microsoft.com/library/en-us/dnbda/html/updater.asp.

Although returning a URL works for downloads, it is of limited use for uploads. For uploads, you must call the Web service from an HTTP server on the Internet, or the Web service will be unable to resolve the supplied URL.

Using Streaming

If you need to transfer large amounts of data (several megabytes, for example) from a Web method, consider using streaming. If you use streaming, you do not need to buffer all of the data in memory on the client or server. In addition, streaming allows you to send progress updates from a long-running Web service operation to a client that is blocked waiting for the operation to return.

In most cases, data is buffered on both the server and client. On the server side, serialization begins after the Web method has returned, which means that all of the data is usually buffered in the return value object. On the client side, the deserialization of the entire response occurs before the returned object is handed back to the client application, again buffering data in memory.

You can stream data from a Web service in two ways:

  • Implementing IList
  • Implementing IXmlSerializable

Implementing IList

The XmlSerializer has special support for types that implement IList whereby it obtains and serializes one list item at a time. To benefit from this streaming behavior, you can implement IList on your return type, and stream out the data one list item at a time without first buffering it.

While this approach provides streaming and the ability to send progress updates, it forces you to work with types that implement IList and to break data down into list items.

Note   The serializer is still responsible for serializing and deserializing each individual list item.

Another disadvantage is that the progress is reported from the returned type. This means that the instance of your type that implements IList used on the client side must be able to communicate progress to the client application while the serializer calls IList.Add. While this is achievable, it is not ideal.

The following server and client code samples show how to implement this approach. The server code is shown below.

// Server code
// If the server wants to return progress information, the IList 
indexer would 
// have to know the current progress and return a value indicating that 
progress 
// when it is called.
public class MyList : IList
{
  int progress=0;
  public object this[int index]
  {
    get
    {
      // Pretend to do something that takes .5 seconds
      System.Threading.Thread.Sleep(500);
      if (progress <= 90)
        return progress+=10;
      else
        return "Some data goes here";
    }
    set
    {
      // TODO:  Add setter implementation
    }
  }
   ... other members omitted
}

[WebMethod]
public MyList DoLongOperation()
{
  // To prevent ASP.NET from buffering the response, the WebMethod must set 
  // the BufferOutput property to false
  HttpContext.Current.Response.BufferOutput=false;

  return new MyList();
}

By using the above code, the Web service response is streamed out and consists of 10 progress data points (10 to 100), followed by a string of data.

The corresponding method on the client proxy class must return a type that implements IList. This type must know how to stream items as they are added to the list, and, if required, how to report progress information as it is retrieved from the stream. The relevant MyList member on the client is the Add method:

Note   With .NET Framework 1.1 you must manually edit the generated proxy code because Wsdl.exe and the Add Web Reference option in Visual Studio.NET generate a proxy class with a method that returns an object array. You need to modify this to return a type that implements IList.

The client code is shown below.

// Client code
public class MyList : IList
{
  public int Add(object value)
  {
    if (progress < 100)
    {
      progress = Convert.ToInt32(value);
      Console.WriteLine("Progress is {0}",progress);
    }
    else
    {
      Console.WriteLine("Received data: {0}",value);
    }
    return 0;
  }
}

The client's proxy class then contains a method that returns MyList as 
shown below:

public ProgressTestClient.MyList DoLongOperation() 
{ 
  ... code omitted 
}

Implementing IXmlSerializable

Another possible approach is to create a type that implements IXmlSerializable, and return an instance of this type from your Web method and the client proxy's method. This gives you full control of the streaming process. On the server side, the IXmlSerializable type uses the WriteXml method to stream data out:

This solution is slightly cleaner than the previous approach because it removes the arbitrary restriction that the returned type must implement IList. However, the programming model is still a bit awkward because progress must be reported from the returned type. Once again, in.NET Framework 1.1, you must also modify the generated proxy code to set the correct method return type.

The following code sample shows how to implement this approach.

public class ProgressTest : IXmlSerializable
{
  public void WriteXml(System.Xml.XmlWriter writer)
  {
    int progress=0;
    while(progress <= 100)
    {
      writer.WriteElementString("Progress",
                                "http://progresstest.com", 
progress.ToString());
      writer.Flush();
      progress += 10;
      // Pretend to do something that takes 0.5 second
      System.Threading.Thread.Sleep(500);
    }
    writer.WriteElementString("TheData",
                               "http://progresstest.com","Some data 
goes here");
  }
}

The Web method must disable response buffering and return an instance of the ProgressTest type, as shown below.

[WebMethod]
public ProgressTest DoLongOperation2()
{
  HttpContext.Current.Response.BufferOutput=false;
  return new ProgressTest();
}

More Information

For more information about bulk data transfer, see:

  • Microsoft Knowledge Base article 318425, "HOW TO: Send and Receive Binary Documents by Using an ASP.NET Web Service and Visual C# .NET," at http://support.microsoft.com/default.aspx?scid=kb;en-us;318425.
  • MSDN article, "Large Data Strategies," at http://msdn.microsoft.com/library/en-us/dnservice/html/service11072001.asp.
  • MSDN article, "XML,SOAP, and Binary Data," at http://msdn.microsoft.com/library/en-us/dnwebsrv/html/infoset_whitepaper.asp.

Attachments

You have various options when handling attachments with Web services. When choosing your option, consider the following:

  • WS-Attachments. WSE versions 1.0 and 2.0 provide support for WS-Attachments, which uses Direct Internet Message Encapsulation (DIME) as an encoding format. Although DIME is a supported part of WSE, Microsoft is not investing in this approach long term. DIME is limited because the attachments are outside the SOAP envelope.
  • Base 64 encoding. Use Base 64 encoding. At this time, you should use Base 64 encoding rather than WS-Attachments when you have advanced Web service requirements, such as security. Base 64 encoding results in a larger message payload (up to two times that of WS-Attachments). You can implement a WSE filter to compress the message with tools such as GZIP before sending it over the network for large amounts of binary data. If you cannot afford the message size that Base 64 introduces and you can rely on the transport for security (for example, you rely on SSL or IPSec), then consider the WSE WS-Attachments implementation. Securing the message is preferable to securing the transport so that messages can be routed securely, whereas transport only addresses point-to-point communication.
  • SOAP Message Transmission Optimization Mechanism (MTOM). MTOM, which is a derivative work of SOAP messages with attachments (SwA), is likely to be the future interop technology. MTOM is being standardized by the World Wide Web Consortium (W3C) and is much more composition-friendly than SwA.

SOAP Messages with Attachments (SwA)

SwA (also known as WS-I Attachments Profile 1.0) is not supported. This is because you cannot model a MIME message as an XML Infoset, which introduces a non-SOAP processing model and makes it difficult to compose SwA with the rest of the WS-* protocols, including WS-Security. The W3C MTOM work was specifically chartered to fix this problem with SwA, and Microsoft is planning to support MTOM in WSE 3.0.

COM Interop

Calling single-threaded apartment (STA) objects from Web services is neither tested nor supported. The ASPCOMPAT attribute that you would normally use in ASP.NET pages when calling Apartment threaded objects is not supported in Web services.

More Information

For more information, see Microsoft Knowledge Base article 303375, "INFO: XML Web Services and Apartment Objects," at http://support.microsoft.com/default.aspx?scid=kb;en-us;303375.

Measuring and Analyzing Web Services Performance

The quickest way to measure the performance of a Web services call is to use the Microsoft Win32® QueryPerformanceCounter API, which can be used with QueryPerformanceFrequency to determine the precise number of seconds that the call consumed.

Note   You can also use the ASP.NET/Request Execution Time performance counter on the server hosting the Web service.

More Information

  • For more information, see "How To: Time Managed Code Using QueryPerformanceCounter and QueryPerformanceFrequency" in the "How To" section of this guide.
  • For more information about measuring Web services performance, see "Web Services" in Chapter 15, "Measuring .NET Application Performance"

Web Service Enhancements

Web Service Enhancements (WSE) is an implementation provided to support emerging Web services standards. This section briefly explains WSE, its role in Web services, and sources of additional information.

WSE 2.0 provides a set of classes implemented in the Microsoft.Web.Services.dll to support the following Web services standards:

  • WS-Security
  • WS-SecureConversation
  • WS-Trust
  • WS-Policy
  • WS-Addressing
  • WS-Referrals
  • WS-Attachments

Figure 10.2 shows how WSE extends the .NET Framework to provide this functionality.

Figure 10.2: WSE runtime

The WSE runtime consists of a pipeline of filters that intercepts inbound SOAP requests and outgoing SOAP response messages. WSE provides a programming model to manage the SOAP headers and messages using the SoapContext class. This gives you the ability to implement various specifications that it supports.

More Information

For more information about WSE, see the MSDN article, "Web Services Enhancements (WSE)," at http://msdn.microsoft.com/webservices/building/wse/default.aspx.

Summary

Web services are the recommended communication mechanism for distributed .NET applications. It is likely that large portions of your application are depending on them or will depend on them. For this reason, it is essential that you spend time optimizing Web services performance and that you design and implement your Web services with knowledge of the important factors that affect their performance and scalability.

This chapter has presented the primary Web services performance and scalability issues that you must address. It has also provided a series of implementation techniques that enable you to tackle these issues and build highly efficient Web services solutions.

Additional Resources

For more information, see the following resources:

  • For a printable checklist, see "Checklist: Web Services Performance" in the "Checklists" section of this guide.
  • Chapter 4, "Architecture and Design Review of a .NET Application for Performance and Scalability"
  • Chapter 13, "Code Review: .NET Application Performance" See the "Web Services" and "ASP.NET" sections.
  • Chapter 15, "Measuring .NET Application Performance" See the "Web Services" and "ASP.NET" sections.
  • Chapter 16, "Testing .NET Application Performance"
  • Chapter 17, "Tuning .NET Application Performance" See the "Web Services Tuning" and "ASP.NET Tuning" sections.
  • For key recommendations to help you create high-performance .NET Enterprise Services components, see ".NET Enterprise Services Performance" by Richard Turner, on MSDN at http://msdn.microsoft.com/library/en-us/dncomser/html/entsvcperf.asp.
  • For more information on using Microsoft WSE, see Microsoft Knowledge Base article 821377, "Support WebCast: Introduction to Microsoft Web Services Enhancements," at http://support.microsoft.com/default.aspx?scid=kb;en-us;821377.

你可能感兴趣的:(Improving Web Services Performance)