消息中间件(MOM): 交换信息的一类软件。当分布式应用使用MOM后,分布式应用会得到的好处是:松耦合、可靠、可扩展、安全。
ActiveMQ 就是一个MOM 。ActiveMQ 遵守JMS 规范,使用ActiveMQ 是JMS的一个提供者。
一 ActiveMQ 的特征
1 遵守JMS 规范
2 连接性:支持多协议传递消息,可以和jee容器整合。
3 可插拔的持久化和安全组件:消息可以被持久化(存储),可以支持KahaDB和jdbc;可以完成验证用户的身份和授权。
4 使用java创建消息应用程序
5 可以与Java 应用服务器整合
6 客户端API:客户端可以使用不同的语言来编写。
7 Broker 集群:这个的borker 我个人认为就是一个mq 的消息处理程序,就是一群mq 一起工作。
8 一些高级的broker 特性和客户端选项
9 动态的简化管理:提供管理工具,管理active mq
二 什么时候应用Active MQ
1 使用不同语言写的应用程序之间的整合:ActiveMQ broker 是使用Java 语言写的,但是mq 也支持不同的语言访问mq。
2 替代RPC
3 应用之间松散耦合
4 可以作为一个事件驱动架构的基础
总体上看,消息是一个业务信息单元,应用之间通过MOM 传递这个消息。应用通过MOM的destinations来发送和接收消息。
异步消息:发送者和接收者不用同时在线,并且发送者和接收者相互不知道。
MOMs 添加了额外的通用功能:
1 消息的持久化;
2 当使用不稳定的或者慢的网络时,可以保证稳定的通信;
2 复杂的消息路由,包括把一个消息发送给多个接收者。
3 消息转换,可以实现消息格式的转换。当两个程序不能处理同一格式的消息时使用。
三 JMS 中定义的概念:
1 JMS 客户端:一个应用100% 使用java 写。 主要使用MessageProducer 和MessageConsumer 接口。
2 非JMS 客户端:使用本地API 的应用。
3 JMS producer: 创建和发送jms 的一个应用
4 JMS consumer: 接收和处理jms 的一个应用
5 JMS 提供者:实现JMS 接口,被客户端使用来发送和接收消息的。这个就是active mq。
6 JMS 消息:JMS 发送和接收的信息。
7 JMS 域:两种消息的类型,point-to-point 和 publish-subscribe
8 被管理的对象:提前被配置好的JMS 对象们(实例),这些对象包含了用户的特殊配置。一般客户端可以通过JNDI访问这些对象。
9 连接工厂:客户端使用连接工厂来创建连接,客户端通过连接访问JSM 提供者(就是Active mq)。
10 Destination(目的地):发送消息给这个对象,或者通过这个对象接收消息。
MessageProducer 类提供了以下功能: 1 发送消息 2 设置消息头,包括JMSDeliveryMode,JMSPriority 和JMSExpiration(通过get/setTimeToLive 方法)。
MessageConsumer 类可以从一个目的地接收消息,可以使用receive() 方法同步的接收信息,也可以使用MessageListener 对象异步的接收信息。
四 JMS 消息的结构
JMS 消息由两部分组成,消息头和负载。消息头提供元数据,该元数据被JMS 提供者和两头的客户端使用(这里指的是接收和发送消息的客户端们)。负载实际上就是消息体,消息体可以携带文本数据或二进制数据。参见图1.
图 1
消息头中的信息很复杂,可以分为两种:
1 headers(头信息): JMS 规范定义的头信息
2 properties(属性信息):客户可以定义的头信息
1 我们先介绍headers(头信息):
当客户端调用send() 方法时,被自动设置的头信息为:
JMSDestination:这个消息要发送到的目的地。这个对象,对于想从这个destination 中得到信息的客户端很重要。
JMSDeliveryMode:JMS 支持两种发送消息的模式,持久的和非持久的。默认的方式是持久的。
持久表示的含义:建议JMS 提供者持久化JMS 消息。如果提供者失败了,消息不会丢失。一个JMS 提供者必须保证发送一个持久的信息“一次且仅一次”。这样设置,表明可靠性比性能更重要。
非持久表示的含义:建议JMS 的提供者不要持久化消息。一个提供者必须发送一个非持久化的消息“最多一次”。换句话说,就是如果提供者失败,消息可能丢失,但是一个不能被发送两次。
JMSExpiration:消息过期的时间。这个头信息被设置,防止JMS 提供者发送过期的消息。可以通过MessageProducer.setTimeToLive() 来设定全局的值。也可以通过MessageProducer.send() 方法来为特殊消息指定特殊的值。默认值为0,表示消息不会过期。这个消息头的值=“time-to-live” + “当前时间”,时间是以GMT(格林尼治标准时间)为基准。
JMSMesaageID:每一个消息都有一个唯一的ID。这个ID 由JMS 提供者分配。可以在客户端通过MessageProducer.setDisableMessageID() 方法来建议提供者不要设定ID 值。当性能重要的时候,可以考虑这个设置。
JMSPriority:指定消息的重要等级。这个头信息通过producer 设定全局的值,可以通过producer 的send() 方法指定一个消息的特殊值。一共10个等级,从0到9。0到4 为一般等级,5-9 为加速等级。
JMSTimestamp:这个值记录了消息被从producer 发送给JMS 提供者的时间。可以通过MessageProducer.setDisableMassageTimestamp() 方法来建议JMS 提供者不要设定这个值。这个值可能是一个提供者的消息ID,可能是一个程序设定的字符串,或者一个本地的byte[] 值。
客户端可以设定的头信息为:
JMSCorrelationID:为当前的消息关联一个先前的消息,这里的值就是先前消息的ID。这个头信息通常被用来完成request 和response (请求和相应)消息的关联。
JMSReplyTo:指定一个destination(目的地),一个回复信息应该被发送到这个目的地。这个一般被于请求/回答(request/reply)风格的消息。
JMSType:
JMS 提供者可以设定的:
JMSRedelivered:
2 properties(属性) 头信息
JMS 规范保留了JMSX property 名称前缀,所以看到JMSX 开头的属性,都是JMS 规范定义的。
Message 类提供了一些方法,可以让客户端定义自己的属性。
举一个例子说明头信息的用途:
消息选择器(Message selectors)
有时候JMS 客户端只想接收目的地(destination)的特定信息,可以通过消息选择器来实现这个需求。定义选择器时,必须根据headers 和properties 中的信息
定义。
String selector = "SYMBOL = 'AAPL'";
MessageConsumer consumer = session.createConsumer(destination, selector);
表示consumer 指接收properties 中,SYMBOL=AAPL 的消息。
3 消息体:真正存放数据和信息的地方
JMS 定义了6种类型的消息体,消息体有时也被称为负载。通过使用下列对象,可以通过负载发送数据和信息。
Message 低级的消息类型,发送一个没有消息体的消息。一般用于简单的事件通知
TextMessage 负载是一个字符串,一般用于发送简单文本和XML 数据。
MapMessage 负载是一个键值对,名字和值都必须是Java的基本类型
ByteMessage 负载是一个二进制数组
StreamMessage 负载是一个基本java 类型的流
ObjectMessage 负载是一个可序列化的Java 对象。通常是一个复杂的java 对象。也支持Java 集合。
五 JMS 域
JMS 有两种方式,点对点(point-to-point),发布/订阅。
(1)点对点的方式:可以把目的地(destinations) 想像成队列,通过使用这个队列,消息可以同步或异步的传递。每个被队列接收的消息都被一次仅一次给消费者(consumer)。参见图2.
同步接收消息: MessageConsumer.recerve();
异步接收消息: MessageConsumer.setMessageListener(MessageListener);
发送到队列的消息将被存储。直到消息被传送给消费者或者消息过期,这个消息才被从队列中移除。
多个生产者,多个消费者(consumer) 能够注册到同一个队列上。但是一个消息只能被一个consumer 接收。(这是点对点的由来)
图 2
(2)发布/订阅(Publish/Subscribe) 方式:把目的地看作为topics(主题)。任何发送给主题的消息都被自动的传递给所有的消费者。这个消息域和邮件列表和相似。参加图3.
同步接收消息: MessageConsumer.recerve();
异步接收消息: MessageConsumer.setMessageListener(MessageListener);
主题不会保存消息,除非你告诉它保存。可以通过用“耐久订阅(durable subsrciption)”来实现保存消息。当你使用耐久订阅时,一个订阅者断开了与JMS 提供者的连接,JMS 提供者负责存储应该给这个订阅者的消息,当这个订阅者重新建立连接后,订阅者可以接收到它应该接收到的消息(如果在断开的过程中,如果消息已经过期,那么消息将不会被接收)。
图 3
注意:消息的耐久性(durability)与持久性(persistence)的区别
1 耐久性只能被应用到pub/sub 域中。
2 消息持久性独立于消息域。消息持久性是一个服务质量属性,用来显示JMS 应用有能力处理JMS 提供者失败的事件。设置方法是消息的产生者的setDeliveryMode() 方法,参数为JMSDeliveryMode.PERSISTENT or JMSDeliveryMode.NON-PERSISTENT。
(3)Request/reply 消息域:
尽管JMS 没有正式定义Request/reply 消息域,但是JMS 提供了一些消息头和一对类来实现request/reply 消息。
六 被管理的对象
被管理的对象包含提供者的特殊配置信息,由JMS 管理员创建它。然后,JMS 客户端来使用它。它被用来隐藏JMS 提供者的细节,抽象了JMS 提供者的管理任务。一般情况下,客户端使用JNDI 来得到被管理的对象。
JMS 规范定义了两种类型的被管理对象:ConnectionFactory 和Destination。
ConnectionFactory(连接工厂):JMS 客户端使用ConnectionFactory 对象创建一个到JMS 提供者的连接。连接通常表示一个被打开的TCP socket,所以一个连接是消耗系统资源的。可以考虑使用连接池来重用连接。这里的连接和JDBC 的连接很相似。客户端可以使用JMS 连接创建javax.jms.Session 对象,这个对象表示客户端与JMS 提供者的一个相互作用。
Destination(目的地):目的地封装了一个提供者的地址,消息可以发送到这个地址,也可以从这个地址被读取。虽然我们通过Session 对象创建Destinations,但是Destination 对象的生命周期和创建Session 的Connection 对象的生命周期是一致的。
参考:本文是读<<ActiveMQ in Action>> 第2章后,做的笔记。可以在网络上下载到这本书。