目录
一、什么是消息队列
二、AMQP和JMS的区别
三、消息的分发策略:
四、在docker安装rabbitmq
五、五种消息模型
1.第一种模型
2.第二种模型:work工作
3.第三种模型:fanout广播
4.第四种模型:router direct静态路由
5.第五种模型:Topics动态路由
六、rabbitmq整合springboot
七、Docker搭建rabbitmq集群
一、什么是消息队列
MQ全称为Message Queue,即消息队列。“消息队列”是在消息的传输过程中保存消息的容器。它是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。
消息中间件负责数据传递、存储和消息分发三个部分,数据的存储和分发过程中肯定要遵循某种规范,在tcp/ip 之上建立一种新的协议AMQP
二、AMQP和JMS的区别
MQ是消息通信的模型,并发具体实现。现在实现MQ的有两种主流方式:AMQP、JMS。
区别:
JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
JMS规定了两种消息模型;而AMQP的消息模型更加丰富
AMQP特性:
1.支持分布式事务的支持,
2.消息的持久化,
3.高性能高可靠的消息优势,
为什么消息中间件不采用HTTP协议呢?
因为消息队列不需要这么复杂,它的请求报文头和响应报文头太过于复杂,影响性能,大部分HTTP协议都是短连接的,在实际的交互过程中,一个请求到响应可能会发生中断,消息中间件可能是一个长期的获取消息的过程,目的是为了保证数据和消息的稳健运行。
总结两点:1.不需要那么复杂,2.需要长连接。
三、消息的分发策略:
消息的队列中有如下的几个角色:
1.生产者
2.消费者
3.存储消息
消息分发策略对比:
发布订阅:
轮询分发(公平): 不会造成数据倾斜,无论服务器性能怎样,都会是公平的
公平分发(倾斜):会造成数据倾斜,会根据服务器性能,能者多劳模式,当数据被一个消费者消费,那么剩下的消费者都不会消费这一个消息
重发:保证消息可靠性的一种机制
消息拉取:
在上面几种策略中rabbitmq都是支持的
kafka追接近底层运行速度高
消息高可用高可靠
无论服务器出现什么问题,服务都不能出现宕机,用集群部署实现高可用
集群模式1.主从共享数据:共享同一块数据空间,在小规模会用到
集群模式2.主从同步部署:会造成消息副本
集群模式3.多住集群同步部署:
集群模式4.:多主集群转发部署
用户级别:
- administrator:可以登录控制台、查看所有信息、可以对 rabbitmq进行管理
- monitoring:监控者 登录控制台,查看所有信息
- policymaker:策略制定者 登录控制台,指定策略
- managment 普通管理员 登录控制台
四、在docker安装rabbitmq
docker这项虚拟化容器化技术让我们安装一些环境的时候省去配置环境变量的操作,只需要将端口映射出来,然后去连接即可。安装命令如下
//安装rabbitmq容器
docker pull rabbitmq
//运行rabbitmq的同时将端口映射
docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5673:5672 rabbitmq
//通过docker ps -a查看部署的mq容器id,在通过 docker exec -it 容器id /bin/bssh 进入容器内部在
运行rabbitmq-plugins enable rabbitmq_management
我这里使用云服务器连接安装的docker,然后在本机上连接rabbitmq,登录名密码没设置的话默认都是guest
五、五种消息模型
环境搭建,导入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.2</version>
</dependency>
然后连接封装成一个工具类,减少冗余代码
package rabbitmq.utils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitmqUtils {
private static ConnectionFactory connectionFactory;
static {
connectionFactory = new ConnectionFactory();
//2创建连接主机
connectionFactory.setHost("47.106.123.32");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/xy"); //设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
}
public static Connection getConnection() {
try {
return connectionFactory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void CloseConnection(Channel channel,Connection connection){
try {
if (channel != null && channel.isOpen()){
channel.close();
}
if (connection != null &&connection.isOpen()){
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
1.第一种模型
简单模型
消费者
package rabbitmq.simple;
import com.rabbitmq.client.*;
import org.junit.Test;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
// 消费者
public class Consumer {
@Test
public void test() throws IOException, TimeoutException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = null;
channel = connection.createChannel();
//绑定队列
channel.queueDeclare("hello",false,false,false,null);
/**
* 1.队列名称
* 2.开始消息的自动确认机制
* 3.消费时的回调接口
* 4。队列中取出的消息
*/
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("new String(body) =" + new String(body));
}
});
}
}
生产者
package rabbitmq.simple;
import com.rabbitmq.client.*;
import org.junit.Test;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
// 消费者
public class Consumer {
@Test
public void test() throws IOException, TimeoutException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = null;
channel = connection.createChannel();
//绑定队列
channel.queueDeclare("hello",false,false,false,null);
/**
* 1.队列名称
* 2.开始消息的自动确认机制
* 3.消费时的回调接口
* 4。队列中取出的消息
*/
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("new String(body) =" + new String(body));
}
});
}
}
2.第二种模型:work工作
公平策略,轮询(默认)
主要跟消息确认机制有关,rabbitmq默认的消息确认机制是true
生产者:
package rabbitmq.work_avg;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//通过通道声明队列
channel.queueDeclare("work",true,false,false,null);
//生产消息
for (int i=1; i<=20; i++){
channel.basicPublish("","work",null,("hello work queue").getBytes());
}
//关闭资源
RabbitmqUtils.CloseConnection(channel,connection);
}}
消费者1:
package rabbitmq.work_avg;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
for (int i = 1; i <= 20; i++) {
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:"+ new String(body));
}
});
}
}
}
消费者2:
package rabbitmq.work_avg;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
for (int i = 1; i <= 20; i++) {
channel.basicConsume("work",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:"+ new String(body));
}
});
}
}
}
能者多劳模型
我们希望消息消费者消费慢的话少消费一点,消费快的多消费一点,这与消息队列的自动确认机制有关。
生产者:
package rabbitmq.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",false,false,false,null);
//生产消息
for (int i=1;i<=20;i++){
channel.basicPublish("","work",null,("消费第"+i+"条消息").getBytes());
}
RabbitmqUtils.CloseConnection(channel,connection);
}
}
消费者1:
package rabbitmq.work;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//告诉通道每一次只消费一个消息
channel.basicQos(1);
channel.queueDeclare("work",false,false,false,null);
//第二个参数:消息自动确认机制 true 消费者自动向rabbitmq足额人消息消费
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者1:"+new String(body));
//手动确认,
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
消费者2:
package rabbitmq.work;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//一次只确认一条消息
channel.basicQos(1);
channel.queueDeclare("work",false,false,false,null);
channel.basicConsume("work",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:"+new String(body));
//开启消息手动确认,保证消息不丢失
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
}
3.第三种模型:fanout广播
消费者:
package rabbitmq.fanout;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
//广播,生产者将消息发送到交换机
public class Provider {
private static final String message = "hello,rabbitmq";
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//将通道声明指定的交换机 1.交换机名称 2.交换机类型
channel.exchangeDeclare("fanout","fanout");
//发送消息
channel.basicPublish("fanout","",null,message.getBytes());
//释放资源
RabbitmqUtils.CloseConnection(channel,connection);
}
}
生产者:
package rabbitmq.fanout;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//通道绑定交换机 参数一:交换机名称 参数二: 交换机类型
channel.exchangeDeclare("fanout","fanout");
//临时队列
String queue = channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queue,"fanout","");
//消费消息
channel.basicConsume(queue,true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1"+new String (body));
}
});
}
}
4.第四种模型:router direct静态路由
1.Direct(直连),在广播模式中,一条消息会被所有的订阅都消费,在某些场景下,我们希望不同的消息被不同的对象消费,这时就要用到Direct类型的交换机。
生产者:
package rabbitmq.routing.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
String exangename = "log_direct";
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exangename,"direct");
//交换机路由
String routingKey = "info";
channel.basicPublish(exangename,routingKey,null,"这是基于direct模型发布的".getBytes());
RabbitmqUtils.CloseConnection(channel,connection);
}
}
消费者:
package rabbitmq.routing.direct;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
String exangename = "logs_direct";
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(exangename,"direct");
//创建队列和交换机绑定
String queue = channel.queueDeclare().getQueue();
//临时队列绑定交换机
channel.queueBind(queue,exangename,"info");
//获取消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1"+new String(body));
}
});
}
}
5.第五种模型:Topics动态路由
就是在原有的direct模型上加了通配符的操作 ,极大增强代码可维护性
*匹配一个字符 #匹配多个
生产者
package rabbitmq.routing.topics;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
//获取连接对象
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机以及交换机类型
channel.exchangeDeclare("topics","topic");
//发布消息
String routerKey = "user.save";
channel.basicPublish("topics",routerKey,null,("这里是topic动态路由模型;["+routerKey+"]").getBytes());
//关闭资源
RabbitmqUtils.CloseConnection(channel,connection);
}
}
消费者
package rabbitmq.routing.topics;
import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitmqUtils;
import java.io.IOException;
// *匹配一个字符 #匹配多个
public class Consumer {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机以及交换机类型
channel.exchangeDeclare("topics","topic");
//创建一个临时队列
String queue = channel.queueDeclare().getQueue();
//基于通配符绑定队列和交换机
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:"+new String(body));
}
});
}
}
实际上rabbitmq还有RPC和发布订阅这两种模型,不过这里不介绍这两种模型
六、rabbitmq整合springboot
1.环境搭建
<!-- springboot整合rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置properties.yml文件
spring:
rabbitmq:
host: 47.106.123.32
port: 5672
username: guest
password: guest
virtual-host: /
RabbitTemplet用来操作Rabbitmq,简化操作,使用的时候直接在项目中注入即可。
七、Docker搭建rabbitmq集群
1.普通集群(副本集群)
在实际生产中很少用到这种集群架构,无法实现主从同步,只能减缓主节点压力。
2.镜像集群(高可用)
基于普通集群的基础上,搭建镜像集群,保证消息百分百不丢失。镜像集群就是将队列在三个阶段之间设置主从关系,消息在三个节点之间进行自动同步,如果其中一个节点不可用,并不会导致消息丢失或者服务不可用的情况,提升集群整体高可用性。
步骤:
1.拉取镜像
docker pull rabbitmq:3.7-management
2.创建映射数据卷目录
mkdir rabbitmqcluster
cd rabbitmqcluster/
mkdir rabbitmq01 rabbitmq02 rabbitmq03
3.创建容器
docker run -d --hostname rabbitmq01 --name rabbitmqCluster01 -v /home/software/rabbitmqcluster/rabbitmq01:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' rabbitmq:3.7-management
docker run -d --hostname rabbitmq02 --name rabbitmqCluster02 -v /home/software/rabbitmqcluster/rabbitmq02:/var/lib/rabbitmq -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' --link rabbitmqCluster01:rabbitmq01 rabbitmq:3.7-management
docker run -d --hostname rabbitmq03 --name rabbitmqCluster03 -v /home/software/rabbitmqcluster/rabbitmq03:/var/lib/rabbitmq -p 15674:15672 -p 5674:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' --link rabbitmqCluster01:rabbitmq01 --link rabbitmqCluster02:rabbitmq02 rabbitmq:3.7-management
//设置开机自动启动
docker container update --restart=always
4、容器节点加入集群
进入第一个节点,执行下列命令
docker exec -it rabbitmqCluster01 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit
接下来,进入第二个rabbitmq节点容器,执行如下命令:
docker exec -it rabbitmqCluster02 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit
紧接着,进入第三个rabbitmq节点容器,执行如下命令:
docker exec -it rabbitmqCluster03 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit
最后,实现镜像模式集群。进入rabbitmqCluster01容器中
docker exec -it rabbitmqCluster01 bash
//设置镜像队列策略(在任意一个节点上执行)
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
执行上述操作,这时候 再查看 RabbitMQ Management的overview面板中的Nodes信息,可查看到节点信息。