一:消息中间件产生的背景
- 通常的程序开发都是客户端与服务器进行通讯时,客户端被访问以后,必须等待服务对象完成处理返回结果才能继续执行,这个过程是基于请求与响应的同步过程
- 客户端与服务器端对象的生命周期紧密耦合,客户端进程和服务端对象进程都都必须正常运行;如果由于服务对象崩溃或者网络故障导致用户的请求不可达,客户会受到异常。
- 同步除了可能会产生阻塞和超时的问题之外,还可能会导致接口重复提交、数据的幂等性问题
等等一些传统做法对现在分布式项目的局限,所以产生了消息中间件RocketMQ、RabbitMQ、ActiveMQ、Kafka、 ZeroMQ 等,一下是对消息中间件的对比图
二:消息中间件
1: 消息中间件概述
面向消息的中间件(MessageOrlented MiddlewareMOM)较好的解决了以上问题。发送者将消息发送给消息服务器,消息服务器将消息
存放在若干队列中,在合适的时候再将消息转发给接收者。
这种模式下,发送和接收是异步的,发送者无需等待;二者的生命周期未必相同:发送消息的时候接收者不一定运行,接收消息的时
候发送者也不一定运行,这种异步机制显得更加灵活。同时可以实现一对多通信:对于一个消息可以有多个接收者。
MQ(Message Queue)是一个非常经典的生产者与消费者模型,简单来说,提供接口给别人调用的是生产者,而调用接口的是消费者。
而消费者与生产者之间的通信,是通过队列实现的。
1、生产者向队列发送消息,如果消费者不在,消息缓存在队列中。
2、当消费者从队列中获取到消息后,视为消息被消费,则该消息会从队列中移除,防止重复消费。
思考:如果生产者在某一时候向队列中发送了大量消息,而消费者未能及时消费,会不会导致消费者挂掉?
如果使用传统方式,生产者A向消费者B某一时刻发送大量消息,B可能会直接宕机挂掉,而MQ不一样,MQ是异步处理的,生产者向消费
者发送大量消息,都会缓存在队列中,排队消费,并且消息队列是可以持久化的。所以由于消息是可缓存的,消息中间件也是解决高并发的
一种手段。
2:消息中间件模式分类
2.1 P2P(点对点) : 消息域使用 queue 作为 Destination,消息可以被同步或异步的发送和接收,每个消息只会给一个 Consumer 传送
一次。Consumer 可以使用 MessageConsumer.receive() 同步地接收消息,也可以通过使用MessageConsumer.setMessageListener()
注册一个 MessageListener 实现异步接收。多个 Consumer 可以注册到同一个 queue 上,但一个消息只能被一个 Consumer 所接收,
然后由该 Consumer 来确认消息。并且在这种情况下,Provider 对所有注册的 Consumer 以轮询的方式发送消息。
2.2 Pub/Sub(发布/订阅发布订阅):使用topic作为通信载体, 消息域使用 topic 作为 Destination,发布者向 topic 发送消息,
订阅者注册接收来自 topic 的消息。发送到 topic 的任何消息都将自动传递给所有订阅者。接收方式(同步和异步)与 P2P 域相同。
除非显式指定,否则 topic 不会为订阅者保留消息。当然,这可以通过持久化(Durable)订阅来实现消息的保存。这种情况下,当
订阅者与 Provider 断开时,Provider 会为它存储消息。当持久化订阅者重新连接时,将会受到所有的断连期间未消费的消息。
3:消息中间件的场景
3.1 异步通信
有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。
想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
3.2 解耦
降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过
消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,
可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
3.3 冗余
有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全
处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,
需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
3.4 扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、
不需要调节参数。便于分布式扩容。
3.5 过载保护
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来
投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
3.6 可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中
的消息仍然可以在系统恢复后被处理。
3.7 顺序保证
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。
3.8 缓冲
在任何重要的系统中,都会有需要不同的处理时间的元素。消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于
控制和优化数据流 经过系统的速度。以调节系统响应时间。
3.9 数据流处理
分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行
大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。
4:消息中间件的使用案例
4.1:点对点消息队列,自己发送消息自己获取消息实现类,消息使用的自动确认机制
package com.zeronode;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 类说明 queue点对点消息队列 <br>
*
* @author dongfang<br>
* @version 创建时间:2019年4月3日 下午3:51:46<br>
*
*/
public class QueueDemo {
/**
* @param args
*/
public static void main(String[] args) {
try {
//第一步:创建工厂信息
ConnectionFactory factory= new ActiveMQConnectionFactory("tcp://localhost:61616");
//第二步:通过工厂创建连接
Connection connection = factory.createConnection();
//第三步:开启连接信息
connection.start();
//通过连接创建会话信息
//connnection.createSession(boolean,int)
//参数一:boolean值:true 或者 false 当前填写true 标识对事物支持 参数二值被忽略 默认值为:SESSION_TRANSACTED
// 当前填写false 标识对事物不支持 参数二会有三种情况值
//Session.AUTO_ACKNOWLEDGE: 为自动确认,客户端发送和接收消息不需要做额外的工作。
//Session.CLIENT_ACKNOWLEDGE: 为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。
//Session.DUPS_OK_ACKNOWLEDGE: 允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,
// 会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//设置队列的标题名称
Destination destination = session.createQueue("test-queue");
//创建会话的发送对象
MessageProducer producer = session.createProducer(destination);
//穿件发送的消息内容
TextMessage msg = session.createTextMessage("我是队列消息");
//通过发送对象发送消息
producer.send(msg);
//同步会话获取当前消息
MessageConsumer consumer = session.createConsumer(destination);
TextMessage textMessage = (TextMessage)consumer.receive();
System.out.println(textMessage.getText());
//异步会话获取当前消息
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//关闭资源
producer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
执行结果:
我是队列消息
4.2:点对点消息队列,发送和接收分开处理类,消息使用的自动确认机制
package com.zeronode;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 类说明 queue 点对点消息队列 发送消息<br>
*
* @author dongfang<br>
* @version 创建时间:2019年4月3日 16:24:48<br>
*
*/
public class QueueProducer {
/**
* @param args
*/
public static void main(String[] args) {
try {
//第一步:创建工厂信息
ConnectionFactory factory= new ActiveMQConnectionFactory("tcp://localhost:61616");
//第二步:通过工厂创建连接
Connection connection = factory.createConnection();
//第三步:开启连接信息
connection.start();
//通过连接创建会话信息
//connnection.createSession(boolean,int)
//参数一:boolean值:true 或者 false 当前填写true 标识对事物支持 参数二值被忽略 默认值为:SESSION_TRANSACTED
// 当前填写false 标识对事物不支持 参数二会有三种情况值
//Session.AUTO_ACKNOWLEDGE: 为自动确认,客户端发送和接收消息不需要做额外的工作。
//Session.CLIENT_ACKNOWLEDGE: 为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。
//Session.DUPS_OK_ACKNOWLEDGE: 允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,
// 会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//设置队列的标题名称
Destination destination = session.createQueue("test-queue");
//创建会话的发送对象
MessageProducer producer = session.createProducer(destination);
//穿件发送的消息内容
TextMessage msg = session.createTextMessage("我是队列消息");
//通过发送对象发送消息
producer.send(msg);
//关闭资源
producer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
package com.zeronode;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 类说明 queue 点对点消息队列 接收消息<br>
*
* @author dongfang<br>
* @version 创建时间:2019年4月3日 16:24:54<br>
*
*/
public class QueueConsumer {
/**
* @param args
*/
public static void main(String[] args) {
try {
//第一步:创建工厂信息
ConnectionFactory factory= new ActiveMQConnectionFactory("tcp://localhost:61616");
//第二步:通过工厂创建连接
Connection connection = factory.createConnection();
//第三步:开启连接信息
connection.start();
//通过连接创建会话信息
//connnection.createSession(boolean,int)
//参数一:boolean值:true 或者 false 当前填写true 标识对事物支持 参数二值被忽略 默认值为:SESSION_TRANSACTED
// 当前填写false 标识对事物不支持 参数二会有三种情况值
//Session.AUTO_ACKNOWLEDGE: 为自动确认,客户端发送和接收消息不需要做额外的工作。
//Session.CLIENT_ACKNOWLEDGE: 为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会删除消息。
//Session.DUPS_OK_ACKNOWLEDGE: 允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,
// 会话对象就会确认消息的接收;而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//设置队列的标题名称
Destination destination = session.createQueue("test-queue");
//创建会话的发送对象
MessageProducer producer = session.createProducer(destination);
//穿件发送的消息内容
TextMessage msg = session.createTextMessage("我是队列消息");
//通过发送对象发送消息
producer.send(msg);
//同步会话获取当前消息
MessageConsumer consumer = session.createConsumer(destination);
TextMessage textMessage = (TextMessage)consumer.receive();
System.out.println(textMessage.getText());
//异步会话获取当前消息
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//关闭资源
producer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
执行效果:
我是队列消息
以上是点对点消息队列的自动确认消息机制,如果需要修改成确认操作才让消息从中间件中删除,需要把接收消息方的connection.createSession(false,Session.CLICK_CLIENT_ACKNOWLEDGE)即可,然后在消息获取地方添加消息确认
代码 textMessage.acknowledge();