In today's connected world, more and more enterprise applications are distributed and run across multiple servers, connected to remote data sources and Web Service, and accessible over the Internet. Distributed computing is powerful, but it is not without challenges. The network is inherently unreliable, and communication with the remote server is slower than the local interprocess communication. In addition, running a program on multiple computers simultaneously can cause many concurrency and synchronization problems.
Instance-based collaboration and service-based protocols
Distributed computing can be based on two radically different architectural styles, according to business Component Factory (Service component Factory):
- Instance-based collaboration
- Service-based collaboration
Instance-based collaboration extends the object-oriented computing model across network boundaries. Components can instantiate remote object instances, pass references to these remote objects, invoke methods of remote objects, and unassign them. The advantage of this approach is that you can apply the same object-oriented programming model used within your application to distributed components. Most run-time platforms include support for instance-based collaboration so that developers do not have to provide special content when accessing remote objects, or to provide minimal special content relative to accessing local objects. This greatly simplifies the development of distributed solutions, and is often simplified to the extent that objects previously in the same location can be distributed at deployment time without any code changes to the application. Instance-based collaboration also enables users of remote objects to more granular control over the lifetime of remote objects, allowing them to use remote resources more efficiently.
However, instance-based collaboration, while providing ease of use, also leads to increased costs due to complex interaction models and close connections between users and providers. Instance-based interaction requires that a particular instance of a remote object be found over the network, which introduces complex lifetimes and instance management into a communication protocol. As a result, most platforms that support instance-based collaboration do not provide interoperability with other platforms.
Service-based collaboration addresses some of these challenges by exposing only "manager-like" or "coordinator-like" interfaces to potential users. Users can invoke methods on the interface, but they cannot control the lifetime of any remote object. This greatly simplifies interaction and allows for the use of standard protocols that support cross-platform interoperability.
However, service-based collaboration cannot provide local objects and remote objects with the continuity of the object-oriented programming model. This means that you must explicitly track the session state between objects, and you don't have to worry about it when using instance-based collaboration. Similarly, while standards-based protocols improve interoperability, they require applications to translate the application's internal data types into a common format that can be understood by each communication terminal, which may involve other transformation logic.
Close links and far links
Another way to consider a distributed system is to treat each system as a collection of processing nodes that are linked together through a chain. These nodes represent the actual server computers, and the links represent the networks that connect those computers together. The links within the system are divided into two categories: near links or far links.
Near links reside in the same trust zone, within the same enterprise, and they are connected in a reliable manner and do not involve interoperability. The far link includes all other links (including any links across the Internet). If your distributed system spans only near links, it is best to use instance-based collaboration. With instance-based collaboration, you can extend object-oriented development capabilities across computer boundaries, while leveraging the platform's infrastructure to optimize speed, navigate type systems, and handle marshaling details for you. The technical options here will include. NET Remoting and Enterprise Services in the Microsoft. NET Framework.
On the other hand, if your distributed system spans far links, service-based collaboration is often a better choice. If you interact with a service that provides a "coordinator-like" interface, it allows the service to be responsible for implementation without having to let the service user know the implementation details. Service interfaces typically return messages, which is less coupled than those provided by remote procedure calls. The best news is those messages that contain both the header and the body, which allows the recipient to take action spontaneously on the received message. The technical options here include features such as Web Services.
The remainder of this chapter describes patterns that are typically associated with instance-based collaboration and near links. The 6th Chapter "service Mode" will further describe the patterns typically associated with service-based collaboration and remote linking.
The challenges of distributed computing
The core of a distributed architecture is the ability to invoke methods on objects or to communicate with services residing in different processes and possibly even on different computers. It may seem easy, but you have to solve a lot of problems:
- How do I instantiate a remote object?
- If you want to invoke a method on an existing object, how do you get a reference to that object?
- The network protocol transmits only the byte stream without transmitting the object. How can I invoke a method through a byte stream?
- How secure is it? Can everyone invoke a method on a remote object?
- Most networks are inherently unreliable. What happens if I can't access the remote object? What if a remote object receives a method call but cannot send a response because of a network problem?
- Calling a remote object is much slower than calling the local method. Do you want to invoke the remote method asynchronously so that the remote object can continue processing the request while it is processing it locally?
The problems abound. Fortunately, the features in the. NET Framework address Most of these issues, allowing developers to create distributed applications without having to deal with too much detail. These features make remote calls almost transparent to programmers (at least at the syntactic level). However, this simplification can be deceptive because developers must also understand some of the fundamentals of remote communication in order to write powerful and efficient distributed applications. Distributed system pattern clustering helps developers make informed design decisions when implementing distributed applications.
Using layered applications
The secret to creating an easy-to-use infrastructure for distributed systems is layered application. The Distributed service layer relies on lower tiers, such as the TCP/IP stack and the socket communication layer, but the lower-level details do not appear in the higher tiers that contain the application and business logic layers. This arrangement allows application developers to work at a higher level of abstraction without having to consider details such as TCP/IP packets and network byte ordering. It can also have no effect on higher layers when replacing lower layers. For example, you do not have to change code at the application layer to switch to another transport protocol (for example, using HTTP instead of using TCP/IP directly).
One way for developers to make remote calls is to use proxy (proxy) [Gamma95]. A proxy is a local alias that communicates with a client object. When a client creates an instance of a remote object, the infrastructure creates a proxy object that appears to the client to be exactly the same as the remote type. When a client invokes a method on the proxy object, the proxy invokes the remoting infrastructure. The remoting infrastructure routes the request to the server process, then invokes the server object, returns the result to the client proxy, and the client agent passes the result to the client object. Because all of these operations are done in the background, the client object may not know at all that another object resides on another computer. This not only facilitates the development of distributed applications, but also allows you to distribute objects after the program has been developed, with minimal changes to the application code.
Mode overview
The Distributed system pattern cluster emphasizes two main concepts: remote invocation and coarse-grained interface.
Figure 1 Patterns in a distributed system cluster
Remote Call
The broker (agent) pattern describes how to find a remote object and invoke one of its methods without introducing the complexity of communicating over the network into the application. This pattern lays the groundwork for most distributed architectures, including. NET Remoting.
One of the guiding principles of the. NET Framework is to simplify complex programming tasks without taking control of the programmer. By this principle,. NET Remoting provides a large number of remoting models for developers to choose from, as described in the following paragraphs.
Local copy
The simplest remoting model involves passing a copy of an object to the client by value. Any subsequent method calls to that object are real local calls. This model avoids many of the complexities inherent in distributed computing, but it also has many drawbacks. First, because you are running a local copy of the object in your own process space, the calculation is not a true distributed computation. Second, because all updates to the object's state occur only locally, they are lost. Finally, objects are usually remote because they require remote resources or if the provider of a remote object wants to protect access to its internal resources. Copying an object instance to a local process not only fails to achieve these two targets, but also increases the overhead associated with transmitting the entire object over the remote channel. Because of these limitations, this chapter discusses only the only application of object replication: Data Transfer object (data transfer objects) mode.
Server-activated objects
Invoking a method directly on a remote object is a better choice than using a local copy of a remote object. However, you can call a method on a remote object only if you have a reference to it. Getting a reference to a remote object requires that the object be instantiated first. The client requires that the server provide an instance of the object, and then the server returns a reference to the remote instance. The above process works smoothly if the remote object can be considered a service. For example, suppose you have a service to validate credit card numbers. The client object submits a credit card number and receives a positive or negative response, depending on the customer's consumption (and repayment) habits. In this case, you don't really care about the instance of the remote object. You just submit some data, receive the results, and continue the operation. This is a good example of a "stateless service". In a stateless service, each request keeps the object in its previous state.
However, not all remote object collaboration follows this model. Sometimes, you want to invoke a remote object to retrieve some data that can be accessed at a later remote call. You must ensure that the same object instance is called during subsequent calls. Also, when you have finished checking the data, you will want to deallocate the object to conserve server memory. This level of control over object instances cannot be achieved with server-activated objects. Server-activated objects provide only two replacement options for instance lifetime management:
- Creates a new instance of the object for each invocation.
- Use only a single instance of the remote object for all clients (making the object effective as Singleton).
None of these options are appropriate if you want to access the same remote instance over several functions and then throw it into the garbage collector.
Client-activated objects
Client-activated objects enable the client to control the lifetime of the remote object. The client can instantiate a remote object almost as if it were instantiating a local object, and after the client deletes all references to the object instance, the garbage collector deletes the remote object. However, this level of control costs higher. To use the client-side activation feature, you must copy an assembly that can be accessed by the client process. This conflicts with the idea that a variety of clients should be able to access remote objects without further setup.
However, you can achieve the best balance by creating a server-activated object that acts as a factory object for the server object. This factory object creates an instance of another object. The factory itself is stateless, so you can easily implement it as a server-activated Singleton. All client requests then share the same instance of the factory. Because the factory object is running remotely, all of the objects it instantiates are remote objects, but the client can decide when and where to instantiate them.
Coarse-grained interface
Calling methods across processes and network boundaries is much slower than calling methods on objects in the same operating system process.
Many object-oriented design practices typically tend to design objects with fine-grained interfaces. These objects can have many fields with related getter and setter and a number of methods, each encapsulating a small piece of functionality that is glued together. Because of this fine-grained nature, you must call a number of methods to achieve the desired result. Because this fine-grained interface approach supports many satisfying application features such as maintainability, repeatability, and testability, this is an ideal choice for standalone applications.
Using objects that expose fine-grained interfaces can greatly affect the performance of your application, because fine-grained interfaces require multiple method calls across process and network boundaries. To improve performance, a remote object must expose a larger, more granular interface. Coarse-grained interfaces expose a relatively small set of independent methods. Each method typically represents an advanced feature (such as an order or update of a customer). Because all the data required by a method is passed into the method as a parameter, these methods are treated as independent methods.
Data Transfer Object
The data Transfer Object mode applies the coarse-grained interface concept to the following problem: Passing data between components separated by processes and network boundaries. It is recommended that you replace many parameters with an object that stores all the data required by the remote method. This technique also works well for data returned by remote methods.
There are several options to implement data transfer objects (DTOS). One approach is to define a separate class for each of the different types of dtos required by the solution. These classes typically have a strongly typed public field (or property) for each of the contained data elements. These classes are serialized for the purpose of transmitting these objects across network or process boundaries. The serialized object is marshaled across the boundary and then re-established on the receiving side. The main advantages of this approach are performance and type safety. The marshaling overhead for this method is minimal, and the strongly typed field of the DTO ensures that the type errors are captured at compile time rather than at run time. The disadvantage of this approach is that you need to create a new class for each DTO. If a solution requires a large number of DTOs, the work associated with writing and maintaining these classes can be daunting.
The second way to create a DTO is to use a generic container class to hold the data. A common implementation of this method is to use a class similar to the ADO. DataSet as a generic container class. This method requires two additional conversions. The first conversion occurs on the sending side, which transforms the application data into the form that is appropriate for the dataset. The second conversion takes place at the receiving end, which extracts the data from the dataset for use in the client application. In some applications, these additional conversions may affect performance. Another disadvantage of this approach is the lack of type safety. If a client object is placed in a DataSet on the sending side, an attempt to fetch a sequential object on the receiving end causes a run-time error. The main advantage of this approach is that you do not have to write, test, or maintain any additional classes.
ADO provides a third optional option-a typed DataSet. ADO provides a mechanism for automatically generating a type-safe wrapper that wraps a DataSet. This method has the same potential performance problems as the DataSet method, but the application can benefit from the benefits of type safety, and the developer does not have to develop, test, and maintain a separate class for each DTO.
5th Distributed System mode