ActiveMQ
1 、JMS (Java Message Service)
1.1 、Java Message Service 即 Java 消息服务
JMS 是 Java 的一套 API 标准,最初的目的是为了使应用程序能够访问现有的 MOM 系统(MOM 是 Message Oriented Middleware 的英文缩写,指的是利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。);后来被许多现有的 MOM 供应商采用,并实现为 MOM 系统。【常见 MOM 系统包括 Apache 的 ActiveMQ、阿里巴巴的 RocketMQ、IBM 的 MQSeries、Microsoft 的 MSMQ、BEA 的 RabbitMQ 等。(并非全部的 MOM 系统都遵循 JMS 规范)】
“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器是在将消息从它的源中发送到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。消息队列的主要特点是异步处理,主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和。如:跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。多个应用之间的耦合,由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送方和接收方同时在线。
在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以 作为集成的方法。应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。跨局域网,甚至跨城市的通讯,比如北京机房与广州机房的应用程序的通信。
2、ActiveMQ 简介
多种语言和协议编写客户端 。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。
应用协议:OpenWire,Stomp REST,WS Notification,XMPP,AMQP
完全支持 JMS1.1 和 J2EE 1.4 规范 (持久化,XA 消息,事务)
对 Spring 的支持 ,ActiveMQ 可以很容易内嵌到使用 Spring 的系统里面去
通过了常见 J2EE 服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA1.5 resource adaptors 的配置,可以让 ActiveMQ 可以自动的部署到任何兼容 J2EE 1.4 商业服务器上
支持多种传送协议 :in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
支持通过 JDBC 和 journal 提供高速的消息持久化
从设计上保证了高性能的集群 ,客户端-服务器,点对点
支持 Ajax
支持与 Axis 的整合, WebServices
可以很容易的调用内嵌 JMS provider,进行测试
2.1、专业名词解释
2.1.1 、Destination 目的地
目的地,JMS Provider(消息中间件)负责维护,用于对 Message 进行管理的对象。MessageProducer 需要指定 Destination 才能发送消息,MessageConsumer 需要指定 Destination才能接收消息。
2.1.2 、Producer 消息生产者
消息生成者(客户端、生成消息),负责发送 Message 到目的地。应用接口为
MessageProducer。在 JMS 规范中,所有的标准定义都在 javax.jms 包中。
2.1.3、Consumer 消费者
消息消费者(处理消息),负责从目的地中消费【处理|监听|订阅】Message。应用接口为 MessageConsumer。
2.1.4 、Message 消息
消息(Message),消息封装一次通信的内容。常见类型有:StreamMessage、BytesMessage、TextMessage、ObjectMessage、MapMessage。
2.1.5、ConnectionFactory 连接工厂
链接工厂, 用于创建链接的工厂类型。 注意,不能和 JDBC 中的 ConnectionFactory 混淆。
2.1.6 、Connection 连接
链接. 用于建立访问 ActiveMQ 连接的类型, 由链接工厂创建. 注意,不能和 JDBC 中的Connection 混淆。
2.1.7、 Session 会话
会话, 一次 持久有效有状态 的访问. 由链接创建. 是具体操作消息的基础支撑。
2.1.8 、Queue & Topic
Queue 是队列目的地,Topic 是主题目的地。都是 Destination 的子接口。
Queue 特点: 队列中的消息,默认只能由唯一的一个消费者处理。一旦处理消息删除。
Topic 特点:主题中的消息,会发送给所有的消费者同时处理。只有在消息可以重复处理的业务场景中可使用。
2.1.9 、PTP PUB & SUB
Point to Point。点对点消息模型。就是基于 Queue 实现的消息处理方式。
Publish & Subscribe 。消息的发布/订阅模型。是基于 Topic 实现的消息处理方式。
3、ActiveMQ安装
3.1、安装资源
3.1.1 、apache官网下载
本次下载当前时间最新版本
apache-activemq-5.15.9-bin.zip windows版
apache-activemq-5.15.9-bin.tar.gz linux
3.1.2、版本说明
ActiveMQ5.10.x 以上版本必须使用 JDK1.8 才能正常使用。
ActiveMQ5.9.x 及以下版本使用 JDK1.7 即可正常使用。
3.1.3、Linux服务器安装JDK1.8
1.下载Linux版的JDK1.8 jdk-8u60-linux-x64.tar.gz
2.登录Linux,切换到root用户
3.通过 Xftp工具将我们安装工具包传入到/opt文件夹下
4.解压jdk的压缩文件 tar -zxvf 压缩文件的名字;
5.配置环境变量 /etc/profile 文件中 (将java的内容配置到最后行比较清晰)
Vim /etc/profile
JAVA_HOME=/opt/jdk1.7.0_79
PATH=/opt/jdk1.7.0_79/bin:$PATH
export JAVA_HOME PATH
Esc退出编辑模式
:wq 保存我们修改的数据
6.重启 linux后,测试是否安装成功
sudo shutdown -r now
java -version
3.1.4 、ActiveMQ安装
1.通过Xftp工具将mq压缩工具包传入/opt文件加下
2.解压 tar -zxvf apache-activemq-5.15.9-bin.tar.gz
3.关闭防火墙:systemctl stop firewalld.service (CenOS 7)
查看防火墙状态:firewall-cmd –state
关闭防火墙开机自启动:systemctl disable firewalld.service
或者开放端口:
添加
firewall-cmd --zone=public --add-port=80/tcp --permanent (–permanent永久生效,没有此参数重启后失效)
重新载入
firewall-cmd --reload
查看
firewall-cmd --zone= public --query-port=80/tcp
删除
firewall-cmd --zone= public --remove-port=80/tcp --permanent
4.进入mq的/opt/apache-activemq-5.15.9/bin目录
5.启动mq ,./activemq start (权限不足时,增加权限。chmod 777 activemq)
6、查验是否安装成功
使用浏览器访问 ActiveMQ 管理应用, 地址如下:
http://ip:8161/admin/
用户名: admin
密码: admin
ActiveMQ 使用的是 jetty 提供 HTTP 服务.启动稍慢,建议短暂等待再访问测试.
见到界面代表服务启动成功
7、重启 ActiveMQ
进入mq的/opt/apache-activemq-5.15.9/bin目录./ activemq restart
8、关闭 ActiveMQ
进入mq的/opt/apache-activemq-5.15.9/bin目录 ./activemq stop
9、修改访问端口
修改 ActiveMQ 配置文件: /opt/activemq/conf/jetty.xml
配置文件修改完毕,保存并重新启动 ActiveMQ 服务。
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8161"/>
</bean>
3.2、ActiveMQ配置文件详解
3.2.1、activemq.xml
就是 spring 配置文件. 其中配置的是 ActiveMQ 应用使用的默认对象组件.
transportConnectors 标签 - 配置链接端口信息的. 其中的端口号 61616 是 ActiveMQ 对外发布的 tcp 协议访问端口. 就是 java 代码访问 ActiveMQ 时使用的端口.
3.2.2、jetty.xml
spring 配置文件, 用于配置 jetty 服务器的默认对象组件.
jetty 是类似 tomcat 的一个中间件容器.
ActiveMQ 默认支持一个网页版的服务查看站点. 可以实现 ActiveMQ 中消息相关数据的页面查看.
8161 端口, 是 ActiveMQ 网页版管理站点的默认端口.
在 ActiveMQ 网页版管理站点中,需要登录, 默认的用户名和密码都是 admin.
3.2.3、users.properties
内容信息: 用户名=密码
是用于配置客户端通过协议访问 ActiveMQ 时,使用的用户名和密码.
4、ActiveMQ 应用
4.1、PTP 处理模式(Queue)
消息生产者生产消息发送到 queue 中,然后消息消费者从 queue 中取出并且消费消息。消息被消费以后,queue 中不再有存储,所以消息消费者不可能消费到已经被消费的消息。
Queue 支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它的则不能消费此消息了。当消费者不存在时,消息会一直保存,直到有消费消费。
4.2 、Publish/Subscribe 处理模式(Topic )
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。
和点对点方式不同,发布到 topic 的消息会被所有订阅者消费。当生产者发布消息,不管是否有消费者。都不会保存消息一定要先有消息的消费者,后有消息的生产者。
4.3、 PTP 和 PUB/SUB 简单对比
4.4、编码实现(没有安全认证,Java项目)
package com.ybjt.mq.producer;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息的生产者
* @author xh_zhang
*
*/
public class ActiveMQProducer {
//默认的连接用户名/密码/ip/发送消息数量
private static final String USERNAEM=ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD;
private static final String URL="tcp://192.168.43.38:61616";
public static void main(String[] args) {
//连接工厂
ConnectionFactory connectionFactory=null;
//连接对象
Connection con = null;
//会话 接受或发送消息的线程
Session session = null;
//目的地
Destination destination = null;
//消息的生产者
MessageProducer messageProducer = null;
//实例化连接工厂
connectionFactory = new ActiveMQConnectionFactory(USERNAEM, PASSWORD, URL);
try {
//获取连接对象
con = connectionFactory.createConnection();
//启动连接
con.start();
//创建session
session=con.createSession(true, Session.AUTO_ACKNOWLEDGE);
//创建叫java的消息队列目的地
destination = session.createQueue("JAVA");
//创建消息生产者
messageProducer = session.createProducer(destination);
//发送消息
sendMessage(session,messageProducer);
//提交
session.commit();
} catch (JMSException e) {
e.printStackTrace();
}finally{
if(con != null){
try {
con.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 发送消息
* @param session
* @param messageProducer
* @throws JMSException
*/
private static void sendMessage(Session session,
MessageProducer messageProducer) throws JMSException {
TextMessage message = session.createTextMessage("大王叫我来巡山");
messageProducer.send(message);
System.out.println("消息发送成功");
}
}
package com.ybjt.mq.consumer;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息的接受者
* @author xh_zhang
*
*/
public class ActiveMQConsumer {
//默认的连接用户名/密码/ip
private static final String USERNAEM=ActiveMQConnection.DEFAULT_USER;
private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD;
private static final String URL="tcp://192.168.43.38:61616";
public static void main(String[] args) {
//连接工厂
ConnectionFactory connectionFactory=null;
//连接对象
Connection con = null;
//会话 接受或发送消息的线程
Session session = null;
//目的地
Destination destination = null;
//消息的生产者
MessageConsumer messageConsumer = null;
//实例化工厂
connectionFactory = new ActiveMQConnectionFactory(USERNAEM, PASSWORD, URL);
try {
con = connectionFactory.createConnection();
con.start();
session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("JAVA");
messageConsumer = session.createConsumer(destination);
while(true){
TextMessage message =(TextMessage)messageConsumer.receive();
if(message!=null && !message.equals("")){
System.out.println("收到的message="+message.getText());
}else{
break;
}
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
5、ActiveMQ 安全认证
ActiveMQ 也提供了安全认证。就是用户名密码登录规则。ActiveMQ 如果需要使用安全认 证 的 话 , 必 须 在 activemq 的 核 心 配 置 文 件 中 开 启 安 全 配 置 。 配 置 文 件 就 是conf/activemq.xml
5.1、在 conf/activemq.xml 配置文件的 broker 标签 中增加下述内容指定了使用 JAAS 插件管理权限,至于 configuration="activemq"是在 login.conf 文件里定义的指定了具体的 Topic/Queue 与用户组的授权关系
<authorizationEntry topic="ActiveMQ.Advisory.>"read=“admins” write=“admins”
admin=“admins”/>这个是必须的配置,不能少。
<plugins>
<!-- use JAAS to authenticate using the login.config file on the classpath to configure JAAS -->
<!-- 添加 jaas 认证插件 activemq 在 login.config 里面定义,详细见 login.config-->
<jaasAuthenticationPlugin configuration="activemq" />
<!-- lets configure a destination based authorization mechanism -->
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
<authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
<authorizationEntry topic="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins"/>
<authorizationEntry queue="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins"/>
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
5.2、开启认证后,认证使用的用户信息由其他配置文件提供。
conf/login.config
activemq {
org.apache.activemq.jaas.PropertiesLoginModule required
org.apache.activemq.jaas.properties.user="users.properties"
org.apache.activemq.jaas.properties.group="groups.properties";
};
user 代表用户信息配置文件,group 代表用户组信息配置文件。寻址路径为相对当前配置文件所在位置开始寻址。
5.3、conf/users.properties
admin=admin
用户名=密码
5.4、conf/groups.properties
admins=admin
组名=用户名,用户名
6、持久化
ActiveMQ 中,持久化是指对消息数据的持久化。在 ActiveMQ 中,默认的消息是保存在内存中的。当内存容量不足的时候,或 ActiveMQ 正常关闭的时候,会将内存中的未处理的消息持久化到磁盘中。具体的持久化策略由配置文件中的具体配置决定。ActiveMQ 的默认存储策略是 kahadb。如果使用 JDBC 作为持久化策略,则会将所有的需要持久化的消息保存到数据库中。所有的持久化配置都在 conf/activemq.xml 中配置,配置信息都在 broker 标签内部定义。
6.1、kahadb 方式
是 ActiveMQ 默认的持久化策略。kahadb 是一个文件型数据库。是使用内存+文件保证数据的持久化的。kahadb 可以限制每个数据文件的大小。不代表总计数据容量。
<persistenceAdapter>
<!-- directory:保存数据的目录; journalMaxFileLength:保存消息的文件大小 -->
<kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="16mb"/>
</persistenceAdapter>
特性是:1、日志形式存储消息;2、消息索引以 B-Tree 结构存储,可以快速更新;3、完全支持 JMS 事务;4、支持多种恢复机制;
6.2 、AMQ 方式
只适用于 5.3 版本之前。AMQ 也是一个文件型数据库,消息信息最终是存储在文件中。内存中也会有缓存数据。
<persistenceAdapter>
<!-- directory:保存数据的目录 ; maxFileLength:保存消息的文件大小 -->
<amqPersistenceAdapter directory="${activemq.data}/amq" maxFileLength="32mb"/>
</persistenceAdapter>
性能高于 JDBC,写入消息时,会将消息写入日志文件,由于是顺序追加写,性能很高。为了提升性能,创建消息主键索引,并且提供缓存机制,进一步提升性能。每个日志文件的大小都是有限制的(默认 32m,可自行配置)。当超过这个大小,系统会重新建立一个文件。当所有的消息都消费完成,系统会删除这个文件或者归档。主要的缺点是 AMQ Message 会为每一个 Destination 创建一个索引,如果使用了大量的Queue,索引文件的大小会占用很多磁盘空间。而且由于索引巨大,一旦 Broker(ActiveMQ 应用实例)崩溃,重建索引的速度会非常慢。虽然 AMQ 性能略高于 Kaha DB 方式,但是由于其重建索引时间过长,而且索引文件占用磁盘空间过大,所以已经不推荐使用。
6.3、JDBC
ActiveMQ 将数据持久化到数据库中。 不指定具体的数据库。 可以使用任意的数据库中。 本环节中使用 MySQL 数据库。下述文件为 activemq.xml 配置文件部分内容。 不要完全复制 。首先定义一个 mysql-ds 的 MySQL 数据源,然后在 persistenceAdapter 节点中配置jdbcPersistenceAdapter 并且引用刚才定义的数据源。
dataSource 指定持久化数据库的 bean,createTablesOnStartup 是否在启动的时候创建数据表,默认值是 true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改成 false。
<broker brokerName="test-broker" persistent="true"
xmlns="http://activemq.apache.org/schema/core">
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#mysql-ds"
createTablesOnStartup="true"/>
</persistenceAdapter>
</broker>
<bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="200"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
配置成功后,需要在数据库中创建对应的 database,否则无法访问。表格 ActiveMQ 可以自动创建。activemq_msgs 用于存储消息,Queue 和 Topic 都存储在这个表中:
ID:自增的数据库主键
CONTAINER:消息的 Destination
MSGID_PROD:消息发送者客户端的主键
MSG_SEQ:是发送消息的顺序,MSGID_PROD+MSG_SEQ 可以组成 JMS 的 MessageID
EXPIRATION:消息的过期时间,存储的是从 1970-01-01 到现在的毫秒数
MSG:消息本体的 Java 序列化对象的二进制数据
PRIORITY:优先级,从 0-9,数值越大优先级越高
activemq_acks 用于存储订阅关系。如果是持久化 Topic,订阅者和服务器的订阅关系在这个表保存:
主要的数据库字段如下:
CONTAINER:消息的 Destination
SUB_DEST:如果是使用 Static 集群,这个字段会有集群其他系统的信息
CLIENT_ID:每个订阅者都必须有一个唯一的客户端 ID 用以区分
SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,
可支持多属性 AND 和 OR 操作
LAST_ACKED_ID:记录消费过的消息的 ID。
表 activemq_lock 在集群环境中才有用,只有一个 Broker 可以获得消息,称为 Master Broker,其他的只能作为备份等待 Master Broker 不可用,才可能成为下一个 Master Broker。这个表用于记录哪个 Broker 是当前的 Master Broker。
只有在消息必须保证有效,且绝对不能丢失的时候。使用 JDBC 存储策略。
如果消息可以容忍丢失,或使用集群/主备模式保证数据安全的时候,建议使用 levelDB或 Kahadb。
7、API简介
7.1 ProducerAPI 简介
7.1.1发送消息MessageProducer.
send(Message message);发送消息到默认目的地,就是创建 Producer 时指定的目的地。
send(Destination destination, Message message); 发送消息到指定目的地,
7.1.2 消息有效期
消息过期后,默认会将失效消息保存到“死信队列(ActiveMQ.DLQ)”。
不持久化的消息,在超时后直接丢弃,不会保存到死信队列中。
死信队列名称可配置,死信队列中的消息不能恢复。
死信队列是在 activemq.xml 中配置的。
7.1.3 消息优先级
不需特殊关注。我们可以在发送消息时,指定消息的权重,broker 可以建议权重较高的消息将会优先发送给 Consumer。在某些场景下,我们通常希望权重较高的消息优先传送;不过因为各种原因,priority 并不能决定消息传送的严格顺(order)。
JMS 标准中约定 priority 可以为 0~9 的整数数值,值越大表示权重越高,默认值为 4。不过 activeMQ 中各个存储器对 priority 的支持并非完全一样。比如 JDBC 存储器可以支持0~9,因为 JDBC 存储器可以基于 priority 对消息进行排序和索引化;但是对于 kahadb/levelDB等这种基于日志文件的存储器而言,priority 支持相对较弱,只能识别三种优先级(LOW: <4,NORMAL: =4,HIGH: > 4)。
7.1.3.1开启
在 broker 端,默认是不存储 priority 信息的,我们需要手动开启,修改 activemq.xml 配置文件,在 broker 标签的子标签 policyEntries 中增加下述配置:
不过对于“非持久化”类型的消息(如果没有被 swap 到临时文件),它们被保存在内存中,它们不存在从文件 Paged in 到内存的过程,因为可以保证优先级较高的消息,总是在 prefetch的时候被优先获取,这也是“非持久化”消息可以担保消息发送顺序的优点。Broker 在收到 Producer 的消息之后,将会把消息 cache 到内存,如果消息需要持久化,那么同时也会把消息写入文件;如果通道中 Consumer 的消费速度足够快(即积压的消息很少,尚未超过内存限制,我们通过上文能够知道,每个通道都可以有一定的内存用来 cache消息),那么消息几乎不需要从存储文件中 Paged In,直接就能从内存的 cache 中获取即可,这种情况下,priority 可以担保“全局顺序”;不过,如果消费者滞后太多,cache 已满,就会触发新接收的消息直接保存在磁盘中,那么此时,priority 就没有那么有效了。在 Queue 中,prefetch 的消息列表默认将会采用“轮询”的方式(roundRobin,注意并不是
roundRobinDispatch)[备注:因为Queue不支持任何DispatchPolicy],依次添加到每个consumer的 pending buffer 中,比如有 m1-m2-m3-m4 四条消息,有 C1-C2 两个消费者,那么:m1->C1,m2->C2,m3->C1,m4->C2。这种轮序方式,会对基于权重的消息发送有些额外的影响,假如四条消息的权重都不同,但是(m1,m3)->C1,事实上 m2 的权重>m3,对于 C1 而言,它似乎丢失了“顺序性”。
7.1.3.2 强顺序
strictOrderDispatch“严格顺序转发”,这是区别于“轮询”的一种消息转发手段;不过不要误解它为“全局严格顺序”,它只不过是将 prefetch 的消息依次填满每个 consumer 的 pendingbuffer 。 比 如 上 述 例 子 中 , 如 果 C1-C2 两 个 消 费 者 的 buffer 尺 寸 为 3 , 那 么(m1,m2,m3)->C1,(m4)->C2;当 C1 填充完毕之后,才会填充 C2。由此这种策略可以保证 buffer中所有的消息都是“ 权重临近的 ”、有序的。(需要注意:strictOrderDispatch 并非是解决 priority消息顺序的问题而生,只是在使用 priority 时需要关注它)7.1.3.3 严格顺序
useCache=false 来关闭内存,强制将所有的消息都立即写入文件(索引化,但是会降低消息的转发效率);queuePrefetch=1 来约束每个 consumer 任何时刻只有一个消息正在处理,那些消息消费之后,将会从文件中重新获取,这大大增加了消息文件操作的次数,不过每次读取肯定都是 priority 最高的消息。7.2 ConsumerAPI 简介
7.2.1 消息的确认
Consumer 拉取消息后,如果没有做确认 acknowledge,此消息不会从 MQ 中删除。消息的如果拉去到 consumer 后,未确认,那么消息被锁定。如果 consumer 关闭的时候仍旧没有确认消息,则释放消息锁定信息。消息将发送给其他的 consumer 处理。消息一旦处理,应该必须确认。类似数据库中的事务管理机制。
7.2.2 消息的过滤
对消息消费者处理的消息数据进行过滤。这种处理可以明确消费者的角色,细分消费者的功能。
设置过滤:
Session.createConsumer(Destination destination, String messageSelector);
过滤信息为字符串,语法类似 SQL92 中的 where 子句条件信息。可以使用诸如 AND、OR、IN、NOT IN 等关键字。详细内容可以查看 javax.jms.Message 的帮助文档。
注意:消息的生产者在发送消息的的时候,必须设置可过滤的属性信息,所有的属性信息设置方法格式为:setXxxxProperty(String name, T value)。 其中方法名中的 Xxxx 是类型,
如 setObjectProperty/setStringProperty 等。