目录
- JMS介绍
- ActiveMQ简介及安装
- ActiveMQ的实例
- ActiveMQ配置介绍
- ActiveMQ的部署模式
- ActiveMQ与Spring整合实例
JMS介绍
- 基本概念
JMS(Java Service Message)即Java消息服务应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。 - 对象模型
(1)连接工厂(ConnectionFactory)是由管理员创建,并绑定到JNDI树中。客户端使用JNDI查找连接工厂,然后利用连接工厂创建一个JMS连接。
(2)JMS连接(Connection)表示JMS客户端和服务器端之间的一个活动的连接,是由客户端通过调用连接工厂的方法建立的。
(3)JMS会话(Session)表示JMS客户与JMS服务器之间的会话状态。JMS会话建立在JMS连接上,表示客户与服务器之间的一个会话线程。
(4)JMS目的(Destination),又称为消息队列,是实际的消息源。
(5)生产者(Message Producer)和消费者(Message Consumer)对象由Session对象创建,用于发送和接收消息。
(6)监听器(MessageListener),如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。 -
消息模型
(1)点对点(Point-to-Point)
在点对点的消息系统中,消息分发给一个单独的使用者。发送者和接收者之间在时间上没有依赖性,接收者在成功接收消息之后需向队列应答成功,点对点消息往往与队列(javax.jms.Queue)相关联。
(2)发布/订阅(Publish/Subscribe)
发布/订阅消息系统支持一个事件驱动模型,消息生产者和消费者都参与消息的传递。生产者发布事件,而使用者订阅感兴趣的事件,并使用事件。发布者和订阅者之间有时间上的依赖性。该类型消息一般与特定的主题(javax.jms.Topic)关联。
- 基本组件
(1)Broker:消息代理。表示消息队里服务器实体
(2)Producer:消息生产者。业务发起方
(3)Consumer:消息消费者。业务处理方
(4)Topic:主题。在发布/订阅模式下消息的统一汇集地
(5)Queue:队列。在点对点模式下,特定生产者向特定队列发送消息
(6)Message:消息。根据不同的通信协议定义的固定格式进行编码的数据包
ActiveMQ简介及安装
- 简介
ActiveMQ是由Apache出品的,一款最流行的,能力强劲的开源消息中间件。ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。 - 安装使用
(1)官网下载地址ActiveMQ
(2)运行服务
解压后找到脚本目录
执行./activemq start命令,出现Starting ActiveMQ Broker...即成功启动
访问默认页面,地址http://127.0.0.1:8161/admin/
默认页面访问端口是8161
默认用户和密码都是admin
- 特性介绍
(1)多种语言和协议编写客户端。语言: Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议: OpenWire,STOMP REST,MQTT,AMQP
(2)完全支持JMS1.1和J2EE 1.4规范 (持久化,分布式事务消息,事务)
(3)对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的框架中去
(4)连接模式多样化,支持多种传输协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
(5)支持通过JDBC和journal提供快速的消息持久化
(6)为高性能集群,客户端-服务器,点对点通信场景而设计
(7)可以轻松地与CXF、Axis等Web Service技术整合
(8)可以被作为内存中的JMS提供者,非常适合JMS单元测试
(9)提供了REST API接口
(10)支持以AJAX方式调用 - 使用场景
(1)多个项目之间集成,跨平台 ,多语言
(2)降低系统间模块的耦合度,解耦
(3)系统前后端隔离
ActiveMQ的简单实例
- 点对点模式
public static void main(String[] args) {
//1.创建工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER,ActiveMQConnectionFactory.DEFAULT_PASSWORD,ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);
Connection connection = null;
Session session = null;
try {
//2.创建连接
connection = connectionFactory.createConnection();
connection.start();
//3.创建session
//Session.AUTO_ACKNOWLEDGE: 接收到消息时,自动ack确认消息
//Session.CLIENT_ACKNOWLEDGE:接收到消息时,客户端调用message.acknowledge(),显式对消息确认。
session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
//4.创建Destination
Destination destination = session.createQueue("hello");
//5.创建生产者/消费者
MessageProducer messageProducer = session.createProducer(destination);
MessageConsumer messageConsumer = session.createConsumer(destination);
//6.设置持久化方式
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//7.定义消息对象并发送/接受
TextMessage textMessage = session.createTextMessage();
textMessage.setText("helloworld");
messageProducer.send(textMessage);
TextMessage receiveMessage = (TextMessage) messageConsumer.receive();
System.out.println(receiveMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session!=null){
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
Messages Enqueued:表示生产了多少条消息,记做P
Messages Dequeued:表示消费了多少条消息,记做C
Number Of Consumers:表示在该队列上还有多少消费者在等待接受消息
Number Of Pending Messages:表示还有多少条消息没有被消费,实际上是表示消息的积压程度,就是P-C
- 发布订阅模式
public class TestPublish {
private static final String TOPIC = "TestPublish.";
private static final String[] topics = {"A", "B", "C"};
public static void main(String[] args) {
Publisher publisher = new Publisher();
publisher.setTopics(topics);
for (int i = 0; i < 10; i++) {
publisher.sendMessage(topics);
}
publisher.close();
}
public static class Listener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
MapMessage map = (MapMessage)message;
String msg = map.getString("msg");
double version = map.getDouble("version");
System.out.println(msg+" version is "+version);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class Customer{
public static void main(String[] args) {
Customer customer = new Customer();
try {
for (String topic : topics) {
Destination destination = customer.getSession().createTopic(TOPIC + topic);
MessageConsumer messageConsumer = customer.getSession().createConsumer(destination);
messageConsumer.setMessageListener(new Listener());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
private Connection connection = null;
private Session session;
public Customer() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER, ActiveMQConnectionFactory.DEFAULT_PASSWORD, ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);
try {
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
} catch (JMSException e) {
e.printStackTrace();
}
}
public Session getSession() {
return session;
}
}
public static class Publisher {
private Connection connection = null;
private Destination[] destinations;
private Session session;
private MessageProducer messageProducer;
public Publisher() {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER, ActiveMQConnectionFactory.DEFAULT_PASSWORD, ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);
try {
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
messageProducer = session.createProducer(null);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void setTopics(String[] topics) {
destinations = new Destination[topics.length];
for (int i = 0; i < topics.length; i++) {
try {
destinations[i] = session.createTopic(TOPIC + topics[i]);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
private Message createTestMessage(String msg, Session session) {
MapMessage message = null;
try {
message = session.createMapMessage();
message.setString("msg", msg);
message.setDouble("version", 1.00);
} catch (JMSException e) {
e.printStackTrace();
}
return message;
}
public void sendMessage(String[] msgs) {
for (int i = 0; i < msgs.length; i++) {
Message message = createTestMessage(msgs[i], session);
try {
messageProducer.send(destinations[i], message);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
public void close() {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
ActiveMQ配置介绍
- jetty-realm.properties
该配置文件主要用于配置MQ登录页面的用户和密码 - jetty.xml
ActiveMQ内置jetty启动,该配置文件包含了管理功能的相关配置 - activemq.xml
该配置文件包含了很多主要功能的配置
(1) 配置传输连接
ActiveMQ提供了广泛的连接模式,包括HTTP/S、JGroups、JXTA、muticast、SSL、TCP、UDP、XMPP等,
5.13.0+ 版本后,将OpenWire, STOMP, AMQP,MQTT这四种主要协议的端口监听进行了合并,并使用auto关键字进行表示。
如果想给网络通信提供安全支持,则可以在uri中使用"auto+ssl"前缀
如果想提高吞吐性,则可以在uri中使用"auto+nio"前缀
详细配置参考官网
除了这些基本协议之外,还支持一些高级协议
- Failover:一种重连机制,工作于上面介绍的连接协议之上,用于建立可靠的传输。配置语法:
failover:(tcp://localhost/61616,tcp://remotehost:61616)?initialReconnectDelay=100 - Fanout:一种重连和复制机制,也是工作于其他连接协议之上, 采用复制的方式把消息复制到多台消息服务器上。配置语法:fanout:(tcp://localhost/61616,tcp://localhost:61617,tcp://localhost:61618)
(2) 持久化存储模式
AMQ消息存储—5.0及以前默认的消息存储,存放在Data Log中。
KahaDB消息存储—比AMQ消息存储更好的可扩展性和可恢复性—5.3以后推荐使用的消息存储
LevelDB消息存储—5.6以后推出的,使用自定义的索引代替了常用的BTree索引,性能高于KahaDB
JDBC消息存储—基于JDBC存储
Memory消息存储—基于内存的消息存储
(3)其他配置详细
ActiveMQ的部署模式
Master-Slave部署方式
(1). Shared Filesystem Master-Slave方式
支持N个AMQ实例组网,但由于他是基于kahadb存储策略,亦可以部署在分布式文件系统上,应用灵活、高效且安全。
(2). Shared Database Master-Slave方式
与shared filesystem方式类似,只是共享的存储介质由文件系统改成了数据库而已,支持N个AMQ实例组网,但他的性能会受限于数据库。
(3). Replicated LevelDB Store方式
ActiveMQ5.9以后才新增的特性,使用ZooKeeper协调选择一个node作为master。被选择的master broker node开启并接受客户端连接。其他node转入slave模式,连接master并同步他们的存储状态。slave不接受客户端连接。所有的存储操作都将被复制到连接至Master的slaves。如果master死了,得到了最新更新的slave被允许成为master。Broker-Cluster部署方式
(1). Static Broker-Cluster部署
(2). Dynamic Broker-Cluster部署示例(基于Shared Filesystem Master-Slave方式):
修改jetty.xml 和ActiveMQ.xml,保证在同一台机器上部署不会冲突,并创建共享文件夹,然后修改默认kahaDB配置
brokerURL="failover:(Master,Slave1,Slave2)?randomize=false"
ActiveMQ与Spring整合实例
pom.xml
4.0.0
springmvc
springmvc
1.0-SNAPSHOT
war
4.1.8.RELEASE
junit
junit
4.10
test
jstl
jstl
1.2
javax.servlet
javax.servlet-api
3.0.1
commons-logging
commons-logging
1.1.1
log4j
log4j
1.2.16
org.slf4j
slf4j-api
1.7.7
org.slf4j
slf4j-log4j12
1.7.7
org.springframework
spring-core
${springframework}
org.springframework
spring-context
${springframework}
org.springframework
spring-tx
${springframework}
org.springframework
spring-webmvc
${springframework}
org.springframework
spring-web
${springframework}
org.springframework
spring-aop
${springframework}
org.springframework
spring-test
${springframework}
test
org.springframework
spring-jms
${springframework}
org.apache.xbean
xbean-spring
4.6
org.apache.activemq
activemq-core
5.7.0
org.apache.activemq
activemq-pool
5.11.1
org.apache.activemq
activemq-web
5.11.1
commons-httpclient
commons-httpclient
3.1
applicationContext.xml
applicationContext-ActiveMQ.xml
demotest
chat1
spring-mvc.xml
web.xml
springMvc
/views/index.jsp
default
*.js
org.apache.activemq.brokerURL
tcp://localhost:61616
AjaxServlet
org.apache.activemq.web.AjaxServlet
2
true
AjaxServlet
/views/amq/*
contextConfigLocation
classpath:applicationContext*.xml;
org.springframework.web.context.ContextLoaderListener
springMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
true
springMVC
/
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
true
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
部分代码
/**
* @description:生产者
* @author: micheal
* @date:2018/7/8
*/
@Service("producerService")
public class ProducerService {
@Resource
private JmsTemplate jmsTemplate;
public void sendMessage(final String msg){
Destination destination = jmsTemplate.getDefaultDestination();
System.out.println(Thread.currentThread().getName()+"向默认队列"+destination+"发送消息");
jmsTemplate.send(session -> session.createTextMessage(msg));
}
}
/**
* @description:消费者
* @author: micheal
* @date:2018/7/8
*/
@Service("customerService")
public class CustomerService {
@Resource
private JmsTemplate jmsTemplate;
public TextMessage receive(){
Destination destination = jmsTemplate.getDefaultDestination();
TextMessage textMessage = (TextMessage) jmsTemplate.receive(destination);
return textMessage;
}
}
/**
* @author micheal
*/
public class TopicMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
try {
System.out.println("从队列:"+message.getJMSDestination()+"获取到消息:"+((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
实现前端页面聊天室
<%--
Created by IntelliJ IDEA.
User: michael
Date: 2018/7/10
Time: 01:26
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
%>
消息接受页面
<%-- ActiveMQ的demo下有需要引入的js,复制过来即可--%>
发送 ajax JMS 消息
消息窗口
昵称:
消息:
参考链接
configuring-transports
Failover Transport
部署模式
成小胖学习ActiveMQ·基础篇
四种存储方式