一、MQ概述
MQ全称Message Queue(消息队列),是在消息的传输过程中保存消息的容器,多用于分布式系统之间的通信。
二、MQ的优势
(1)应用解耦
使用MQ使得应用间解耦,提升容错性和可维护性
(2)任务异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
(3)削峰填谷
当外部请求突然增加的时候,请求会到达MQ中间件,不会直接到达下游程序。下游的程序会根据自身的处理能力,从MQ中间件中获取数据处理,不会因为服务器(数据库)压力过大造成宕机。
(4)AMQP与JMS
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,遵循此协议,不收客户端和中间件产品和开发语言限制。
JMS 即 Java 消息服务(JavaMessage Service)应用程序接口,是一个 Java 平台中关于面向消息中间件的API。JMS 是 JavaEE 规范中的一种,类比JDBC
AMQP与JMS区别
JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
JMS规定了两种消息模式;而AMQP的消息模式更加丰富
三、RabbitMQ
RabbitMQ基础架构图
RabbitMQ 中的相关概念:
Broker:接收和分发消息的应用,RabbitMQ Server就是 Message Broker
Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类
似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务
时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等
Connection:publisher/consumer 和 broker 之间的 TCP 连接
Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP
Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,
如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含
了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。
Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销
Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发
消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and
fanout (multicast)
Queue:消息最终被送到这里等待 consumer 取走
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息
被保存到 exchange 中的查询表中,用于 message 的分发依据
RabbitMQ提供6中模式:简单模式(一个producer与一个Consumer)、work模式(一个Producer与两个及以上的Consumer)、发布与订阅模式、Routing路由模式、Topocs主题模式、RPC远程调用模式。
四、RabbitMQ搭建工程
(1)简单模式
1、连接rabbitMQ服务器,创建连接
package com.zhb.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//主机地址(rabbitMQ虚拟机地址)
connectionFactory.setHost("192.168.56.114");
//连接端口:默认为 5672
connectionFactory.setPort(5672);
//rabbitMQ中虚拟主机名称
connectionFactory.setVirtualHost("/zhb");
//连接用户名:默认为guest
connectionFactory.setUsername("zhb");
//连接密码:默认为guest
connectionFactory.setPassword("zhb");
return connectionFactory.newConnection();
}
}
2、Producer
3、Consumer
(2)工作模式
工作模式与简单模式类似,都不需要声明交换机,只是多了一个或几个Consumer同时处理数据
(3)发布订阅模式
发布订阅模式: 1、每个消费者监听自己的队列。 2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。(通俗讲:每一个与交换机绑定的队列都可获得消息)
1、Producer
2、Consumer
package com.zhb.rabbitmq;
import com.rabbitmq.client.*;
import com.zhb.utils.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(Producer.FANOUT_NAME, BuiltinExchangeType.FANOUT);
//创建队列
channel.queueDeclare(Producer.FANOUT_QUEUE_1,true,false,false,null);
//队列绑定交换机
channel.queueBind(Producer.FANOUT_QUEUE_1,Producer.FANOUT_NAME,"");
Consumer consumer = new DefaultConsumer(channel){
/**
* No-op implementation of {@link Consumer#handleDelivery}.
*
* @param consumerTag 消息者标签,在channel.basicConsume时候可以指定
* @param envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重
* 传标志(收到消息失败后是否需要重新发送)
* @param properties
* @param body 接受到的消息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//路由key
System.out.println("路由key为:" + envelope.getRoutingKey());
//交换机
System.out.println("交换机为:" + envelope.getExchange());
//消息id
System.out.println("消息id为:" + envelope.getDeliveryTag());
//收到的消息
System.out.println("消费者1-接收到的消息为:" + new String(body, "utf-8"));
}
};
channel.basicConsume(Producer.FANOUT_QUEUE_1,true,consumer);
}
}
在Consumer中绑定队列和交换机路由也为空
(3)路由模式(Routing)
路由模式有通配符模式类似,只是路由模式指定具体的路由名称,然而通配符模式由“*”指定匹配一个词、“#”指定匹配一个或者多个词。详情可以参照通配符模式代码。