松耦合,双方/多方并不知晓对方的存在,异步操作
1.远程调用(RPC)的实现: COM, CORBA, DCE, and EJB
2.事件通知,内部进程通信,植入系统的消息队列实现: FIFO buffers, message queues, pipes, signals, sockets, and others3.一系列用于提供异步,可靠消息队列服务的中间件实现:IBM WebSphere MQ, SonicMQ, TIBCO Rendezvous, and Apache ActiveMQ, 通常用于企业级应用整合(EAI)
什么是message-oriented 中间件?
Message-oriented middleware ( (MOM) 是一系列作用于在一些分布式应用或者系统间,提供异步,松耦合,具有良好可靠性,扩展性以及安全的通信服务. MOMs是在分布式计算中的一个重要理念. 它允许使用应用与应用之间使用各自供应商提供的API来通讯,并可以在分布式系统空间中处理大量事情.
MOM背后的大体思想是让其扮演消息发送,接收方之间的消息中间人.中间人提供完整的松耦合级别方案. 下图向我们描绘了MOM是如何作为中间链接,消息桥梁,在应用与主机,应用到应用间发挥作用.
什么是Java Message Service(JMS)?
The Java Message Service (JMS) moved beyond vender-centric MOM APIs to provide an API for enterprise messaging. JMS aims to provide a standardized API to send and receive messages using the Java programming language in a vendor-neutral manner.The JMS API minimizes the amount of enterprise messaging knowledge a Java program-mer is required to possess in order to develop complex messaging applications, while still maintaining a certain amount of portability across JMS provider implementations.
JMS本身不是MOM. 它是一套用于抽象消息客户端与MOM之间交互的API.就像 JDBC 抽象了关系数据间的通信那样.Figure 2.4 shows at a high level how JMS provides an API used by messaging clients to interact with MOM-specific JMS providers, which handle interaction with the vendor-specific MOM. The JMS API lowers the barrier to creating enterprise messaging applications. It also eases the portability to other JMS providers.
In standardizing the API, JMS formally defined many concepts and artifacts from the world of messaging:
JMS client —An application is written using 100% pure Java to send and receivemessages.
Non-JMS client —An application is written using the JMS provider’s native client API to send and receive messages instead of JMS.
JMS producer —A client application that creates and sends JMS messages.
JMS consumer—A client application that receives and processes JMS messages.
JMS provider—The implementation of the JMS interfaces, which is ideally written in 100% pure Java.
JMS message—The most fundamental concept of JMS; sent and received by JMS clients.
JMS domains—The two styles of messaging that include point-to-point and publish/subscribe.
Administered objects —Preconfigured JMS objects that contain provider-specific configuration data for use by clients. These objects are typically accessible by clients via JNDI.
Connection factory—Clients use a connection factory to create connections to the JMS provider.
Destination—An object to which messages are addressed and sent and from which messages are received.
JMS spec 定义了两种客户端—JMS clients 和 non-JMS clients.
Headers set automatically by the client’s send() method:
JMSDestination—The destination to which the message is being sent. This is valu-able for clients who consume messages from more than one destination.
JMSDeliveryMode—JMS supports two types of delivery modes for messages: persis-tent and nonpersistent. The default delivery mode is persistent. Each deliverymode incurs its own overhead and implies a particular level of reliability.
* Persistent—Advises the JMS provider to persist the message so it’s not lost ifthe provider fails. A JMS provider must deliver a persistent message onceand only once. In other words, if the JMS provider fails, the message won’t belost and won’t be delivered more than once. Persistent messages incurmore overhead due to the need to store the message, and value reliabilityover performance.
* Nonpersistent—Instructs the JMS provider not to persist the message. A JMSprovider must deliver a nonpersistent message at most once. In other words,if the JMS provider fails, the message may be lost, but it won’t be deliveredtwice. Nonpersistent messages incur less overhead and value performanceover reliability.
The delivery mode is set on the producer and is applied to all messages sent fromthat producer. But the delivery mode can be overridden for individual messages.
JMSExpiration—The time that a message will expire. This header is used to pre-vent delivery of a message after it has expired. The expiration value for mes-sages can be set using either the MessageProducer.setTimeToLive() methodto set the time-to-live globally for all messages sent from that producer, or usingone of the MessageProducer.send() methods to set the time-to-live locally foreach message that is sent. Calling any of these methods sets the default lengthof time in milliseconds that a message should be considered usable, althoughthe MessageProducer.send() methods take precedence.
The JMSExpiration message header is calculated by adding the time-to-live tothe current time in GMT. By default the time-to-live is zero, meaning that themessage won’t expire. If a time-to-live isn’t specified, the default value is usedand the message won’t expire. If the time-to-live is explicitly specified as zero,then the same is true and the message will not expire.
This header can be valuable for time-sensitive messages. But be aware thatJMS providers shouldn’t deliver messages that have expired, and JMS clientsshould be written so as to not process messages that have expired.
JMSMessageID —A string that uniquely identifies a message that’s assigned by theJMS provider and must begin with ID. The message ID can be used for messageprocessing or for historical purposes in a message storage mechanism. Because message IDs can cause the JMS provider to incur some overhead, the producercan advise the JMS provider that the JMS application doesn’t depend on thevalue of this header via the MessageProducer.setDisableMessageID() method.If the JMS provider accepts this advice, the message ID must be set to null. Beaware that a JMS provider may ignore this call and assign a message ID anyway.
JMSPriority—Used to assign a level of importance to a message. This header isalso set on the message producer. Once the priority is set on a producer, itapplies to all messages sent from that producer. The priority can be overriddenfor individual messages. JMS defines 10 levels of message priority, ranging from0 (the lowest) to 9 (the highest):
* Priorities 0–4—These priorities are finer granularities of the normal priority.
* Priorities 5–9—These priorities are finer granularities of expedited priority.
JMS providers aren’t required to implement message ordering, although mostdo. They should simply attempt to deliver higher-priority messages beforelower-priority messages.
JMSTimestamp—This header denotes the time the message was sent by the pro-ducer to the JMS provider. The value of this header uses the standard Java millistime value. Similar to the JMSMessageID header, the producer may advise theJMS provider that the JMSTimestamp header isn’t needed via the Message-Producer.setDisableMessageTimestamp() method. If the JMS provideraccepts this advice, it must set the JMSTimestamp to zero.
Header set optionally by the client:
JMSCorrelationID —Used to associate the current message with a previous mes-sage. This header is commonly used to associate a response message with arequest message. The value of the JMSCorrelationID can be one of the following:
* A provider-specific message ID
* An application-specific String
* A provider-native byte[] value
The provider-specific message ID will begin with the ID: prefix, whereas theapplication-specific String must not start with the ID: prefix. If a JMS providersupports the concept of a native correlation ID, a JMS client may need to assigna specific JMSCorrelationID value to match that expected by non-JMS clients, butthis isn’t a requirement.
JMSReplyTo —Used to specify a destination where a reply should be sent. Thisheader is commonly used for request/reply style of messaging. Messages sentwith this header populated typically expect a response, but it’s actually optional.The client must make the decision to respond or not.
JMSType—Used to semantically identify the message type. This header is used byfew vendors and has nothing to do with the payload Java type of the message.
Headers set optionally by the JMS provider:
JMSRedelivered—Used to indicate the liklihood that a message was previouslydelivered but not acknowledged. This can happen if a consumer fails toacknowledge delivery, or if the JMS provider hasn’t been notified of deliverysuch as an exception being thrown that prevents the acknowledgement fromreaching the provider.
JMS MESSAGE 属性
The JMS specification
JMS-DEFINED PROPERTIES
The JMS spec reserves the JMSX property name prefix for JMS-defined properties, andsupport for these properties is optional:
JMSXAppID —Identifies the application sending the message
JMSXConsumerTXID —The transaction identifier for the transaction within which this message was consumed
JMSXDeliveryCount—The number of message delivery attempts
JMSXGroupID —The message group of which this message is a part
JMSXGroupSeq—The sequence number of this message within the group
JMSXProducerTXID —The transaction identifier for the transaction within whichthis message was produced
JMSXRcvTimestamp —The time the JMS provider delivered the message to theconsumer
JMSXState—Used to define a provider-specific state
JMSXUserID —Identifies the user sending the message
The only recommendation provided by the spec for use of these properties is for the JMSXGroupID and JMSXGroupSeq properties, and that these properties should be usedby clients when grouping messages and/or grouping messages in a particular order.
PROVIDER-SPECIFIC PROPERTIES
The JMS spec reserves the JMS_<vendor-name> property name prefix for provider-specific properties. Each provider defines its own value for the <vendor-name> place-holder. These are most typically used for provider-specific non-JMS clients andshouldn’t be used for JMS-to-JMS messaging.
Now that JMS headers and properties on messages have been discussed, for whatexactly are they used? Headers and properties are important when it comes to filter-ing the messages received by a client subscribed to a destination.
Message selectors
Item | Values |
---|---|
Literals Identifiers Operators |
Booleans TRUE/FALSE; numbers such as 5, -10, +34; numbers with decimal or scientific notation such as 43.3E7, +10.5239 A header or property field AND, OR, LIKE, BETWEEN, =, <>, <, >, <=, =>, +, -, *, /, IS NULL, IS NOT NULL |
The items shown in table 2.1 are used to create queries against message headers and
properties. Consider the message defined in the next listing. This message defines two
properties that will be used for filtering messages in the example that follows.
Listing 2.4
A JMS message with custom properties
....
....
Filter messages using both the SYMBOL and PRICE headers
...
...
This example specifies a selector to match messages for Apple, Inc. whose price isgreater than the previous price. This selector will show stock messages whose price isincreasing. But what if you want to take into account the timeliness of stock messagesin addition to the price and symbol? Consider the next example.
Filter messages using headers
MESSAGE BODY JMS defines six Java types for the message body, also known as the payload. Throughthe use of these objects, data and information can be sent via the message payload. Message —The base message type. Used to send a message with no payload, only headers and properties. Typically used for simple event notification.
TextMessage —A message whose payload is a String. Commonly used to send simple textual and XML data.
MapMessage —Uses a set of name/value pairs as its payload. The names are of type String and the values are a Java primitive type.
BytesMessage —Used to contain an array of uninterpreted bytes as the payload.
StreamMessage—A message with a payload containing a stream of primitive Java types that’s filled and read sequentially.
ObjectMessage—Used to hold a serializable Java object as its payload. Usually used for complex Java objects. Also supports Java collections.
As noted earlier, the creation of JMS was a group effort, and the group contained vendors of messaging implementations. It was the influence of existing messaging implementations that resulted in JMS identifying two styles of messaging (or domains as they’re referred to in the spec)—point-to-point and publish/subscribe.
THE POINT-TO-POINT DOMAIN
The point-to-point (PTP) messaging domain uses destinations known as queues.Through the use of queues, messages are sent and received either synchronously orasynchronously. Each message received on the queue is delivered once and onlyonce to a single consumer. This is similar to a person-to-person email sent through amail server. Consumers receive messages from the queue either synchronously usingthe MessageConsumer.receive() method or asynchronously by registering aMessageListener implementation using the MessageConsumer.setMessage-Listener() method. The queue stores all messages until they’re delivered or untilthey expire.
Multiple consumers can be registered on a single queue as shown in figure 2.6, butonly one consumer will receive a given message and then it’s up to that consumer toacknowledge the message. Note that the message in figure 2.6 is sent from a singleproducer and is delivered to a single consumer, not all consumers. As mentioned ear-lier, the JMS provider guarantees the delivery of a message once and only once to thenext available registered consumer. In this regard, the JMS provider is distributing themessages in a sort of round-robin style across all the registered consumers.
THE PUBLISH/SUBSCRIBE DOMAIN
The publish/subscribe (pub/sub) messaging domain uses destinations known astopics. Publishers send messages to the topic and subscribers register to receive mes-sages from the topic. Any messages sent to the topic are automatically delivered to allsubscribers. This messaging domain is similar to subscribing to a mailing list where allsubscribers will receive all messages sent to the mailing list in a one-to-many paradigm.The pub/sub domain is depicted in figure 2.7.
DISTINGUISHING MESSAGE DURABILITY FROM MESSAGE PERSISTENCE
Two points within JMS that are often confused are message durability and messagepersistence. Though they’re similar, there are some semantic differences betweenthem and each has its specific purpose. Message durability can only be achieved withthe pub/sub domain. When clients connect to a topic, they can do so using a durableor a nondurable subscription. Consider the differences between the two:
Durable subscription—A durable subscription is infinite. It’s registered with thetopic subscription to tell the JMS provider to preserve the subscription state inthe event that the subscriber disconnects. If a durable subscriber disconnects,the JMS provider will hold all messages until that subscriber connects again oruntil the subscriber explicitly unsubscribes from the topic.
Nondurable subscription—A nondurable subscription is finite. It’s registered withthe topic subscription to tell the JMS provider to not preserve the subscriptionstate in the event that the subscriber disconnects. If a subscriber disconnects,the JMS provider won’t hold any messages during the disconnection period.Message persistence is independent of the message domain.
Message persistence is aquality of service property used to indicate the JMS application’s ability to handlemissing messages in the event of a JMS provider failure. As discussed previously, thisquality of service is specified on the message producer’s setDeliveryMode methodusing one of the JMSDeliveryMode class’s PERSISTENT or NON-PERSISTENT propertiesas an argument.
Request/reply messaging in JMS applications
Although the JMS spec doesn’t define request/reply messaging as a formal messag-ing domain, it does provide some message headers and a couple of convenienceclasses for handling basic request/reply messaging. Request/reply messaging is anasynchronous back-and-forth conversational pattern utilizing either the PTP domain orthe pub/sub domain through a combination of the JMSReplyTo and JMSCorrelationIDmessage headers and temporary destinations. The JMSReplyTo specifies the desti-nation where a reply should be sent, and the JMSCorrelationID in the reply messagespecifies the JMSMessageID of the request message. These headers are used tolink the reply message(s) to the original request message. Temporary destinationsare those that are created only for the duration of a connection and can only be con-sumed from by the connection that created them. These restrictions make temporarydestinations useful for request/reply.
The convenience classes for handling basic request/reply are the QueueRequestorand the TopicRequestor. These classes provide a request() method that sends arequest message and waits for a reply message through the creation of a temporarydestination where only one reply per request is expected. These classes are usefulonly for this most basic form of request/reply, as shown in figure 2.8—one reply perrequest.
Figure 2.8 depicts the basic request/reply style of messaging between two end-points. This is commonly achieved using the JMSReplyTo message header and a tem-porary queue where the reply message is sent by the receiver and consumed by therequestor. As stated previously, the QueueRequestor and the TopicRequestor canhandle basic request/reply but aren’t designed to handle more complex cases ofrequest/reply, such as a single request and multiple replies from many receivers.Such a sophisticated use case requires you to develop a custom JMS application.