Interesting applications rarely live in isolation; an application can't do much that is very useful without working with other applications. Service-Oriented Architecture takes the trend of integrating applications so that they can work together and accelerates it, breaking each application into parts that then must be integrated with each other. The SOA model -- service consumers invoking service providers -- may seem simple, but it introduces two significant problems:
- How does a consumer find the providers of the service it wants to invoke?
- How can a consumer invoke a service quickly and reliably, because the network is actually slow and unreliable?
It turns out there's a fairly straightforward answer to both these problems, an approach called the Enterprise Service Bus (ESB). An ESB makes service invocation simpler for both the consumer and the provider by handling all of the complexity in between. Not only does an ESB make it simpler for applications (or their parts) to invoke services, it also helps them transfer data and broadcast event notifications. An ESB's design embodies a number of established design patterns and standard specifications.
I wrote this article to help you, the developer, understand why an ESB is a useful and necessary part of application integration, including SOA. The article doesn't focus on definitions or products, but on the functions and capabilities an ESB implements for you so that you don't have to. It shows what an ESB does for you.
To help you understand application integration and SOA, I'll start with how Web services work. Web services are only one approach you can use to implement a service invocation. They may not even be the best approach, but they are the most standardized approach available today, and they do help frame the design of what I'm trying to accomplish.
To begin, I must explain the terminology. A Web service is a lot like a function in procedural programming: it has a name, parameters, and a result. The name is a Uniform Resource Identifier (URI), which the Web services provider uses to make the Web service available as an endpoint. The Web services consumer uses the endpoint URI as the address for locating and invoking the Web service. The specific operation and parameters are contained in a request, which the consumer passes into the Web service when it invokes the endpoint. After performing the service, the endpoint passes a response back to the consumer, which indicates success (or an error) and contains the result of the service. So, a consumer invokes a provider's endpoint, passing in a request, and getting back a response.
The current definition of how to implement a Web service is the WS-I Basic Profile 1.1, which consists of SOAP 1.1 over HTTP 1.1 described by Web Services Description Language (WSDL) 1.1. (See Resources for a link to the specification itself.) With SOAP over HTTP, the consumer invokes a service using a SOAP request transported over HTTP in an HTTP request. The consumer blocks synchronously on the HTTP socket waiting for the HTTP response containing the SOAP response. The endpoint's API is described by its WSDL, a contract between the consumer and the provider.
Now that you understand the terminology, let's look at the communication choices a consumer has for invoking a service: synchronous or asynchronous.
A consumer can implement a service invocation synchronously or asynchronously. From the consumer's point of view, the difference is this:
- Synchronous – The consumer uses a single thread to invoke the service; the thread sends the request, blocks while the service is running, and waits for the response.
- Asynchronous – The consumer uses a pair of threads to invoke the service; one thread sends the request, then a separate thread receives the response.
The terms synchronous and asynchronous are often confused with the terms sequential and concurrent. These latter terms have to do with the order that separate tasks must be performed, whereas synchronous and asynchronous have to do with the way a thread performs a single task, such as invoking a single service. A good way to understand the difference between synchronous and asynchronous invocation is to consider the consequences for crash recovery:
- Synchronous – If the consumer crashes while blocking as the service runs, when it restarts it has no way of reconnecting to the invocation in progress, so the response is lost. The consumer must repeat the invocation and hope it doesn't crash this time.
- Asynchronous – If the consumer crashes while waiting for the response after sending the request, when it restarts it can continue waiting for the response, so the response is not lost.
Crash recovery is not the only difference between synchronous and asynchronous invocation, but if you're trying to decide which style a particular invocation uses, looking at the way each handles crash recovery is usually a good way to tell.
Now that you understand the consumer's choices for service invocation communication style, take a look at the consumer's connectivity style choices for connecting to a provider. The consumer may choose from among the following communication styles:
- Synchronous direct invocation
- Synchronous brokered invocation
- Asynchronous brokered invocation
I will explain each style separately.
The SOAP over HTTP style of invoking a Web service is said to be direct: Much like performing a function call, the consumer knows the address of the endpoint and invokes it directly. For the invocation to succeed, the Web service must be operational when the consumer invokes the endpoint and must respond before the consumer times out. If a Web service is deployed to a new location (such as a different Internet domain), the consumer must be made aware of the new URI for the endpoint. To deploy several providers of the same type of service, each provider's endpoint must be deployed to a different URI. To choose between the different service providers, the consumer must know each of their URIs.
For example, consider a simple Web service for getting a stock quote: The consumer passes in a stock symbol and gets back the stock's current price. This service might be provided by several different brokerage companies, each under a different Internet URL. Getting the URL for a Web service is a chicken-and-egg problem. If the consumer knew the location of the endpoint, it could ask the service what its address is, but that's the address the consumer would need so that it could ask for the address.
To get around this problem, the Universal Description Discovery and Integration (UDDI) specification describes a Web service that is a directory, like a phonebook, for locating other Web services. The idea is that the UDDI service is deployed to a well-known address the consumer will already know; then the consumer can use UDDI to find other Web services.
In the case of the stock quote service, the consumer knows the UDDI service's address, which in turn knows the stock quote services' addresses, as shown in Figure 1.
Figure 1: Direct invocation of a Web service
Figure 2 shows how the consumer uses the UDDI service to find the stock quote providers' endpoints and invoke one of them. The process works as follows:
- The consumer asks UDDI for a list of providers for a service.
- The consumer selects a provider's endpoint from the list returned by UDDI.
- The consumer invokes that endpoint.
Figure 2: Synchronous direct service invocation
Note that the algorithm for selecting a provider is completely up to the consumer; in this example, the consumer just chooses the first one in the list. A real implementation might be more sophisticated.
Notice also that because the service endpoints can change, every time the consumer wants to invoke the service, it should re-query UDDI to see if the providers' details have changed. Having to query UDDI for every service invocation greatly adds to the overhead of invoking the service, especially in the usual case where the providers' details haven't changed.
A shortcoming of the direct invocation approach is that the consumer must know the URI of the provider's endpoint to invoke the service. It uses UDDI as a directory to find that URI. If there are multiple providers, UDDI specifies multiple URIs, and the consumer must select from among them. If a provider changes its endpoint's URI, it must reregister with the UDDI server so that UDDI has the new URI, and then the consumer must re-query UDDI to get the new URI. In effect, this means that every time the consumer wants to invoke a service, it must query UDDI for the endpoint URIs and select from among them. This causes the consumer to waste a lot of effort repeatedly making UDDI lookups and selecting a provider. This approach also forces the consumer to somehow choose a provider from among a seemingly indistinguishable list.
One approach to simplify this problem is to introduce a broker to act as an intermediary for invoking the Web service. The consumer no longer invokes the service provider directly, but rather invokes a service proxy in the broker, which in turn invokes the service provider. The consumer needs to know the URI for the proxy's endpoint and so uses UDDI to find the address, but in this case, UDDI only returns a single URI, so the consumer doesn't have to choose. The consumer probably doesn't even realize that the endpoint is in a proxy; it just knows that it can use this URI for invoking the Web service. The broker coordinates the consumer with the service providers, as shown in Figure 3.
Figure 3: A synchronous enterprise service bus
The proxy's URI should be stable: After the consumer uses UDDI to get the proxy's URI the first time it invokes the service, the consumer can reuse that URI for subsequent invocations (at least until the proxy stops working). Meanwhile, the proxy keeps track of the providers, their URIs (which may change between invocations), whether they're available (did the last invocation fail?), their load (how long did the last invocation take?), and so on. The proxy then takes the responsibility for choosing the best provider for each invocation, relieving the consumer of this responsibility. The consumer simply invokes the same proxy at the same address every time, and the proxy takes care of coordinating with the various providers.
Figure 4 shows how the consumer uses the broker to invoke the service, which works in the following way:
- The consumer asks UDDI for a list of providers for a service. The URI returned by UDDI is actually for the service proxy. UDDI will only return one URI, not several, because the broker only has one proxy for a particular service.
- The consumer invokes the service using the proxy's URI.
- The service proxy selects a service provider from its list of available providers.
- The service proxy invokes the selected provider's endpoint.
Figure 4: Synchronous brokered service invocation
Notice that the effort of selecting the provider has moved from the consumer and is now encapsulated in the broker's proxy. This simplifies life for the consumer. Ultimately, the selection process the proxy uses may be no different than the process the consumer would have used. However, because the UDDI server and proxy are encapsulated within the broker, certain efficiencies can be more easily introduced, such as caching UDDI information in the proxy and having the UDDI server notify the proxy when the cached information becomes stale.
Note too that because the proxy's address is stable, the consumer doesn't have to query UDDI repeatedly, once per service invocation. Rather, the consumer only needs to query UDDI once and then can safely cache the proxy's address and use it to invoke the service repeatedly. This greatly lowers the overhead of invoking a service.
A shortcoming of the synchronous approach is that the consumer must wait while the service is performed -- the consumer's thread must block while the service runs. If the service takes a long time to perform, the consumer may give up before it receives the response. When the consumer makes the request, if none of the service providers are running or if they're all overloaded, the consumer will not be able to wait. And as explained earlier, if the consumer crashes while blocking, even if it restarts the response will be lost and the invocation must be repeated.
A common solution to this problem is for the consumer to invoke the service asynchronously. With this approach, the consumer uses one thread to send the request and another to receive the response. This way the consumer doesn't have to block waiting for the response and can perform other work in the meantime. The consumer is therefore much less sensitive to how long it takes the provider to perform the service.
A broker that enables a consumer to invoke a Web service asynchronously is implemented using a messaging system that uses message queues for sending the request and receiving the response. Like a synchronous service proxy, the pair of message queues acts as a single address the consumer uses to invoke the service, regardless of how many providers may be listening, as shown in Figure 5.
Figure 5: An asynchronous enterprise service bus
This approach uses the Request-Reply pattern to invoke a Web service. Instead of HTTP as specified in WS-I BP 1.1, message queues now perform the transport. The SOAP request and response are the same as with WS-I BP, but they are now enclosed in messaging system messages.
Figure 6 shows how the consumer invokes the service asynchronously using a broker, which proceeds according to the following steps:
- The consumer sends the SOAP request as a message on the request queue. The consumer is now finished and can use that thread to perform other work.
- Each of the providers is a consumer on the request queue, which makes them competing consumers. The messaging system determines which provider is able to receive the message and ensures that only one provider receives the message. How this works specifically depends on the implementation of the messaging system.
- The winning provider receives the message from the request queue.
- The provider performs the service.
- The provider sends the SOAP response as a message on the reply queue. The provider is now finished and can use its thread to perform other work (such as waiting for another request).
- The consumer's listener thread receives the message containing the SOAP response.
Figure 6: Asynchronous brokered service invocation
Notice that the effort of selecting a provider is now encapsulated within the messaging system, simplifying life for the consumer. Notice also that if the consumer crashes after making the request, even if the response comes back in the meantime, the messaging system will save the response on the reply queue until the consumer starts up again.
Note as well that the consumer does not use UDDI to find the request and reply queues. Currently, there is no standard service for returning a pair of queue addresses, so the consumer must just know the addresses. The consumer is either hardcoded with the addresses or reads them from an external configuration file. In the future, either UDDI needs to be extended or a similar service specified that a consumer can query to discover the pair of queues for invoking a particular service.
Now you understand the connectivity style choices for service invocation. Let's look at additional integration capabilities that can also be helpful, then at how you develop an ESB that gives us these abilities.
An ESB also gives you the opportunity to go beyond service invocation and integrate applications and parts of an SOA using other techniques. A service invocation is almost always a two-way operation, meaning that a request is sent from the consumer to the provider and a response is sent back in the other direction. Other integration techniques work as one-way operations, where a sender pushes information to the receiver but does not wait for a response; the receiver simply consumes the information without responding.
Sometimes an application simply wants to transfer data to another application without necessarily invoking a procedure in the receiver, and definitely without waiting for a result. This is a classic integration problem: One application has data that another one needs. The sender doesn't need to tell the receiver what to do with the data; it is just making the data available.
A service invocation can be used to transfer data, which is equivalent to invoking a setter method, but that's forcing the data transfer into the RPC paradigm. The data transfer is actually more like a file transfer: data is exported from the sender and imported to the receiver without the sender overtly directing the receiver as to what to do with the data. This is more akin to a document-style SOAP message than an RPC-style message.
Using an ESB for data transfer leverages its abilities to locate the receiver and transfer data reliably. The sender doesn't have to know how to find the receiver, it just has to know how to find the ESB and trust that it will find the receiver. The ESB is also responsible for transferring the data reliably. The sender can simply push the data into the ESB and know that it will be delivered.
For more information about the data transfer technique, see the Document Message pattern. (For more about this, see the book Enterprise Integration Patterns listed in Resources.)
Sometimes a change occurs in one application that needs to be announced to other applications. For example, if a customer edits his address in an application, other applications with their own databases should be notified so they can update their records.
Again, one application can invoke a service on another application to inform it of a change, but there are three problems with this approach. The first two problems are the same as those with data transfer. First, a service invocation is too specific about what the receiver should do with the information, and second it tends to be two-way, forcing the sender to wait for a reply (even asynchronously) it doesn't really want.
The third and most significant problem with invoking a service for event notification is that service invocation is inherently one-to-one, consumer-to-provider, whereas event notification is inherently broadcast, one-to-many, to all interested receivers. Using service invocation, the sender would have to keep track of all interested receivers and invoke the service separately on each of them. This broadcasting of the notification is better left to the broker between the sender and the receivers.
Using an ESB for event notification leverages its abilities to keep a list of interested receivers and make sure the notification gets delivered to each one. It enables the sender to issue the notification just once and be assured that the notification will be delivered to all interested receivers, whoever those are. Because the operation is one-way, the sender can go about doing other work while the notifications are being delivered, and the notifications can be delivered concurrently.
For more information about the data transfer technique, see the Event Message pattern. (Again, see the book Enterprise Integration Patterns listed in Resources.)
Now you know the difference between invoking a Web service in a provider directly as compared with doing so using a broker. You've also seen how the broker can enable the consumer to invoke the service synchronously or asynchronously.
The broker is often what has come to be called an ESB. So how does an ESB compare to already established designs and patterns?
What distinguishes Web services from previous integration approaches is that a consumer can dynamically bind to the provider of a service. This works because of the following two main capabilities:
- Self-describing – A Web service describes itself in a machine-readable way. Two or more providers of the same service are immediately recognizable, even though they may have very different implementations, because their declarative interfaces fit the same description.
- Discoverable – Web services providers can be organized into machine-executable directories. A consumer can search such a directory to find a provider of a desired service.
These self-describing and discoverable capabilities of Web services are very different from previous integration approaches. With other approaches, interfaces were enforced at compile-time, which is also when consumers were bound to providers. Message formats were not expressed declaratively. They were implied by mutual agreement and not enforceable until a receiver failed to parse the structure created by the sender.
Self-describing services simplified integration by declaring interfaces that could be enforced. Dynamic discovery of services freed the consumer from being bound to a particular provider at a particular address, but the opportunity for run time discovery created its own problems. Should a consumer perform discovery of providers of a service once or repeatedly?
It has been difficult to resolve the tension between binding a consumer to a provider once-and-for-all at compile time versus potentially discovering a new provider with every invocation during run time. An ESB offers a third choice, a compromise enabling a consumer to dynamically bind to a service proxy just once and yet still be able to use multiple and future providers using the proxy.
Thus, an ESB not only makes services available so that consumers can invoke them, but it also offers consumers the ability to find the services programmatically.
The foundation for a synchronous ESB is what's known as a services gateway, which acts as a middleman between service consumers and providers to facilitate synchronous brokered invocations. It provides access to all known services and the service proxy for each of them. In this way, a gateway provides one-stop shopping for a consumer that wants to invoke any of the services in any of the providers the gateway brokers.
When the services the gateway coordinates are Web services, the services are self-describing. Each service declares its interface using WSDL, which consists of the following four parts:
- Port types -- The sets of operations performed by the Web service. A port type might be
stockBrokerServices, with ports/operations like
- Messages – The format of the requests and responses, such as
getQuoteRequest(which contains a stock symbol) and
getQuoteResponse(which contains a price).
- Types – The data types used by the Web service, such as symbol and price (or simply
- Bindings – The address for invoking the operation, such as http://stockbroker.example.com/getQuote.
Such a gateway's Web services -- or more specifically, its proxies for the services -- are also discoverable. The gateway provides this capability as a UDDI service, as illustrated earlier. To discover the address for invoking a service, a consumer queries the gateway's UDDI service for providers of the desired WSDL operation and gets back the binding for the gateway's proxy for that operation.
At the foundation of an asynchronous enterprise service bus is an established pattern called a Message Bus, described in the book Enterprise Integration Patterns listed in Resources. A message bus is a collection of message channels (also known as a queue or topic) usually configured as request-reply channel pairs. Each pair represents a service that can be invoked by a consumer using the bus. The caller puts a request message on the request queue for the service and then (asynchronously) listens for the result on the reply queue. (The caller knows which result is the right one for its particular request because it has the right correlation identifier.)
The consumer invoking the service doesn't actually know who's providing the service. The service providers also connect to the message bus and listen for request messages. If there are multiple service providers, they actually compete with each other as competing consumers to be the one to consume a particular request. The messaging system that implements the message bus acts as a message dispatcher and distributes the request messages to the service providers, ideally optimizing this distribution somehow based on load balancing, network latency, and so forth. After a service provider receives a request, it performs the service and then puts the result in a message on the agreed-upon reply channel. So the provider and the consumer never directly know about each other's locations; they just know about the message bus and how to address the appropriate channels, and by sharing the same channels they can communicate.
This message bus is the gist of an ESB, and this is nothing new. Application integrators have been doing this for more than a decade using message queuing products like WebSphere® MQ and TIBCO Enterprise Message Service. Indeed, it's often said that if you are using enterprise messaging products, you have an ESB. IBM customers have been doing this for quite some time with WebSphere Business Integration Message Broker and WebSphere MQ.
So, is an ESB just a message bus? No, a message bus is definitely the foundation of an asynchronous ESB, but a full ESB is something more. An ESB has abilities message buses have always lacked: The aforementioned describing and discovery abilities.
So if a message bus is not a full ESB, then what else does an ESB do?
A shortcoming of the traditional message bus approach is that it's not self-describing. From the consumer's perspective, there are a whole lot of channels for invoking a whole lot of services. But which one is the right channel for invoking the service the consumer needs? The consumer can't just put a request on any request channel; it has to know the right channel to use to invoke the particular service it needs. Otherwise, it might end up buying an airline ticket when it wanted a book. Likewise, even after the consumer knows (somehow) which channel to use (and which channel to listen to for the reply), it then needs to know what format the data in the request needs to be (and what data format to expect for the reply).
As described earlier, WSDL solves this problem for synchronous Web services, and for the time being is the standard of choice for describing asynchronous services as well. The WSDL associated with a request channel would describe what service that channel provides as well as the format of the request message the consumer must provide. The WSDL would probably also specify the reply channel the caller should listen to for the reply and the format to expect for the reply message. In this way, a caller application can programmatically look at the pair of channels for invoking a service and know that they provide the desired service using the desired request and reply message formats.
Self-describing services channels lead to another issue, namely discovery, which synchronous Web services solve with UDDI. As shown earlier, a consumer asks a UDDI server for the address of a Web service provider, and the server replies with the URL for that provider. The consumer uses the URL to invoke the service.
An ESB needs a similar directory service, one with a UDDI-like API that a consumer can invoke to ask for the address of a service that implements the desired WSDL operation. The ESB replies with the address of the appropriate pair of request-reply channels. So an ESB consumer, like a UDDI consumer, needs to know nothing more than the following:
- The WSDL describing the service it wants to invoke
- The address of the ESB's directory service (which can probably be derived from the ESB's root address)
This is enough to locate a service's request and reply channels and start invoking the service. Furthermore, this directory service is just another service that the ESB provides, a master service for locating other services.
Service consumers face an artificial choice between communication styles: synchronous or asynchronous. To solve this dilemma, many ESBs will support both synchronous and asynchronous services, and in fact may offer both invocation models for the same service. In this case, when a consumer asks for the address of a service, it can get two matches: one synchronous, the other asynchronous. The consumer can then choose the invocation model it likes best. Either way, the service performed is the same although the specific service provider instance may differ.
So an ESB is better than a traditional message bus because it also makes a service self-describing and provides a directory service for finding the other services. This is what the vendors of products for building ESBs are striving to deliver.
As you've seen, a service can be invoked in any of following three ways:
- Directly and synchronously
- Synchronously through a broker
- Asynchronously through a broker
An Enterprise Service Bus is a broker that supports synchronous and asynchronous service invocation. It also enables data transfer and event notification between applications. It helps consumers find providers and handles the details of communication between them.
A synchronous ESB is a services gateway that acts as a central coordinator of a variety of services. An asynchronous ESB is a message bus whose services also support the Web service capabilities of being self-describing and discoverable. Standards and patterns exist today for implementing a synchronous ESB and a message bus that is a simplified asynchronous ESB. Additional standards are needed for asynchronous ESBs to reach their full potential.
New to SOA and Web services provides an overview for readers who would like to learn about Web services but don't know where to start.
WS-I Basic Profile Version 1.1 (WS-I) defines the WS-I Basic Profile 1.1.
Simple Object Access Protocol (SOAP) 1.1 discusses the SOAP 1.1.
Find out more about Hypertext Transfer Protocol (HTTP) 1.1.
Web Services Description Language (WSDL) 1.1 is an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information.
Universal Description, Discovery and Integration (UDDI) 2.04 specification describes the programming interface and expected behaviors of all instances of the UDDI registry.
"Simplify integration architectures with an Enterprise Service Bus" (developerWorks, August 2005) dispells the myths of an Enterprise Service Bus and shows how you can apply this architectural style to the implementation of Service-Oriented Architecture-based applications.
Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf (Addison Wesley ISBN 0321200683; October 2003) provides a catalog of patterns and real-world solutions that demonstrate the power of messaging and helps you design effective messaging solutions for your enterprise.
"Building a JMS Web service using SOAP over JMS and WebSphere Studio" (developerWorks, February 2004) describes the basics of JMS Web services and shows you how to develop a two-way request and response JMS Web service using WebSphere Studio V5.1.
Find out more about WSDL JMS Extension at the Apache Web site.
Calling an Asynchronous Web Service Sample shows the use of a Web service control.
Bobby Woolf is a WebSphere J2EE Consultant for IBM Software Services for WebSphere (ISSW). Bobby assists clients in developing applications for WebSphere Application Server using WebSphere Studio Application Developer. He is a co-author of Enterprise Integration Patterns and The Design Patterns Smalltalk Companion. He also has a blog on the IBM developerWorks Web site called J2EE in Practice. Bobby is a frequent conference speaker.