上一节我们了解了JMS规范并且知道了JMS规范的良好实现者-activemq。今天我们就去了解一下activemq的使用。另外我们应该抱着目的去学习,别忘了我们为什么要使用消息中间件:解耦系统之间的联系,同步或异步的消息传输,尤其是异步的消息传输,分布式环境下,可靠、高效的消息传输,可以保证消息的重发性和顺序性。即解决业务系统比较多或者是分布式环境下的系统之间安全有效通信的问题,带着这样的目的我们来学习消息中间件就有了方向。
1. 为什么用activemq
在设计分布式应用程序时,应用程序间的耦合(或称集成)方式很重要。耦合意味着两个或者多个应用程序或系统的相互依赖关系。一种简单的方式是在所有的应用程序中从架构上设计他们与其他应用程序间的交叉实现。这样必然导致,一个应用程序的改变,直接导致另一个应用程序的改变。
ActiveMQ采用松耦合方式,应用程序将消息发送给ActiveMQ而并不关心什么时间以何种方式消息投递给接收者。同样的,消息接收者也不会关心消息来源于哪里和消息是怎样投递给ActiveMQ的。对于多语言编写的复杂应用环境中,允许客户端使用不同的编程语言甚至不同的消息包装协议。ActiveMQ作为消息的中间件,允许复杂的多语言应用程序以一种一步的方式集成和交互。所以说,ActiveMQ是一种好的,提供松散耦合的,能够为多语言交叉应用提供集成的中间件。
2. 什么时候用activemq
ActiveMQ的设计目标是提供标准的,面向消息的,能够跨越多语言和多系统的应用集成消息通信中间件。大多数情况下ActiveMQ被用于做系统之间的数据交换。
只要是两个应用程序间需要通信的情况,都可以考虑使用JMS,不论这种通信是在本地的(就是通信的两个应用程序在同一台主机上),还是分布在不同机器上。尽管是在同一个主机上的两个应用程序需要通信也可以使用ActiveMQ。ActiveMQ可以确保消息投递成功并采用异步方式通信。
3. activemq特性
支持JMS规范:ActiveMQ完全实现了JMS1.1规范。
连接方式的多样化:ActiveMQ提供了广泛的连接模式,包括HTTP/S、JGroups、JXTA、muticast、SSL、TCP、UDP、XMPP等。提供了如此多的连接模式表明了ActiveMQ具有较高的灵活性。
与其他的Java容器紧密集成:ActiveMQ提供了和其它流行的Java容器的结合,包括Apache Geronimo、Apache Tomcat、JBoss、Jetty等。
客户端API:ActiveMQ提供了多种客户端可访问的API,包括Java、C/C++,.NET,Perl、PHP、Python、Ruby等。当然,ActiveMQ中介必须运行在Java虚拟机中,但是使用它的客户端可以使用其他的语言来实现。
中介集群:多个ActiveMQ中介可以一起协同工作,来完成某项复杂的工作,这被称为网络型中介(network of brokers),这种类型的中介将会支持多种拓扑类型。
4. 使用activemq
首先我们去apache上下载activemq,点此下载。
接下来我是使用maven来管理jar的,如果你不用maven的话就去刚下载的activemq包中找到jar包导入即可。maven引入jar:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.14.5</version>
</dependency>
然后我们进入刚下载的activemq,我进入的路径如下:apache-activemq-5.14.5-bin\apache-activemq-5.14.5\bin\win64\activemq.bat
,我用的是64位的系统,如果你是32位的同理进入相应文件夹下点击activemq.bat启动activemq客户端,启动完成之后,直接访问ActiveMQ管理页面http://localhost:8161/admin/ 默认用户名密码admin/admin。
客户端界面如下:
接下来该我们写代码的时候了,首先我们还是先写一个P2P(点对点)模式的客户端。代码如下:
Sender.java
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Sender {
public static void main(String[] args) {
//ConnectionFactory是连接工厂,JMS用它创建连接
ConnectionFactory connectionFactory;
//Connection JMS客户端到JMS provider的连接
Connection connection = null;
//Session 一个发送或者接收消息的线程
Session session;
//Destination 消息发送目的地,消息发送给谁接收
Destination destination;
//MessageProducer 消息发送者
MessageProducer messageProducer;
//构造ConnectionFactory 实例对象,此处采用ActiveMQ的实现jar
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://localhost:61616");
try {
//构造工厂得到连接对象
connection = connectionFactory.createConnection();
//启动
connection.start();
//获取操作连接
session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建一个Queue,名称为FirstQueue
destination = session.createQueue("FirstQueue");
//得到消息生产者【发送者】
messageProducer = session.createProducer(destination);
//设置不持久化,根据实际情况而定
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建一个消息对象
TextMessage message = session.createTextMessage();
//把我们的消息写入msg对象中
BufferedReader b=new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println("Enter Msg, end to terminate:");
String s=b.readLine();
if (s.equals("end"))
break;
message.setText(s);
//发送消息
messageProducer.send(message);
System.out.println("Message successfully sent.");
}
session.commit();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(null != connection){
connection.close();
}
} catch (Throwable ignore) {
}
}
}
}
Receiver.java
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class Receiver {
public static void main(String[] args) {
//connectionFactory 连接工厂,JMS用它创建连接
ConnectionFactory connectionFactory;
//connection JMS客户端到JMS provider 的连接
Connection connection = null;
//session一个发送或者接收的线程
Session session;
//destination 消息目的地,发送给谁接收
Destination destination;
//消费者消息接收者
MessageConsumer consumer;
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://localhost:61616");
try {
//构造工厂得到连接对象
connection = connectionFactory.createConnection();
//启动
connection.start();
//获取操作连接
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("FirstQueue");
consumer = session.createConsumer(destination);
while(true){
//设置接收者收消息的时间,为了方便测试,这里暂定设置为100s
TextMessage message = (TextMessage)consumer.receive(100);
if(null != message){
System.out.println("收到消息==="+message.getText());
}else{
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(null != connection){
connection.close();
}
} catch (Throwable ignore) {
}
}
}
}
代码已经注释过了,就不多做解释,接着我们先运行Sender,需要你在控制台输入你要发送的消息,当你输入”end”的时候才会结束输入,否则你每一次输入按回车都是发送一条消息。然后去看一下activemq的控制台,点击一下菜单栏上的Queues:
因为我刚发送了两条消息在这里会显示,消息会由activemq这个中间人统一管理,当接受者需要接受消息的时候,他会来请求activemq,从这里获取消息而不是发送端一直等着接收端。
下面你可以运行一下Receiver,这时候就把刚才这两条消息消费了。消息队列此刻就是空的。之所以强调这一点是为了和接下来的 发布/订阅 模式做一个比较,限于篇幅我就不截图了,大家可以尝试。
下面我们接着写一个Pub/Sub模式的例子,并没有多大的变化,在创建消息队列的时候改为topic模式:
TopicSender.java
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Created by Administrator on 2017/4/25.
*/
public class TopicSender {
public static void main(String[] args) {
//ConnectionFactory是连接工厂,JMS用它创建连接
ConnectionFactory connectionFactory;
//Connection JMS客户端到JMS provider的连接
Connection connection = null;
//Session 一个发送或者接收消息的线程
Session session;
//Destination 消息发送目的地,消息发送给谁接收
Topic destination;
//MessageProducer 消息发送者
MessageProducer messageProducer;
//构造ConnectionFactory 实例对象,此处采用ActiveMQ的实现jar
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://localhost:61616");
try {
//构造工厂得到连接对象
connection = connectionFactory.createConnection();
//启动
connection.start();
//获取操作连接
session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建一个Queue,SecondQueue 此处使用的是Topic模式
destination = session.createTopic("SecondQueue");
//得到消息生产者【发送者】
messageProducer = session.createProducer(destination);
//设置不持久化,根据实际情况而定
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//创建一个消息对象
TextMessage message = session.createTextMessage();
//把我们的消息写入msg对象中
BufferedReader b=new BufferedReader(new InputStreamReader(System.in));
message.setText("你好");
//发送消息
messageProducer.send(message);
System.out.println("Message successfully sent.");
session.commit();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(null != connection){
connection.close();
}
} catch (Throwable ignore) {
}
}
}
}
同理接受方也是如此:
TopicReciever.java
public class TopicReciever {
public static void main(String[] args) {
//connectionFactory 连接工厂,JMS用它创建连接
ConnectionFactory connectionFactory;
//connection JMS客户端到JMS provider 的连接
Connection connection = null;
//session一个发送或者接收的线程
final Session session;
//destination 消息目的地,发送给谁接收 这里注意改成Topic类型的
Topic destination;
//消费者消息接收者
final MessageConsumer consumer;
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://localhost:61616");
try {
//构造工厂得到连接对象
connection = connectionFactory.createConnection();
//启动
connection.start();
//获取操作连接
session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//此处使用的是Topic模式
destination = session.createTopic("SecondQueue");
consumer = session.createConsumer(destination);
while(true){
//设置接收者收消息的时间
TextMessage message = (TextMessage)consumer.receive(10000);
if(null != message){
System.out.println("收到消息==="+message.getText());
}else{
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里我们可以把Reciever同样的代码再复制一份Reciever1,然后我们先把两个接收端启动,再启动发送端,这时候我们发现消息被接受到了;但是如果我们先启动发送端再启动接收端,这时候虽然消息是被发送出去了,但是接收端并未收到,这是为什么呢?这就是我前面在讲P2P模式的时候留下的一个对比点:
P2P模式是1V1的,我发送只对当前声明的这个标识,接受者也只接受该标识所对应的消息。一旦接受者获取该消息,该标识对应的消息即从消息队列中移除;
Pub/Sub模式是1 V N 的,1个发送端发出的消息,可以有多个接收端去消费,但是有一个前提:想消费这条消息的接收端必须先注册,即先启动接收端去activemq的客户端注册,发送端就根据注册的情况主动把消息推送到订阅过该消息的消费者。
我们看到消息队列里面有一条消息,然后有两位消费者来订阅这一条消息,上面我们看到两个消费者分别取队列取一次消息,然后activemq会创建两个临时生产者去他们服务把消息给他们。
好拉,这一节的入门知识就讲到这里,既然是入门我们就不必太深刻,不然适得其反啊!哈哈。下面开始我们就详细的探讨activemq的一些特性以及消息中间件在集群环境中的应用。