ActiveMQ简介
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供·消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
关注于数据的发送和接受,利用高效可靠的异步消息传递机制继承分布式系统。
应用场景
异步消息:
应用解耦:
流量削峰:
安装与启动ActiveMQ:
1.上传ActiveMQ.jar
2.tar -zxvf ActiveMQ.jar
3.bin目录下:sh activemq start 启动完成
4.访问address:port 我的是:http://192.168.74.131:8161/
为什么是8161?
通过查看conf下的jetty.xml,默认端口号是8161
JMS基本概念
Java消息服务(Java Message Service)是java平台中关于面向消息中间件的API,用于两个应用程序之间,或者分布式系统中发送消息,进行异步通信。
JMS是一个与具体平台无关,绝大多数MOM(面向消息中间件)提供商同对JMS提供了支持
什么是MOM:
消息传送提供者相当于数据库,消息被消费者得到后,就相当于执行了delete操作,将消息delete掉,其他的客户端就获取不到了。
第一个activeMQ程序
引入jar包:
采用点对点传递域(p2p):
1.每一个消息只能有一个消费者
2.消息的生产者与消费者之间没有任何时间上的相关性。无论消费者在生产者发送消息时是否处于运行状态,都可以提取消息。
消息发送者
public class JmsSender {
public static void main(String[] args) {
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("" +
"tcp://192.168.11.140:61616");
Connection connection=null;
try {
//创建连接
connection=connectionFactory.createConnection();
connection.start();
Session session=connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建队列(如果队列已经存在则不会创建, first-queue是队列名称)
//destination表示目的地
Destination destination=session.createQueue("first-queue");
//创建消息发送者
MessageProducer producer=session.createProducer(destination);
TextMessage textMessage=session.createTextMessage("hello, 菲菲,我是帅帅的mic");
producer.send(textMessage);
session.commit();
session.close();
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消息接受者
public class JmsReceiver {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("" +
"tcp://192.168.11.140:61616");
Connection connection = null;
try {
//创建连接
connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建队列(如果队列已经存在则不会创建, first-queue是队列名称)
//destination表示目的地
Destination destination = session.createQueue("first-queue");
//创建消息接收者
MessageConsumer consumer = session.createConsumer(destination);
TextMessage textMessage = (TextMessage) consumer.receive();
System.out.println(textMessage.getText());
session.commit();
session.close();
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消息包括消息头,消息体,消息属性。消息体有:TextMessage、MapMessage、BytesMessage、StreamMessage、ObjectMessage。
为什么是61616?
在p2p模型中:
1.如果session关闭时,有一些消息已经收到,但还没有被签收,那么当消费者下次连接到相同队列时,消息还可以被签收。
2.如果用户在receive方法中设定消息选择条件,那么不符合条件的消息会留在队列中不会被接受。
3.队列可以长久保存消息直到消息被消费者签收。消费者无需担心因为消息丢失而时刻与jms provider保持连接状态。
发布订阅传递域(pub/sub)模型
每个消息可以有多个消费者,消息的生产者和消费者之间存在时间上的相关性,订阅一个主题的消费者只能消费自它订阅之后发布的消息。JMS允许客户端创建持久订阅。
1.订阅可以分为持久性订阅与非持久性订阅
2.当所有的消息必须接收时,需要用到持久订阅,反之,则用非持久性订阅。
非持久订阅消息发送者:
public class JmsTopicSender {
public static void main(String[] args) {
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("" +
"tcp://192.168.11.140:61616");
Connection connection=null;
try {
//创建连接
connection=connectionFactory.createConnection();
connection.start();
Session session=connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建队列(如果队列已经存在则不会创建, first-queue是队列名称)
//destination表示目的地
Destination destination=session.createTopic("first-topic");
//创建消息发送者
MessageProducer producer=session.createProducer(destination);
TextMessage textMessage=session.createTextMessage("今天心情,晴转多云");
producer.send(textMessage);
session.commit();
session.close();
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
非持久订阅消息接受者:
public class JmsTopicReceiver {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.74.131:61616");
Connection connection = null;
try {
//创建连接
connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建队列(如果队列已经存在则不会创建, first-queue是队列名称)
//destination表示目的地
Destination destination = session.createTopic("first-topic");
//创建消息接收者
MessageConsumer consumer = session.createConsumer(destination);
TextMessage textMessage = (TextMessage) consumer.receive();
System.out.println(textMessage.getText());
session.commit();
session.close();
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
持久订阅消息接受者: 先启动一下,创建持久订阅,第二次启动时就可以订阅启动前创建的消息
public class JmsTopicPersistenteReceiver {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.74.131:61616");
Connection connection = null;
try {
//创建连接
connection = connectionFactory.createConnection();
connection.setClientID("DUBBO-ORDER"); //设置持久订阅
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
//创建队列(如果队列已经存在则不会创建, first-queue是队列名称)
//destination表示目的地
Topic topic = session.createTopic("first-topic");
//创建消息接收者
// MessageConsumer consumer = session.createConsumer(destination);
MessageConsumer consumer = session.createDurableSubscriber(topic,"DUBBO-ORDER");
TextMessage textMessage = (TextMessage) consumer.receive();
System.out.println(textMessage.getText());
session.commit();
session.close();
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
JMS的可靠性机制
JMS消息被确认后,才会认为是被成功消费。消息的消费阶段包含三个阶段:客户端接收消息、客户端处理消息、消息被确认。
事务性会话
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
上面代码,当true时,消息会在session.commit后自动提交,不commit就不会被提交。如果将sender的commit注释掉,消息将不会被发送,将receiver的commit注释掉,消息将会一直能读取,而不是读取一次后就被删除了。同时,第二个参数将会毫无意义。源码如下:
public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException {
this.checkClosedOrFailed();
this.ensureConnectionInfoSent();
if (!transacted) {
if (acknowledgeMode == 0) {
throw new JMSException("acknowledgeMode SESSION_TRANSACTED cannot be used for an non-transacted Session");
}
if (acknowledgeMode < 0 || acknowledgeMode > 4) {
throw new JMSException("invalid acknowledgeMode: " + acknowledgeMode + ". Valid values are Session.AUTO_ACKNOWLEDGE (1), Session.CLIENT_ACKNOWLEDGE (2), Session.DUPS_OK_ACKNOWLEDGE (3), ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE (4) or for transacted sessions Session.SESSION_TRANSACTED (0)");
}
}
return new ActiveMQSession(this, this.getNextSessionId(), transacted ? 0 : acknowledgeMode, this.isDispatchAsync(), this.isAlwaysSessionAsync());
}
非事务性会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
在该模式下,消息何时被确认取决于创建会话时的应答模式,这时commit就没有作用了。
AUTO_ACKNOWLEDGE
当客户端成功从recive方法返回以后,或者[MessageListener.onMessage]方法成功返回后,会话会自动确认该消息。
CLIENT_ACKNOWLEDGE
客户端通过调用消息的textMessage.ackonwledge()确认消息。
这种模式下,如果一个消息消费者消费一共10个消息,那么消费了5个消息,然后在第5个消息通过textMessage.acknowledge(),那么之前的所有消息都会被消息确认。
DUPS_OK_ACKNOWLEDGE
延迟确认
Broker
当前允许的activeMQ就是一个broker实例,可以自己构建一个brokerServer
public class DefineBrokerServer {
public static void main(String[] args) {
BrokerService brokerService=new BrokerService();
try {
brokerService.setUseJmx(true);
brokerService.addConnector("tcp://localhost:61616");
brokerService.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后可以将前面代码中的地址改为"tcp://localhost:61616",发现也可以正常使用。