-
1 JMS与MQ
1.1 JMS
JMS(Java Messaging
Service)是Java平台上有关面向消息中间件(MOM)的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、
发送、接收消息的接口简化企业应用的开发,中文称Java消息服务。JMS是一种与厂商无关的
API,用来访问消息收发系统消息;就像JDBC(Java Database
Connectivity)可以用来访问许多不同关系数据库一样,JMS则提供同样与厂商无关的访问方法,以访问消息收发服务。
JMS是在Java标准化组织(JCP)内开发的标准(代号JSR 914)。2001年6月25日,发布了JMS 1.0.2b,2002年3月18日发布了JMS1.1,统一了消息域。
JMS规范约定了两种消息方式:P2P(Point To Point)和发布/订阅(Publish/Subscribe)。P2P是用来进行两个节点之间的点对点通信;发布/订阅则是用于一个发布者和多个订阅者之间的通信。
1.2 MQ
MQ是Message
Queue的缩写,中文称消息队列,MQ是在消息的传输过程中保存消息的容器。消息管理器在将消息从它的源中继到它的目标时充当中间人;队列的主要目的是
提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。
MQ有很多不同厂商和语言实现的产品,如Sun MQ、Microsoft MQ、IBM MQ的等商业产品;Java语言实现的MQ,则有ActiveMQ、基于JMS标准的OpenJMS,以及新近的迁移项目Jafka等开源项目。
1.3 二者关系
JMS是一个用于提供消息服务的技术规范,TA制定了在整个消息服务提供过程中的所有数据结构和交互流程。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的服务提供者;MQ的实现可以基于JMS,也可以基于其他规范或标准。2 MQ产品介绍
2.1 ActiveMQ
ActiveMQ是Apache的一个顶级Java开源项目,也是目前最流行的,能力强劲的开源消息队列。ActiveMQ是比较老牌的MQ,2004年由Apache开源孵化,2007年成为Apache顶级项目。最新版本已经更新到5.8.0,TA拥有众多特性:
支持Java,C/C++,C#,Ruby,Perl,Python,PHP等多种语言实现客户端和协议。
完全支持企业集成模式。
支持消息分组、虚拟目标及复合目标等高级特性。
完全支持JMS1.1,和J2EE1.4(持久化、事务,及XA消息)。
提供对Spring的支持,可以很容易内嵌到使用Spring的系统中。
通过了常规的J2EE服务器(如TomEE、Geronimo、Jboss、GlassFish、WebLogic等)的测试。
支持多种传输协议,如:in-VM、TCP、SSL、NIO、UDP、multicast、Jgroups、JXTA等。
支持通过JDBC和journal提供高速的消息持久化。
从设计上保证了高性能的集群、客户端-服务器、点对点通信。
支持基于Web的API及其他方式的REST调用。
支持Ajax。
支持CXF和Axis。
支持用作内嵌JMS provider,进行测试。
2.2 其他产品
2.2.1 OpenJMS
OpenJMS是完全基于JMS1.1规范实现的JMS provider,有以下特性:
支持通过JDBC提供消息持久化。
支持Applet。
能够与Jakarta Tomcat这样的Servlet容器结合。
支持RMI、TCP、HTTP与SSL协议。
提供可靠消息传输、事务和消息过滤。
2.2.2 Jafka
基于scala语言开发的分布式发布订阅消息系统Kafka的Java移植版,功能比较简单,但其最大的特色分布式和高性能:
消息持久化非常快,服务端存储消息的开销为O(1)。
基于文件系统,能够持久化TB级的消息而不损失性能。
吞吐量很大,同等机器配置下,Jafka吞吐量比同类MQ均高。
完全的分布式系统,broker、producer、consumer都原生自动支持分布式;自动实现复杂均衡。
内核非常小,内部机制简洁,适合进行内嵌或者二次开发。
消息格式以及通信机制非常简单,适合进行跨语言开发。3 ActiveMQ的使用
ActiveMQ的使用分为两种,一种是嵌入式,即把ActiveMQ作为内嵌的JMS provider集成到Tomcat或其他Web服务器中;另一种是独立部署,即ActiveMQ单独部署,独立运行,通过ActiveMQ提供的API进行进程外访问。
我们采用独立部署的方式使用ActiveMQ,这样的做法更有利于业务的解耦和工程部署结构的分离;也有利于对ActiveMQ进行配置定制和优化。
我们知道JMS规范中约定了两种消息发送方式:P2P和Publish/Subscribe。根据业务的需求,我们采用P/S方式更为合理,也能更好的发挥ActiveMQ本身的优势。
3.1 消息的发布
// 连接到ActiveMQ服务器
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建主题
Topic topic = session.createTopic("myTopic.messages");
MessageProducer producer = session.createProducer(topic);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);while(true) {
TextMessage message = session.createTextMessage();
message.setText("TIME:" + (new Date()).toLocaleString());
// 发布主题消息
producer.send(message);
System.out.println("Sent message: " + message.getText());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
3.2 消息的订阅
// 连接到ActiveMQ服务器
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建主题
Topic topic = session.createTopic("myTopic.messages");
// 创建订阅
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener() {
// 订阅接收方法
public void onMessage(Message message) {
TextMessage tm = (TextMessage) message;
try {
System.out.println("Received message: " + tm.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
3.3 消息的持久订阅
// 连接到ActiveMQ服务器
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
String ip = "";
try {
// 获取本机IP
InetAddress addr = InetAddress.getLocalHost();
ip = addr.getHostAddress().toString();
} catch (UnknownHostException ex) {
ex.printStackTrace();
ip = "";
}
if(!"".equals(ip)) {
System.out.println("CLIENT: " + ip);
// 设置订阅客户端ID
connection.setClientID(ip);
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建主题
Topic topic = session.createTopic("myTopic.messages");
// 创建订阅
MessageConsumer consumer = session.createDurableSubscriber(topic, "test");
consumer.setMessageListener(new MessageListener() {
// 订阅接收方法
public void onMessage(Message message) {
TextMessage tm = (TextMessage) message;
try {
System.out.println("Received message: " + tm.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
}
3.4 注意事项
普通订阅的情况下,客户端只有在连接到服务器的情况下,才能接收服务器上的主题消息。
持久订阅后,当客户端在线时,服务器端会把客户端在上次下线之后到本次上线之间的所有消息一并推送给客户端;这样就保证了客户端不会有丢失的消息。
持久订阅会引发另一个问题:当新增一个订阅客户端时,这个客户端会收到服务器上该主题下的所有未过期消息。4 ActiveMQ的部署
4.1 下载
4.2 安装
安装JDK(1.4以上即可)。
设置JAVA_HOME环境变量。
直接解压ActiveMQ压缩包。
默认配置下,执行解压目录下bin/activemq.bat即可正常运行。之后,可以通过访问http://localhost:8161/admin查看ActiveMQ的运行情况(默认用户名和密码为admin/admin)。
4.3 配置
ActiveMQ的配置存放在安装目录的conf/activemq.xml文件中。
因为ActiveMQ采用了Jetty作为容器,因此Jetty相关的配置在conf/jetty.xml文件中。5 其他
5.1 消息持久化
默认的情况下,ActiveMQ的消息持久化是基于文件系统的KahaDB。我们可以通过配置,让ActiveMQ使用MySQL实现消息持久化:
将MySQL的jar包复制到安装目录的lib下。
修改配置文件:
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#derby-ds" />
</persistenceAdapter>
增加节点(与broker节点同级):
<bean id="derby-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/activemq?relaxAutoCommit=true" />
<property name="username" value="activemq" />
<property name="password" value="activemq" />
<property name="maxActive" value="200" />
<property name="poolPreparedStatements" value="true" />
</bean>
然后,重启ActiveMQ即可。
5.2 集群
ActiveMQ的集群由服务器端和客户端共同完成。服务器端通过部署Master/Slaver机制,通过进行分布式部署,以实现服务器集群的平行扩展。而客户端则采取静态地址发现,或者动态地址发现的方式,实现服务器的负载均衡选择。
5.2.1 服务器端的部署
ActiveMQ支持Master/Slaver机制,但简单Master/Slaver方式有一定的局限性,不适合服务器集群的平行扩展(当然,简单
Master/Slaver已经足够支撑一般的商业应用)。因此,ActiveMQ提供了支持大并发请求的集群方式:共享文件系统的集群,以及基于
JDBC的集群。
共享文件系统的集群
实际上就是基于文件系统进行集群部署(前面提到过,ActiveMQ默认的消息存储就是基于文件系统的),可以通过分布式存储系统或共享数据目录来实现。这种方式只需要修改conf/activemq.xml:
<persistenceAdapter>
<journaledJDBC dataDirectory="/sharedFileSystem/broker"/>
</persistenceAdapter>
基于JDBC的集群
原理与共享文件系统一致,只不过把文件系统换成了数据库平台。即:多台ActiveMQ连接同一个数据库,从而实现ActiveMQ的服务器集群。配置同5.1。
5.2.2 客户端的使用
服务器端的集群对客户端而言是透明的,但如果客户端希望得到集群和负载均衡的功能支持,则必须在代码中有所体现。
最常规的方法就是failover协议,fileover支持客户端在当前服务器断开的情况下,自动重新连接到新的服务器上,而新的服务器地址可以来源于静态地址列表,也可以来源于动态地址广播。
静态地址发现的常规用法
ActiveMQConnectionFactory factory = new
ActiveMQConnectionFactory("failover:(tcp://primary:61616,tcp://secondary:61616)?randomize=false");
动态地址发现的常规用法
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("failover:(multicast://host:6255)");
当然,ActiveMQ还支持更多的协议,如:fanout、discovery等。
5.3 管理与监控
ActiveMQ提供了一个Web后台用于查看服务器运行状态,并提供了对消息队列、主题及订阅者等进行管理的功能。
另外,ActiveMQ也可以通过配置支持Nagios的集成监控。
转载请注明:学时网 » JMS和ActiveMQ学习总结