On the surface, accessing a service sounds so simple—just call the interface! In reality, access can get very complicated as you try to control access to services and maintain flexibility concerning where service users and providers are deployed. The following sections explore a number of the design issues you will encounter and the design patterns that you can use to address them.
Direct Service Access
The most obvious way to use a service is to directly access the service provider's interface (Figure 4-10). While this is straightforward, it requires that the service user be aware of both the functionality provided by the service interface (which you would expect) and the location of the interface. If you are using HTTP to access your service, for example, then the service user needs to know either the IP address or hostname of the machine on which the service is running as well as the specific socket corresponding to the service's interface.
Figure 4-10 Direct Service Access
This requirement to know about the interface's location makes the design of the service user dependent on the deployment specifics of the service provider. If the service provider is moved from one machine to another, then the service user's configuration must be updated to reflect the change. If the service becomes very successful and has many users, such dependencies make it difficult to move the service provider. Such movement might be required to add capacity by moving the service to a larger machine or recover from a machine outage by moving the service to an alternate machine. While network-level solutions such as virtual IP addresses and virtual hostnames provide some relief, they have their own limitations and accompanying administrative costs as well.
Variations in Direct Service Access
One means of avoiding this dependency between the service user and the location of the service interface is to employ a lookup service (Figure 4-11). The lookup service knows the actual location of the provider's service interface. In order for the service user to access the service, it first uses the lookup service to get the actual location of the service interface and then uses this information to access the service. The JNDI (Java Naming and Directory Interface)1 is a service interface that is commonly used for this purpose. The advantage of this approach is that when a service provider changes location, only the lookup service needs to be updated. The service users will pick up the change from the lookup service.
Figure 4-11 Lookup Service
When a lookup service is used, the lookup typically occurs just the first time a particular user wants to access the service. Once the service interface is located, the user continues to use that interface as long as it is operational.
Of course, the lookup service itself now presents exactly the same problem that it solves for other services: the service user must know the location of the lookup interface to be able to use it. But at least the problem is reduced in scope to just accessing the lookup service, which presumably will not change its interface location very often.
Another approach that can isolate the service user from the details of the service provider's deployment is to employ a level of indirection—a proxy—for accessing the service (Figure 4-12). To the service user, the proxy presents what appears to be the service's interface without knowing that it is, in reality, a proxy interface. The proxy forwards all incoming requests to the real service interface and forwards replies from the service interface back to the service user through the proxy interface.
Figure 4-12 Proxy Access Interfaces
Direct Access Limitations
There are a couple of drawbacks to the direct access approach and its service lookup and proxy variants. One is that any change in the location of the service provider's interface requires changes to the configuration of some other component. In direct access, the component requiring update is the service user. With service lookup, it is the lookup service, and with a proxy it is the proxy. This dependency of other components on the physical location of the service provider's interface complicates changing the service provider's location. It adds administrative overhead and adds to the time it takes to implement such changes.
This administrative overhead affects more than just the normal deployment of services. It comes into play when you want to provide a fault tolerant or highly available service as well. In such cases, when something happens to the existing service provider you need to bring up a replacement copy of the service provider. This replacement copy will be in a different location, generally on a different machine. For local failover, this different machine may be in the same data center, but in the case of a site disaster it will be in a different data center. In such cases it becomes very difficult to maintain the appearance of the "same location" for the service interface with network-based virtual IP addresses and hostnames. Administrative changes to service users, lookup services, or proxy agents are generally required.
These administrative changes significantly complicate the design, implementation, and test of service failover. A failure to make any of the required administrative changes during a failover will cause the service to become unavailable just as surely as a breakdown in bringing up the backup service provider. The more administrative changes that need to be made, the greater the chance an implementation mistake will cause the failover itself to fail. Since testing failover is an arduous and risky activity, there are advantages to keeping the administrative changes as simple as possible—or eliminating them altogether.
This situation gets even more complicated when asynchronous request-reply, subscription, and unsolicited notification patterns come into play. These patterns often require delivery interfaces on the service user—interfaces that the service provider, lookup service, or proxy need to know about. This makes the administrative problem even more complex, since location information now needs to be updated when service clients move.
Message-Based Service Access
Messaging services provide an alternate means for service users and service providers to interact. We are all familiar with messaging services such as e-mail and instant messaging. At the systems level, messaging services such as the standards-based JMS (Java Messaging Service), IBM Websphere MQ, and TIBCO Rendezvous are in common use.
When using a messaging service, all communications between the parties is exchanged using an intermediary—the Messaging Service Provider (Figure 4-13). In contrast with the proxy approach, both sender and recipient are very much aware that they are employing a third party to send and receive messages.
Figure 4-13 Messaging Service
Electronic messaging services differ from postal (physical mail) services in a subtle but significant way. Postal service mailboxes are physical entities that serve as the interfaces between the messaging service and its users. An address in physical mail systems designates the physical mailbox to which the letter or package is to be delivered. In other words, the address is location-specific.
In contrast, electronic messaging separates the concepts of destination and interface. The address now denotes a logical destination whose physical location is unknown to either the sender or recipient. The messaging service interface is no longer tied to a specific destination. Instead, the messaging service provides a generic interface for sending and receiving messages regardless of the destination. This separation greatly simplifies the use of messaging services.
Abstract destinations provide location independence. The fact that the logical destination is no longer tied to the physical location of the recipient means that the recipient's location can change without impacting the sender—or the mail service. Thus, you can send an e-mail message to firstname.lastname@example.org without having any idea where Jane Doe actually is. Furthermore, Jane Doe can retrieve her message from any machine that can access the messaging service, whether at home, at work, or at an Internet café in Istanbul!
The messaging service provides all the interfaces needed for communications (Figure 4-14). This simple shifting of interfaces to the messaging service greatly simplifies making deployment changes. None of the participants—sender, recipient, or messaging service—has any dependency at all on the location of the sender and recipient. In fact, the only location dependency that remains is that the sender and recipient must know the location of the messaging service interfaces. Consequently, the sender and recipient can move freely without altering any configuration information—anywhere. As long as both can access the messaging service interfaces, they can communicate with each other.
Figure 4-14 Messaging Service Interfaces
There is another significant benefit that arises from the fact that the messaging service provides both the sending and receiving interfaces. Because the message recipient does not have to provide an interface to receive a message (it uses the messaging service's interface instead), it is easy for any component to receive a message. All that is required is an agreed-upon destination name. Thus it is easy for any component to be both a sender and a recipient. This communications flexibility enables the convenient implementation of the asynchronous request-reply, subscription, and unsolicited notification service interaction patterns. Therefore, the use of a messaging service provides flexibility in choosing whatever service interaction pattern seems most appropriate for the service being designed.