2013年6月12日星期三

第二章:理解面向消息的中间件和jms


2.1 企业级消息简介
企业级消息是在分布式系统之间进行传递的数据。当前有很多方法可以完成这一目的:
1.远程过程调用,例如COMCORBADCEEJB
2.事件通知、进程间通信和消息队列等,依赖于具体操作系统的数据交换方式,例如先进先出(FIFO)缓冲、消息队列、管道(pipes)、信号(signals)、套接字(sockets)等。
3.一类消息中间件,能够提供异步的、可靠的消息队列,例如:MQSeriesSonicMQTIBCOApache ActiveMQ等用于企业级应用程序集成(EAI Enterprise Application Interation)的中间件
很多种消息传输的方式都能够完成应用程序间的通信,基于中间件的消息通信是本文的关注重点。基于中间件的通信方式具有很好的数据交互和传输能力,并且熟练的管理分布式系统中的数据格式化以屏蔽不同的操作系统、协议和编程语言带来的通信问题。此外,成熟的消息传输和路由机制也正不断的结合进来,这种系统被人们称为面向消息的中间件。
2.2 面向消息的中间件
面向消息的中间件(MOM)可以描述为一类能够为分布式应用程序或者异构的操作系统提供松散耦合的、可靠的、可扩展的、安全的消息通信软件。MOMs在分布式计算中有着重要的地位。使用MOMs利用不同厂商提供的APIs完成应用程序和应用程序之间的通信,处理企业级领域的通信问题。
MOM在消息的发送者和消息的接收者之间提供一个消息中介的作用,这种中介提供了高层次松散耦合的企业级消息应用。MOM不仅可以用于应用程序和应用程序之间的通信,也可以应用于应用程序(终端)和主框架之间的通信。如图所示:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)
在更高的层次上来理解,消息就是一组由一个应用程序通过MOM发向另一个应用程序的业务信息。通过MOM发送和接收消息的应用程序被称为目的地(destinations),消息将会投递给链接或者订阅它的接收者。消息的发送者和接收者在消息发送期间无需跟MOM保持链接状态,这是松散耦合机制的根本。发送者无需对接收者做任何假设和了解,接收者也无需知道发送者的相关信息。这就是一种异步消息通信。
MOMs提供了很多企业级消息没有的特性,包括消息持久化、高延时和不可靠链接的消息加强发送、复杂消息的路由、消息事务等。消息持久化减轻了不可靠链接的处理代价,当消息的接收者出现简单错误时不会影响到消息发送者的状态。复杂消息的路由可以将一条消息发送给多个接收者,消息路由基于消息的属性和消息的内容。消息事务确保两个应用不会重复处理同一个消息。
此外,很多的市场上的MOMs支持多种链接协议。很多厂商都支持HTTP/SmulticastSSLTCP/IPUDP等,很多厂商还提供对多语言的支持。
2.3 JMS
JMS屏蔽了不同厂商提供的MOM APIs的异构性,为企业级消息通信提供了标准的APIJMS指在为使用Java语言进行编程并且使用面向消息中间件的程序提供一个标准的APIJMS自身不是一个MOM(面向消息的中间件),它是一个在客户端和MOMs之间提供的抽象接口,就像JDBC为数据库和客户端提供的编程接口一样。如下图所示:

JMS第一个版本是1998年制定的,最后一个版本是在2002年制定的JMS1.1。在标准APIJMS定义了很多的概念:
  JMS Client –一个完全使用JMS编写的发送和接收消息的客户端(很显然,这个客户端必然用Java实现)。
Non-JMS Client—使用JMS提供商提供的本地客户端API编写收发消息,而不是JMS提供的API
JMS Producer—一个创建和发送JMS消息的客户端应用。
JMS Consumer – 一个接收并处理JMS消息的客户端应用。
JMS Provider –完全使用JavaJMS接口的一个实现。
 JMS Message – JMS基础概念,JMS客户端发送和接收的消息。
 JMS Domains – 两种类型的消息,分别为点对点(PTPpoint-to-point)和发布/订阅(publish/subscribe)
 Administered Object –JMS对象进行配置。
Connection Factory—客户端使用Connection Factory 创建与JMS Provider的连接。
Destination – 用于收发消息的客户端
2.3.1 Messaging Clients
正如前面提到的,有两种JMS客户端,分别为clients-JMSnon-JMS Client
2.3.1.1 JMS Clients
JMS Clients使用JMS API连接到消息服务器。许多JMS服务提供者会提供比JMS规范更多的特性,如果您使用了这些特性,当您更换JMS服务提供者时,将不得不重构程序代码,以适应新的JMS服务提供者。
JMS客户端工具包提供了MessageProducerMessageConsumer这两个接口,所有的JMS提供商都必须提供对这些接口的实现。发送消息的JMS客户端称为消息发送者(Producer),接收消息的客户端称为消费者(consumer)。在很多情况下,一个JMS客户端即要充当发送者,也要充当接收者。
2.3.1.1.1 JMS Producer
JMS客户端使用MessageProducer来发送消息到目的地。在使用session.createProducer()创建Producer的时候可以设置默认的目标(destination)。但是可以通过使用MessageProducer.send()方法进行重写。MessageProducer接口描述如下:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)


MessageProducer不仅提供了发送消息的方法,同时也提供了设置消息头部的方法(JMSDeliveryModeJMSPriorityJMSExpirationget/setTimeToLive())).
2.3.1.1.2 JMS Consumer
JMS客户端使用JMS MessageConsumer类来接收和处理消息。可以使用receive方法处理同步消息,也可以采用MessageListener的实现来处理异步消息。下面是MessageConsumer的接口:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)

MessageConsumer没有设置目标(destination)的方法,在消费者被创建的时候(session.createConsumer())需要提供默认的目标。
2.3.1.2 Non-JMS Clients
Non-JMS就是不使用JMS接口的客户端。
2.3.2 The JMS Provider
JMS Provider 就是实现了JMS接口的MOM提供商的实现。
2.3.3 JMS 消息的分析
JMS允许消息承载多种形式的信息,可以是文本形式,也可以是二进制的数据或者是在头部提供特殊的属性设置。如下图:
activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)

JMS消息主要分为三个部分,分别为头部(headers),属性对(properties)和实际负载(payLoad)。头部提供消息的元数据(主要是对消息的一些控制信息),可以被客户端和JMS提供者来使用。Properties是可选字段,为消息的消费者提供附加的信息。实际负载是消息的主体,可以是文本数据也可以是二进制数据等。
2.3.3.1 JMS Message Headers
可以被客户端的send方法自动设置的headers属性
JMSDestination消息投递的目标。
JMSDeliveryMode—JMS提供了两种类型的发送模式:持久化(persistent)和非持久化。默认情况下发送模式为持久化。他们分别有不同的开销和可靠性。
         Persistent –建议JMS提供者持久化消息,当提供者出现错误时,消息不会丢失。JMS提供者必须保证消息的once-and-only-once语义。消息持久化会导致额外的储存开销但是具有较高的可靠性。
         Non-Persistent 建议JMS提供者不要持久化消息。保证了at-most-once语义。如果JMS提供者出现错误,消息很可能会丢失。这种方式具有开销小可靠性低的特点。
发送模式是由消息提供者设置的,并且应用于该Producer发送的所有消息。但是这种发送模式可以通过重写个别的发送方法,对个别消息进行修改。
JMSExpiration 消息过期的时间。可以阻止过期的消息被再次投递。可以使用MessageProducer.setTimeToLive()设置默认的过期时间,也可以使用MessageProducer.send()方法设置个别消息的过期时间。默认情况下,time-to-live设置是0,表示消息永不过期。
这个头部属性可以应用于时间敏感的消息。要注意的是,JMS Providers不会发送过期的消息,所以JMS客户端应该不会处理已经过期的消息。
JMSMessageID  一个能够唯一标识一条消息的字符串,这个字符串被JMS提供者签发,并且必须以“ID:”开始。这个MessageID可以用于消息处理或者消息处理机制中的历史目的。由于消息ID可能导致一些额外的开销,所以消息提供者可以通过使用MessageProducer.setDisableMessageID()建议JMS Provider JMS应用无需依赖这样一个消息ID。如果JMS 提供者接受这个建议,那么消息ID选项必然被设置为null。但是,需要注意的是JMS提供者很可能忽略您的设置,无论如何都会继续签发messageID
JMSPriority 用来为消息签发优先级。这个消息头属性仍然是由消息提供者设置。一旦消息优先级被设置,消息发送者将会对该消息提供者发出的所有消息使用这一优先级。这个优先级同样也可以被个别消息重写。JMS定义了10个等级的优先级策略,0表示最低优先级,9表示最高优先级。等级策略如下:
Priorities0-4 适用于日常普通消息。
Priorities 5-9 适用于紧急消息。
JMS提供者没有强制性的要求必须实现消息的次序,尽管应该如此。它们只是尽量的将优先级高的消息先投递。
JMSTimestamp  这个头选项标志了消息提供者向JMS提供者发送消息的时间。时间是以标准Java的毫秒值表示。可以使用MessageProducer.setDisableMessageTimestamp()取消JMS提供者为JMSTimestamp赋值。如果JMS提供者接受了这项设置,那么JMSTimestamp选项将被赋值为0

客户端可选的头选项:
JMSCorrelationID 用于当前的消息与之前提交的消息建立关联。这个头选项往往用于将一个请求消息(Request)关联一个响应消息(Response)。JMSCorrelationID可以是下面值中的一种:
         与一个提供者(Provider-specific)相关的消息ID
         与一个应用程序相关(application-specific)的字符串
         一个本地提供者(Provider-native)的byte[]值。
Provider-specific类型的MessageID将会以“ID:”为前缀,而application-specific必须不能以“ID:”为前缀。如果JMS Provider支持接受本地相关的ID,那么JMS 客户端可能需要签发一个被non-JMS客户端匹配的specific JMSCorrelationID 值,但这不是必须的。
JMSReplyTo 用来指定一个反馈消息投递的目的地。这个头部属性经常用于request/reply类型的消息。使用这个头部属性的消息希望接收者能够给出一个反馈信息,但是这是可选的。客户端决定是否发送反馈消息。
JMSType 在语义上定义消息类型,这个头部属性只有很少的厂商提供支持,并且与实际负载的Java类型没有关系。
JMS 提供者设定的头部可选字段:
JMSRedelivered 用来指明已经发送的消息但是没有获取确认消息。当消费者反馈确认消息失败时会出现这种情况。
2.3.3.2 JMS Message Properties
Properties是附加的头部信息,可以更加详细的定制消息。JMS为客户属性设置提供了多种方法。JMS提供使用Java原始模型设置属性的方法包括BooleanbyteshortintlongfloatdoubleString Object等。Message interface描述如下:


activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)

activemq <wbr>in <wbr>action <wbr>第二章:理解面向消息的中间件和JMS <wbr>(第一部分)


需要注意的是getPropertyNames方法和propertyExists方法。可以使用getPropertyNames方法返回一个给定消息的具有全部属性的Enumeration,然后简单遍历这些属性。propertyExists可以判定给定的属性字段是否存在。
有三种类型的属性:任意的或称客户属性(arbitry or custom Properties),JMS定义的属性和提供商属性(Provider-specific)。
2.3.3.2.1 客户属性
这些属性是任意的,并且是由JMS应用定义的。开发者可以根据需要任意的定义消息的相关属性。
2.3.3.2.2 JMS-Defined 属性
JMSX为前缀的JMS定义的属性。对这些属性的支持是可选的。
JMSXAppID:标识发送消息的应用程序。
JMSXConsumerTXID:标识处理这条消息的事物(transaction)。
JMSXDeliveryCount:消息被尝试发送的次数。
JMSXGroupID:标识组消息。
JMSXGroupSeq:本条消息在消息组中的次序。
JMSXProducerTXID:产生消息的事物(transaction
JMSXRvcTimestampJMS提供者向消费者发送消息的时间。
JMSXState:定义Provider-specific的状态
JMSXUserID:标识发送消息的用户。
仅有JMSGroupIDJMSXGroupSeq属性被提倡使用。这两个属性很可能在发送组消息时使用。
2.3.3.2.3 Provider-specific Properties
JMS_<vendor-name>为前缀的属性为Provider-specific属性。不同的厂商根据需要提供各自的属性。这些属性在non-JMS客户端使用,您不应该在JMS-to-JMS消息通信中使用这些属性。

头部和头部属性对于客户端过滤消息是很重要的。

没有评论:

发表评论