ActiveMQ介绍
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
特性列表:
⒈ 多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP
⒉ 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)
⒊ 对spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性
⒋ 通过了常见J2EE服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上
⒌ 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
⒍ 支持通过JDBC和journal提供高速的消息持久化
⒎ 从设计上保证了高性能的集群,客户端-服务器,点对点
⒏ 支持Ajax
⒐ 支持与Axis的整合
⒑ 可以很容易的调用内嵌JMS provider,进行测试
1.2 JMS介绍
JMS的全称是Java Message Service,即Java消息服务。用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
它主要用于在生产者和消费者之间进行消息传递,生产者负责产生消息,而消费者负责接收消息。把它应用到实际的业务需求中的话我们可以在特定的时候利用生产者生成一消息,并进行发送,对应的消费者在接收到对应的消息后去完成对应的业务逻辑。
对于消息的传递有两种类型:
一种是点对点的,即一个生产者和一个消费者一一对应;
另一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。
JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。
· StreamMessage – Java原始值的数据流
· MapMessage–一套名称-值对
· TextMessage–一个字符串对象
· ObjectMessage–一个序列化的 Java对象
· BytesMessage–一个字节的数据流
一、下载:
http://archive.apache.org/dist/activemq/5.14.5/
apache-activemq-5.14.0-bin.tar.gz(Linux&MAC)
apache-activemq-5.14.5-bin.zip(Windows)
二、安装activemq
1、gz文件拷贝到指定目录下
2、解压启动
cd apache-activemq-5.14.0
cd bin
./activemq start
3、停止
../activemq stop
三、开启防火墙端口
1、如果使用了云服务器需要先开启8161(web管理页面端口)、61616(activemq服务监控端口) 两个端口
2、打开linux防火墙端口
/sbin/iptables -I INPUT -p tcp --dport 8161 -j ACCEPT&&/etc/init.d/iptables save&&service iptables restart&&/etc/init.d/iptables status
/sbin/iptables -I INPUT -p tcp --dport 61616 -j ACCEPT&&/etc/init.d/iptables save&&service iptables restart&&/etc/init.d/iptables status
四、打开web管理页面
http://IP:8161/admin
默认用户名密码 admin/admin
这个用户名密码是在conf/users.properties中配置的
五、ActiveMQ的使用方法
5.1 JMS消息发送模式
在点对点或队列模型下,一个生产者向一个特定的队列发布消息,一个消费者从该队列中读取消息。这里,生产者知道消费者的队列,并直接将消息发送到消费者的队列。这种模式被概括为:只有一个消费者将获得消息。生产者不需要在接收者消费该消息期间处于运行状态,接收者也同样不需要在消息发送时处于运行状态。每一个成功处理的消息都由接收者签收。
发布者/订阅者模型支持向一个特定的消息主题发布消息。0或多个订阅者可能对接收来自特定消息主题的消息感兴趣。在这种模型下,发布者和订阅者彼此不知道对方。这种模式好比是匿名公告板。这种模式被概括为:多个消费者可以获得消息.在发布者和订阅者之间存在时间依赖性。发布者需要建立一个订阅(subscription),以便客户能够购订阅。订阅者必须保持持续的活动状态以接收消息,除非订阅者建立了持久的订阅。在那种情况下,在订阅者未连接时发布的消息将在订阅者重新连接时重新发布。
类似QQ的单聊和群聊
5.2 JMS应用程序接口
ConnectionFactory 接口(连接工厂)
用户用来创建到JMS提供者的连接的被管对象。JMS客户通过可移植的接口访问连接,这样当下层的实现改变时,代码不需要进行修改。管理员在JNDI名字空间中配置连接工厂,这样,JMS客户才能够查找到它们。根据消息类型的不同,用户将使用队列连接工厂,或者主题连接工厂。
Connection 接口(连接)
连接代表了应用程序和消息服务器之间的通信链路。在获得了连接工厂后,就可以创建一个与JMS提供者的连接。根据不同的连接类型,连接允许用户创建会话,以发送和接收队列和主题到目标。
Session 接口(会话)
表示一个单线程的上下文,用于发送和接收消息。由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事务。如果用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务之前,用户可以使用回滚操作取消这些消息。一个会话允许用户创建消息生产者来发送消息,创建消息消费者来接收消息。
Destination 接口(目标)
目标是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。JMS管理员创建这些对象,然后用户通过JNDI发现它们。和连接工厂一样,管理员可以创建两种类型的目标,点对点模型的队列,以及发布者/订阅者模型的主题。
MessageConsumer 接口(消息消费者)
由会话创建的对象,用于接收发送到目标的消息。消费者可以同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。
MessageProducer 接口(消息生产者)
由会话创建的对象,用于发送消息到目标。用户可以创建某个目标的发送者,也可以创建一个通用的发送者,在发送消息时指定目标。
Message 接口(消息)
是在消费者和生产者之间传送的对象,也就是说从一个应用程序创送到另一个应用程序。一个消息有三个主要部分:
消息头(必须):包含用于识别和为消息寻找路由的操作设置。
一组消息属性(可选):包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。
一个消息体(可选):允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。
消息接口非常灵活,并提供了许多方式来定制消息的内容。
5.3 不使用JMS模版消息队列
5.3.1 Producer
public void testQueueProducer() throws Exception {
// 第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
//brokerURL服务器的ip及端口号
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.168:61616");
// 第二步:使用ConnectionFactory对象创建一个Connection对象。
Connection connection = connectionFactory.createConnection();
// 第三步:开启连接,调用Connection对象的start方法。
connection.start();
// 第四步:使用Connection对象创建一个Session对象。
//第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
//第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。一般是自动应答。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。
//参数:队列的名称。
Queue queue = session.createQueue("test-queue");
// 第六步:使用Session对象创建一个Producer对象。
MessageProducer producer = session.createProducer(queue);
// 第七步:创建一个Message对象,创建一个TextMessage对象。
/*TextMessage message = new ActiveMQTextMessage();
message.setText("hello activeMq,this is my first test.");*/
TextMessage textMessage = session.createTextMessage("hello activeMq,this is my first test.");
// 第八步:使用Producer对象发送消息。
producer.send(textMessage);
// 第九步:关闭资源。
producer.close();
session.close();
connection.close();
}
5.3.2 Consumer
publicclass QueueConsumer {
publicstaticvoidmain(String[]args) {
//创建一连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.168:61616");
try {
//创建一个连接
Connection connection = connectionFactory.createConnection();
//打开连接
connection.start();
//创建一个回话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个目的地Destination
Queue queue = session.createQueue("mytestqueue");
//创建一个消费者
MessageConsumer consumer = session.createConsumer(queue);
while(true) {
//设置接收者接收消息的时间,为了便于测试,这里定为100s
Messagemessage =consumer.receive(100000);
if (message !=null) {
System.out.println(message);
}else {
//超时结束
break;
}
}
consumer.close();
session.close();
connection.close();
} catch (Exceptione) {
e.printStackTrace();
}
}
}
5.4 使用JMS模版消息队列
把ActiveMQ依赖的jar包添加到工程中。
5.4.1使用maven工程,添加jar包的依赖:
<!--spring-jms -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<!-- jms -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.13.0</version>
</dependency>
5.4.2写applicationContext-jms.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- ActiveMQ 提供的ConnectionFactory -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<!-- Spring jms 提供的连接池 -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>
<!-- 队列目的地,点对点的 -->
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="bidding-queue" />
</bean>
<!-- jmsTemplate -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="queueDestination" />
</bean>
<!-- 消息监听 -->
<bean id="queueMessageListener" class="com.jiuqi.liad.service.impl.QueueMessageListen" />
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="queueDestination" />
<property name="messageListener" ref="queueMessageListener" />
</bean>
</beans>
5.4.3 Producer
package com.jms.service.impl;
import com.jms.service.ProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
/**
* 队列模式生产者
*/
@Service
public class QueueProducerServiceImpl implements ProducerService {
@Autowired
private JmsTemplate jmsTemplate;
public void sendMessage(Destination destination, String msg) {
System.out.println("生产者1向队列"+destination+"发送消息:"+msg);
jmsTemplate.convertAndSend(destination,msg);
}
public void sendMessage(String msg) {
System.out.println("生产者1向队列"+jmsTemplate.getDefaultDestination().toString()+"发送消息");
jmsTemplate.convertAndSend(msg);
}
public void sendByName(String topicName, final String phone , final String message) {
Map<String,String> map=new HashMap<String,String>();
map.put("phone", phone);
map.put("message", message);
jmsTemplate.convertAndSend(topicName,map);
System.out.println("生产者1向队列服务器"+jmsTemplate.getDefaultDestination().toString()+"发送短信");
}
public void sendByName(Destination destination, String phone, String message) {
Map<String,String> map=new HashMap<String,String>();
map.put("phone", phone);
map.put("message", message);
jmsTemplate.convertAndSend(destination,map);
System.out.println("生产者2向队列服务器"+jmsTemplate.getDefaultDestination().toString()+"发送短信");
}
}
5.4.4 同步Consumer
package com.jms.service.impl;
import com.jms.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import javax.jms.Destination;
import javax.jms.MapMessage;
/**
* 队列模式消费者
*/
@Service
public class QueueCustomerServiceImpl implements CustomerService {
@Autowired
private JmsTemplate jmsTemplate;
/**
* 接收消息
* @param destination
*/
public void receive(Destination destination) {
System.out.println("接收者1进入");
try {
MapMessage mapMessage = (MapMessage) jmsTemplate.receive(destination);
String phone=mapMessage.getString("phone");
String message1=mapMessage.getString("message");
String result = "000/***";
if (result.startsWith("000")) {
System.out.println("电话号码:" + phone
+ "==验证码:" + message1
+ "==============发送成功");
mapMessage.acknowledge();
} else {
// 发送失败
System.out.println("发送失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.4.5 异步Consumer
package com.jms.service.impl;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
/**
* 队列模式监听版消费者
*/
@Component
public class QueueMessageListen implements MessageListener{
public void onMessage(Message message) {
System.out.println("监听==================监听");
try {
MapMessage tm = (MapMessage)(message);
String phone=tm.getString("phone");
String message1=tm.getString("message");
String result = "000/***";
if (result.startsWith("000")) {
System.out.println("电话号码:" + phone
+ "==验证码:" + message1
+ "==============发送成功");
tm.acknowledge();
} else {
// 发送失败
System.out.println("发送失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.4.6 测试类
package com.jsm.test;
import com.jms.service.ProducerService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 消息队列测试类
*/
public class QuereJmsTest {
@Test
public void producerTest(){
ClassPathXmlApplicationContext springContext = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-jms.xml"});
Destination queuedestination = (ProducerService)springContext.getBean("queueDestination");
ProducerService quereproducerService = (ProducerService)springContext.getBean("queueProducerServiceImpl");
//如果是异步监听器不用创建消费者
CustomerService querecustomerService = (CustomerService)springContext.getBean("queueCustomerServiceImpl");
//往指定名字的队列中存入消息
for(int i =0 ;i<100 ;i++){
quereproducerService.sendByName("message","1760055****","你好,你的短信验证码为"+i);
}
//如果是异步监听器不用指定接受者接受
for(int i =0 ;i<100 ;i++){
querecustomerService.receive(queuedestination);
}
}
}