3 Best Practices for Application Design

This chapter describes design options for WebLogic Server JMS, application behaviors to consider during the design process, and recommended design patterns.

Message Design

This section provides information on how to design messages improve messaging performance:

Serializing Application Objects

The CPU cost of serializing Java objects can be significant. This expense, in turn, affects JMS Object messages. You can offset this cost, to some extent, by having application objects implement java.io.Externalizable, but there still will be significant overhead in marshalling the class descriptor. To avoid the cost of having to write the class descriptors of additional objects embedded in an Object message, have these objects implement Externalizable, and call readExternal and writeExternal on them directly. For example, call obj.writeExternal(stream) rather than stream.writeObject(obj). Using Bytes and Stream messages is generally a preferred practice.

Serializing strings

Serializing Java strings is more expensive than serializing other Java primitive types. Strings are also memory intensive, they consume two bytes of memory per Character, and cannot compactly represent binary data (integers, for example). In addition, the introduction of string-based messages often implies an expensive parse step in the application in order to process the String into something the application can make direct use of. Bytes, Stream, Map and even Object messages are therefore sometimes preferable to Text and XML messages. Similarly, it is preferable to avoid the use of strings in message properties, especially if they are large.

Server-side serialization

WebLogic JMS servers do not incur the cost of serializing non-persistent messages. Serialization of non-persistent message types is incurred by the remote client. Persistent are serialized by the server.

Selection

Using a selector is expensive. This consideration is important when you are deciding where in the message to store application data that is accessed via JMS selectors.

Message Compression

Compressing large messages in a JMS application can improve performance. This reduces the amount of time required to transfer messages across the network, reduces the amount of memory used by the JMS server, and, if the messages are persistent, reduces the size of persistent writes. Text and XML messages can often be compressed significantly. Of course, compression is achieved at the expense of an increase in the CPU usage of the client.

Keep in mind that the benefits of compression become questionable for smaller messages. If a message is less than a few KB in size, compression can actually increase its size. The JDK provides built-in compression libraries. For details, see the java.util.zip package.

For information on using JMS connection factories to specify the automatic compression of messages that exceed a specified threshold size, see "Compressing Messages" in the Tuning Performance of Oracle WebLogic Server.

Message Properties and Message Header Fields

Instead of user-defined message properties, consider using standard JMS message header fields or the message body for message data. Message properties incur an extra cost in serialization, and are more expensive to access than standard JMS message header fields.

Also, avoid embedding large amounts of data in the properties field or the header fields; only message bodies are paged out when paging is enabled. Consequently, if user-defined message properties are defined in an application, avoid the use of large string properties.

For more information, see Message Header Fields and Message Property Fields.

Message Ordering

You should use the Message Unit-of-Order feature rather than Ordered Redelivery to guarantee ordered message processing. The advantages of Message Unit-of-Order over Ordered Redelivery are:

  • Ease of configuration.

    • Does not require a custom connection factory for asynchronous receivers, such as setting MessagingMaximum to 1 when using message-driven beans (MDBs).

    • Simple configuration when using distributed destinations.

  • Preserves message order during processing delays.

  • Preserves message order during transaction rollback or session recovery.

Oracle recommends applications that use Ordered Redelivery upgrade to Message Unit-of-Order. For more information, see Using Message Unit-of-Order.

Topics vs. Queues

Surprisingly, when you are starting to design your application, it is not always immediately obvious whether it would be better to use a Topic or Queue. In general, you should choose a Topic only if one of the following conditions applies:

  • The same message must be replicated to multiple consumers.

  • A message should be dropped if there are no active consumers that would select it.

  • There are many subscribers, each with a unique selector.

It is interesting to note that a topic with a single durable subscriber is semantically similar to a queue. The differences are as follows:

  • If you change a topic selector for a durable subscriber, all previous messages in the subscription are deleted, while if you change a queue selector for consumer, no messages in the queue are deleted.

  • A queue may have multiple consumers, and will distribute its messages in a round-robin fashion, whereas a topic subscriber is limited to only one consumer.

For more information on configuring JMS queues and topics, see "Queue and Topic Destination Resources" in Administering JMS Resources for Oracle WebLogic Server.

Asynchronous vs. Synchronous Consumers

In general, asynchronous (onMessage) consumers perform and scale better than synchronous consumers:

  • Asynchronous consumers create less network traffic. Messages are pushed unidirectionally, and are pipelined to the message listener. Pipelining supports the aggregation of multiple messages into a single network call.

    Note:

    In WebLogic Server, your synchronous consumers can also use the same efficient behavior as asynchronous consumers by enabling the Prefetch Mode for Synchronous Consumers option on JMS connection factories, as described in Use Prefetch Mode to Create a Synchronous Message Pipeline.

  • Asynchronous consumers use fewer threads. An asynchronous consumer does not use a thread while it is inactive. A synchronous consumer consumes a thread for the duration of its receive call. As a result, a thread can remain idle for long periods, especially if the call specifies a blocking timeout.

  • For application code that runs on a server, it is almost always best to use asynchronous consumers, typically via MDBs. The use of asynchronous consumers prevents the application code from doing a blocking operation on the server. A blocking operation, in turn, idles a server-side thread; it can even cause deadlocks. Deadlocks occur when blocking operations consume all threads. When no threads remain to handle the operations required to unblock the blocking operation itself, that operation never stops blocking.

For more information, see Receiving Messages Asynchronously using the Classic API and Receiving Messages Synchronously using the Classic API.

Persistent vs. Non-Persistent Messages

When designing an application, make sure you specify that messages will be sent in non-persistent mode unless a persistent QOS is required. We recommend non-persistent mode because unless synchronous writes are disabled, a persistent QOS almost certainly causes a significant degradation in performance.

Note:

Take special care to avoid persisting messages unintentionally. Occasionally an application sends persistent messages even though the designer intended the messages to be sent in non persistent mode.

If your messages are truly non-persistent, none should end up in a regular JMS store. To make sure that none of your messages are unintentionally persistent, check whether the JMS store size grows when unconsumed messages are accumulating on the JMS server. Here is how message persistence is determined, in order of precedence:

  • Producer's connection's connection factory configuration:

    • PERSISTENT (default)

    • NON_PERSISTENT

  • JMS Producer API override on QueueSender and TopicPublisher:

    • setDeliveryMode(DeliveryMode.PERSISTENT)

    • setDeliveryMode(DeliveryMode.NON_PERSISTENT)

    • setDeliveryMode(DeliveryMode.DEFAULT_DELIVERY_MODE) (default)

  • JMS Producer API per message override on QueueSender and TopicPublisher:

    • for queues, optional deliveryMode parameter on send()

    • for topics, optional deliveryMode parameter on publish()

  • Override on destination configuration:

    • Persistent

    • Non-Persistent

    • No-Delivery (default, implies no override)

  • Override on JMS server configuration:

    • No store configured implies using the default persistent store that is available on each targeted WebLogic Server instance

    • Store configured implies no-override.

  • Non-durable subscribers only:

    • If there are no subscribers, or there are only non-durable subscribers for a topic, the messages will be downgraded to non-persistent. (Because non-durable subscribers exist only for the life of the JMS server, there is no reason to persist the message.)

  • Temporary destinations:

    • Because temporary destinations exist only for the lifetime of their host JMS server, there is no reason to persist their messages. WebLogic JMS automatically forces all messages in a temporary destination to non-persistent.

Durable subscribers require a persistent store to be configured on their JMS server, even if they receive only non-persistent messages. A durable subscription is persisted to ensure that it continues through a server restart, as required by the JMS specification.

Deferring Acknowledges and Commits

Because sends are generally faster than receives, consider reducing the overhead associated with receives by deferring acknowledgment of messages until several messages have been received and can be acknowledged collectively. If you are using transactions substitute the word commit for acknowledge.

Deferment of acknowledgements is not likely to improve performance for non-durable subscriptions, however, because of internal optimizations already in place.

It may not be possible to implement deferred acknowledgements for asynchronous listeners. If an asynchronous listener acknowledges only every 10 messages, but for some reason receives only 5, then the last few messages may not be acknowledged. One possible solution is to have the asynchronous consumer post synchronous, non-blocking receives from within its onMessage() callback to receive subsequent messages. Another possible solution is to have the listener start a timer that, when triggered, sends a message to the listener's destination in order to wake it up and complete the outstanding work that has not yet been acknowledged—provided the wake-up message can be directed solely at the correct listener.

Using AUTO_ACK for Non-Durable Subscribers

Non-durable, non-transactional topic subscribers are optimized to store local copies of the message on the client side, thus reducing network overhead when acknowledgements are being issued. This optimization yields a 10-20% performance improvement, where the improvement is more evident under higher subscriber loads.

One side effect of this optimization, particularly for high numbers of concurrent topic subscribers, is the overhead of client-side garbage collection, which can degrade performance for message subscriptions. To prevent such degradation, we recommended allocating a larger heap size on the subscriber client. For example, in a test of 100 concurrent subscribers running in 10 JVMs, it was found that giving clients an initial and maximum heap size of 64MB for each JVM was sufficient.

Alternative Qualities of Service, Multicast and No-Acknowledge

WebLogic JMS provides alternative qualities of service (QOS) extensions that can aid performance. This section contains the following topics:

Using MULTICAST_NO_ACKNOWLEDGE

Non-durable topic subscribers can subscribe to messages using MULTICAST_NO_ACKNOWLEDGE. If a topic has such subscribers, the JMS server will broadcast messages to them using multicast mode. Multicast improves performance considerably and provides linear scalability, as the network only needs to handle only one message, regardless of the number of subscribers, rather than one message per subscriber. Multicast messages may be lost if the network is congested, or if the client falls behind in processing them. Calls to recover() or acknowledge() have no effect on multicast messages.

Note:

On the client side, each multicasting session requires a dedicated thread to retrieve messages off the multicast socket. Therefore, you should increase the JMS client-side thread pool size to adjust for this.

This QOS extension has the same level of guarantee as some JMS implementations default QOS from vendors other than Oracle WebLogic Server for non-durable topic subscriptions. The JMS 1.1 specification specifically allows non-durable topic messages to be dropped (deleted) if the subscriber is not ready for them. WebLogic JMS actually has a higher QOS for non-durable topic subscriptions by default than the JMS 1.1 specification requires.

Using NO_ACKNOWLEDGE

A no-acknowledge delivery mode implies that the server gives messages to consumers, but does not expect acknowledge to be called. Instead, the server pre-acknowledges the message. In this acknowledge mode, calls to recover will not work, as the message is already acknowledged. This mode saves the overhead of an additional network call to acknowledge, at the expense of possibly losing a message when a server failure, a network failure, or a client failure occurs.

Note:

If an asynchronous client calls close() in this scenario, all messages in the asynchronous pipeline are lost.

Asynchronous consumers that use a NO_ACKNOWLEDGE QOS may wish to tune down their message pipeline size in order to reduce the number of lost messages in the event of a crash.

Avoid Multi-threading

The JMS Specification, at http://www.oracle.com/technetwork/java/jms/index.html, states that multi-threading a session, producer, consumer, or message method results in undefined behavior except when calling close(). For this release, if WebLogic JMS determines that you created a multi-threaded producer, the server instance throws a JMSException. If your application is thread limited, try increasing the number of producers and sessions.

Using the JMSXUserID Property

For WebLogic Server 9.0 and later, you can configure a JMS connection factory and/or destination to automatically propagate the message sender's authenticated username. The username is placed in a javax.jms.Message property named JMSXUserID.

Consider the following points when using the JMSXUserID property in your application.

  • While the JMS specification makes some mention of JMSXUserID, the behavior is lightly defined and so will likely be different for different JMS vendors.

  • The JMSXUserID is based on the credential of the thread an application uses to create the JMS producer. It does not derive from the credential that is on a thread during the JMS send call itself.

  • JMS will ignore or override any attempt by an application to directly set JMSXUserID (for example, javax.jms.Message.setXXXProperty() will not work).

  • JMS messages are not signed or encrypted (similar to any RMI/EJB call). Therefore, fully secure transfers of the JMSXUserID require sending the message through secure protocols (for example, t3s or https).

  • WebLogic Store-and-Forward agents do not propagate JMSXUserID (they null it out).

  • WebLogic Messaging bridges will propagate JMSXUserID of the source destination's message if the messaging bridges are both are forwarding to a 9.0 or later JMS server and are configured to Preserve Message Properties. Otherwise, the forwarded message will either contain no username or the username used by the bridge sender. The latter behavior is determined by the configuration of the bridge sender's connection factory and destination.

  • The WebLogic JMS WLMessageProducer.forward() extension can forward a received message's JMSXUserID.

    Note:

    The JMSXUserID interop behavior for WebLogic JMS clients prior to 9.0 is undetermined.

For instructions on setting the JMSXUserID property on a connection factory or destination, see the following topics in the WebLogic Server Administration Console online help:

Performance and Tuning

For information on how to get the most out of your applications, implement the performance tuning features available with WebLogic JMS at "Tuning WebLogic JMS" in Tuning Performance of Oracle WebLogic Server.