Remoting (.NET 1.1) Performance Guidelines - Design Considerations

From Guidance Share
Jump to navigationJump to search

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


Use .NET Remoting for Communicating between Application Domains in the Same Process

.NET remoting is appropriate for communicating between application domains within the same process, and it supports a wide range of data format and communication channel combinations. However, it does not provide the same degree of interoperability and support for Web Services Description Language (WSDL) and SOAP over HTTP as Web services.


Choose the Right Host

Choosing the right host can improve the ability to scale out the server infrastructure. You can use IIS and ASP.NET, a Windows service, or any Windows executable as a remoting host. The primary advantage of using IIS is that you can use IIS security features, such as authentication and Secure Sockets Layer (SSL). You should only consider a custom process in a trusted server environment. If you do build a custom process, use the TcpChannel for optimum performance. For more information, see "Hosts" later in this chapter.


Choose the Right Activation Model

The way an object is activated has a large impact on affinity, state management, load balancing, performance, and scalability. .NET remoting supports server-activated and client-activated objects.

  • Server-Activated Objects (SAOs). Server-activated objects (SAOs) are usually preferred, because they allow the method call from the client to be serviced by any available server that receives the request. There are two types of SAOs:
    • Singleton. In the singleton case, an object is instantiated at the server and this instance serves requests for all clients. The common language runtime (CLR) provides a thread pool to service incoming client calls. As a result, singletons that maintain shared state require synchronization code to ensure valid state management. Note Singletons do not guarantee that the same instance is always available in memory to serve client requests. They do guarantee that there is only a single instance of that type available, at any one time, to service client requests. For ways to control a singleton's lifetime, see "Lifetime Considerations" later in this chapter.
    • SingleCall. SingleCall objects live only for the life of the method call. They cannot maintain any state because they are destroyed after servicing the method. SingleCall objects are free from synchronization code because they serve requests for only a single client before being destroyed.
  • Client-Activated Objects (CAOs). Client-activated objects (CAOs) have the benefit of making state management in the object easier to maintain. However, CAOs limit the scalability of the system. CAOs are beneficial when you are using a single server approach and opting for stateful components.


Choose the Right Channel

Choosing a channel can impact the speed of message transfer and interoperability. There are two channel types to choose from:

  • TCPChannel. This produces maximum performance for a remoting solution using the BinaryFormatter.
  • HttpChannel. Uses SOAP by default to encode payloads. Use when interoperation is important, or when communicating through a firewall.


Choose the Right Formatter

Both channels, TcpChannel and HttpChannel, can work with both the BinaryFormatter and the SoapFormatter. If speed is your primary concern, use the BinaryFormatter. The following describes both formatters:

  • BinaryFormatter. The HttpChannel defaults to the SoapFormatter. To change this default, change the following configuration information in the configuration files for your server and client application.

Server configuration:

  <?xml version="1.0" encoding="utf-8" ?>
  <configuration>
 <system.runtime.remoting>
   <application>
     <channels>
       <channel ref="http" port="8080">
         <serverProviders>
           <formatter ref="binary"/>
         </serverProviders>
       </channel>
    </channels>
   </application>
 </system.runtime.remoting>
  </configuration>


Client configuration:


  <?xml version="1.0" encoding="utf-8" ?>
  <configuration>
 <system.runtime.remoting>
   <application>
     <channels>
       <channel ref="http" port="8080">
         <clientProviders>
           <formatter ref="binary"/>
         </clientProviders>
       </channel>
    </channels>
   </application>
 </system.runtime.remoting>
  </configuration>
  • SoapFormatter. This is primarily used for interoperability. If you need to use SOAP for message communication, consider using Web services. Note The SOAP format used by the SoapFormatter is not entirely interoperable and should not be considered a truly interoperable approach for cross-platform communication. Interoperability is best achieved with Web services.


Choose Between Synchronous or Asynchronous Communication

Remote calls can be invoked by using synchronous or asynchronous calls. By choosing the most appropriate remote calling method, you can increase the responsiveness of your application. When making a decision, consider the following:

  • Consider asynchronous client calls when you perform parallel tasks. If client responsiveness is an issue for Windows Forms applications, consider invoking remote method calls asynchronously rather than performing synchronous blocking calls. However, if your client is an ASP.NET application, you should be aware that invoking methods asynchronously and then blocking the calling thread to pick up return values can have a detrimental impact on the Web application and can quickly lead to thread starvation. You should use asynchronous calls in the following scenarios:
    • When you have some useful work to do after making the remote call before blocking on the main worker thread processing the requests.
  MyRemoteObj obj = new MyRemoteObj();
  IASyncReult ar= obj.BeginLongCall(…);
  //Do some useful work before blocking here
  Ar.AsynchWaitHandle.WaitOne();
    • When you need to make discrete remote calls that are not dependent on each other.


  MyRemoteObj1 obj1 = new MyRemoteObj1();
  MyRemoteObj2 obj2 = new MyRemoteObj2();
  IASyncReult ar1= obj1.BeginLongCall1(…);
  IASyncReult ar2 = obj2.BeginLongCall2(…);
  WaitHandle[] wh = {ar1.AsynchWaitHandle, ar2.AsynchWaitHandle}
  WaitHandle.WaitAll(wh);


  • Asynchronous remoting limitations. There are no bidirectional channels in remoting. This means that you have to configure a separate channel for callbacks. Having a firewall between the object and client leads to the opening of more ports and configuration issues. Note Events and delegates also require the typeFilterLevel to be set to Full when using .NET Framework 1.1.
  • Consider using the OneWay attribute when you do not need return values. You can mark server object methods with the OneWay attribute. Clients that call methods marked with this attribute do not wait for the method to execute before control is returned. This can improve client performance by not having to wait for method completion at the server. OneWay methods can be called synchronously or asynchronously. In either case, the calling thread does not wait. One-way methods must not have any return values, out parameters, or raise any errors. The client cannot assume that the server successfully completed an operation. The following is an example of the OneWay attribute.
  public class HelloServer : MarshalByRefObject {
  public HelloServer() {}
    [OneWay()]
    public void SayHello() 
    {
    Console.WriteLine(" Hello World");
    }
  }
  • Consider message queuing. Many applications can benefit from asynchronous message queuing. Microsoft Windows Message Queuing (also known as MSMQ) is available to .NET Framework applications. Alternatively, for object-based message queuing, Enterprise Services provides Queued Components, which also uses message queuing. Message queuing allows you to queue requests for processing when server processes are not available for processing. Clients can still receive notifications about the success or failure of an asynchronous message by processing reply messages from the server through a second notification queue. Message queuing is available to managed applications through the types in the System.Messaging namespace, while Queued Components are available through the System.EnterpriseServices namespace.


Minimize Round Trips and Avoid Chatty Interfaces

Design chunky, not chatty, interfaces to minimize round trips. Each time a remote method is invoked, this incurs data marshaling, security checks, and thread switches. All of these impact performance.

Try to reduce the number of calls used to complete a logical unit of work. The following code shows an example of calling a component, by using a chatty interface. Notice how the inefficient (chatty) interface design forces the caller to traverse the remoting boundary three times to perform the single task of saving customer details.


  MyComponent.Firstname = "bob";
  MyComponent.LastName = "smith";
  MyComponent.SaveCustomer();

The following code shows how the same functionality should be implemented to reduce the operations and boundary crossings required to complete the single logical operation.

  MyComponent.SaveCustomer( "bob", "smith");

References

For more information, see Knowledge Base article 322975, "BUG: Passing Large Quantities of Data in .NET Remoting Calls Causes an Unexpected Exception to Occur," at http://support.microsoft.com/default.aspx?scid=kb;en-us;322975.


Avoid Holding State in Memory

State management is a big concern for distributed application design. Poor state management choices can limit the ability to scale out the server farm, provide failover support, and limit performance.

You should use stateless components where possible. Where state must be held in memory, choose data that remains constant, such as country codes and city names.

If you activate components as singletons, they are bound to the server that they are created on. In a server farm, this presents problems because each server has a different object with its own state. This affects your application's ability to scale out and failover. By designing stateless components, you can use singleton-activated components in a farm; this may outperform single-call activated objects because there is no creation and destruction associated with each call.

If you need state information across calls in a farm, make your classes state-aware by implementing methods to read and write the caller's state information. The method call must include a parameter that uniquely identifies the caller. Use this unique identifier to retrieve the associated state before the method performs work and then to write the state afterward. The storage for the state information should be persistent, available to all servers in the farm, and it should support synchronization. A database table is a great option.

The following code demonstrates this pattern in your single-call or singleton-activated classes. This allows you to have state information accessible, but still maintain the ability to place your remote objects in a server farm.


  public class YourSingleCallClass
  {
    public bool DoSomeWork ( int callIdentifier, int customerNo )
    {
      stateStruct = retrivestateInfo( callIdentifier );
      // Do some operations here on state and parameter(s)
      // potentially update stateStruct
      storeStateInfo( stateStruct );
    }
  }