Message-Driven Beans Best Practices
Just like a Session bean is a façade for the synchronous invocation of business logic, a Message-Driven bean is a façade for asynchronous business logic. Another design pattern is Service Activator. According to the EJB 2.0 specification, Session beans and Entity beans are synchronous, but there are situations in which asynchronous invocations are desirable. Message-Driven beans can naturally serve as service activators. Whenever a client needs an asynchronous service, a JMS message is posted, and the corresponding MDB interprets the request, locates the service, and subsequently invokes it.
In the beginning of this chapter, we used an example to demonstrate the necessity of asynchronous processing use cases. In that example, a fictitious online store accepts credit cards and ships products when payment is received. For a synchronous design, there are two key drawbacks:
Slow response time—
To complete the transaction, we have to sequentially deal with not only internal systems (such as CRM) but also external services (credit card processing). Some of the systems might work at very slow pace, depending on the load, size of database, network connection, and so on.
Poor reliability and lack of fault tolerance—
The success of this transaction depends on that of all the subtransactions, so there are many points of failure.
Asynchronous design fixes both these problems:
Prompt response time—
The user will experience no delay because the ordering process is essentially reduced to information collecting. The transaction is decoupled. How long it takes to complete the transaction becomes irrelevant.
High reliability, fault tolerance, and scalability—
If any one step in the transaction fails abnormally, the system still has the chance to retry until a valid transaction state is reached. Because the back-end processing is decoupled from the user interaction, scaling up the system is extremely easy: Simply adding more Message-Driven beans, either locally or remotely, can deal with increased volume.
When the transaction is finally completed, a notification such as an email will be sent to the user.
Within the message façade, depending on the situation, the business logic can be carried out in different ways. It can use a few Session beans. It can call a single session façade that handles everything. Or it can use one or more other message façades.
Multiple Message-Driven beans can be used so that each point of failure is dealt with by an individual Message-Driven bean. For instance, in our online store example, we need to complete the credit card transaction first, update the CRM system, and send out the email to enable users to download. What if the credit card is charged and money is transferred but the CRM update fails? This CRM update could be repeated until it finally goes through. Thus, we need one queue for the credit card transactions and another for CRM updates. The last Message-Driven bean that listens on the CRM update notification queue can finish the business by sending the email to the customer.
Asynchronous design with a message façade obtains its merits at the expense of benefits of synchronous design, such as strongly typed parameters, return values, and exception propagation. Suppose that the user's credit card has expired or does not have enough funds. A synchronous system can simply throw a BadCreditCardException so that the UI can prompt user for a valid one. In an asynchronous system, this process is not straightforward any more because the initiator (the user) might already be offline or off this site. One solution is sending an email that asks the customer to come back to the site and pay with another credit card or another means. Another solution is to have a queue for error messages, which customers can consult through a client application.
Despite the inherent complexity and apparent drawbacks of asynchronous system design, it is very useful and effective for the use cases we just talked about. Message-Driven beans set a standard and make this truly easy for J2EE applications.
Message-Driven beans should follow general guidelines for enterprise beans development, such as using java:comp/env resources, and so on.
It's common to use Message-Driven beans to receive messages and dispatch their processing to one or more Session beans, so that such business logic can be shared across synchronous and asynchronous components.
One Message-Driven bean can listen on only one JMS destination; multiple beans can listen on the same destination. A JMS destination can receive any kind of messages. If multiple kinds of beans listen on a same queue, for instance, each processes certain types of messages. Use <message-selector> tag to specify this.
Sometimes certain messages must be processed in order. One way to do this is to set the <max-beans-in-free-pool> value to 1, which effectively reduces the message processing to single-thread. If other types of messages can be processed in any order, create separate queues.
Often times there are different ways to fulfill the same set of use cases, especially when Message-Driven beans are involved, so it's wise to compare multiple designs to find the best solution.
|