一:消息中间件产生的背景

  1. 通常的程序开发都是客户端与服务器进行通讯时,客户端被访问以后,必须等待服务对象完成处理返回结果才能继续执行,这个过程是基于请求与响应的同步过程
  2. 客户端与服务器端对象的生命周期紧密耦合,客户端进程和服务端对象进程都都必须正常运行;如果由于服务对象崩溃或者网络故障导致用户的请求不可达,客户会受到异常。
  3. 同步除了可能会产生阻塞和超时的问题之外,还可能会导致接口重复提交、数据的幂等性问题
    等等一些传统做法对现在分布式项目的局限,所以产生了消息中间件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 以轮询的方式发送消息。

activemq点对点在管理界面如何查看_apache activemq

2.2 Pub/Sub(发布/订阅发布订阅):使用topic作为通信载体, 消息域使用 topic 作为 Destination,发布者向 topic 发送消息,
订阅者注册接收来自 topic 的消息。发送到 topic 的任何消息都将自动传递给所有订阅者。接收方式(同步和异步)与 P2P 域相同。
除非显式指定,否则 topic 不会为订阅者保留消息。当然,这可以通过持久化(Durable)订阅来实现消息的保存。这种情况下,当
订阅者与 Provider 断开时,Provider 会为它存储消息。当持久化订阅者重新连接时,将会受到所有的断连期间未消费的消息。

activemq点对点在管理界面如何查看_消息队列_02


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();
		}
	}
}

执行结果:

我是队列消息

activemq点对点在管理界面如何查看_apache activemq_03

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();