一、互联网大厂为什么选择RabbitMQ
①RabbitMQ是使用Erlang语音编写的,并且基于AMQP协议;
②它是一个开源的消息代理和队列服务器,可以通过普通协议实现夸语言、跨平台之间的高性能、高可靠、可用性数据共享服务。
③可以提供可靠的投递模式(confirm)、返回模式(return)
④与springAMQP整合完美,提供丰富的api
⑤集群模式丰富、表达式配置、HA模式、镜像队列模型
二、RabbitMQ 是如何做到高性能的
核心就是Erlang语言,这种语言具有和原生socket一样的低延迟,使得具有高性能特性
三、什么是AMQP高级协议
AMQP(即Advanced Message Queuing Protocol):二进制协议,通用消息队列协议规范,高级消息队列协议
Server: 又称Broker,接受客户端连接实现AMQP实体服务
Connection: 应用程序与Broker的网络连接
Channel: 网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道。客户端可以建立多个channel,每个channel代表一个会话任务。
Message: 消息服务器和应用程序之间传送的数据,由properties和Body组成。properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性;body则就是消息的内容。
Virtual host: 虚拟地址,用于进行逻辑隔离,最上层的消息路由。一个virtual host里面可以有若干个Exchange和Queue,同一个virtual host里面不能出现相同名称的Exchange和Queue。
Exchange: 交换机,接收消息;根据由键转发消息到绑定的队列。
Binding: Exchange和Queue进行虚拟连接,binding可以包含routing key。
Routing key: 一个路由规则,虚拟机通过交换机类型和路有规则确定特定的消息队列的。
四、RabbitMQ的整体架构
生产者将消息投递到Exchange,然后Exchange按照Routing key规则路由到绑定了对应的Queue中,消费者监听对应的Queue就可以接收到消息了;中间的server就是一个broker对象。
交换机类型:
- direct:直连方式,所有发送到exchange都直接转发到相同routingKey的Queue上(默认就是direct模式,按照routekey查找队列名,找不到将被抛弃)
- topic:所有发送到topic的消息被转发到所有关心Routkey中指定的topic的Queue上(exchange可以按照Queue的路由键routingKey模糊匹配)
- fanout:这种类型下的routingKey是失效的,生成者直接将消息转发至交换机下所有绑定的队列上,这种类型也是速度最快的。
Durability:是否需要持久化 ,默认为 true 表示持久化
Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange
Internal:当前Exchange是否用于RabbitMQ内部使用,默认为False
Arguments:扩展参数,用于扩展AMQP协议自制定化使用Name:交换机名称
五、安装和使用
rabbitmq-server start & //后台开启服务
rabbitmq-plugins enable rabbitmq_management //安装页面管理端插件 页面端口15672
rabbitmqctl start/stop_app/status
1、交换机类型为:direct直接转发
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 Product {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//发布一条消息
String msg = "test one message!";
Map<String, Object> map = new HashMap<>();
map.put("key1","value1");
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.headers(map) //在properties中放置一些信息
.expiration("10000")//设置这个消息10秒后将在队列中消失
.build();
channel.basicPublish("test_direct_exchange","test_direct",properties,msg.getBytes());
channel.close();
connection.close();
}
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Customer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);//自动重连
connectionFactory.setNetworkRecoveryInterval(3000);//自动重连时间间隔3秒
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//声明一个test_direct_exchange名称、类型为direct、持久化的交换机
String exchangName = "test_direct_exchange";
channel.exchangeDeclare(exchangName,"direct",true);
//声明一个队列
String queueName = "test_direct_queue";
channel.queueDeclare(queueName,true,false,false,null);
//将队列绑定到交换机上,按照键test_direct路由
channel.queueBind(queueName,exchangName,"test_direct");
//创建消费者
Consumer consumer = new DefaultConsumer(channel){
@Override //处理接受到的消息
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("接收到message:"+ new String(body,"UTF-8"));
System.err.println("接收到properties属性:"+ properties.getHeaders().get("key1"));
}
};
//监听队列,回调消费处理方法方法
channel.basicConsume(queueName,true,consumer);
}
}
2、交换机类型为:topic exchange可以按照Queue的路由键模糊匹配
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 TopicProduct {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);//自动重连
connectionFactory.setNetworkRecoveryInterval(3000);//自动重连时间间隔3秒
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//声明一个test_topic_exchange名称、类型为topic、持久化的交换机
String exchangName = "test_topic_exchange";
//路由键规则,*表示占一位、#表示模糊查询如数据库后置%效果
String routingKey1 = "topic.update";
String routingKey2 = "topic.save";
String routingKey3 = "topic.create.abc";
String msg = "生产者发送topic消息!";
channel.basicPublish(exchangName,routingKey1,null,msg.getBytes());
channel.basicPublish(exchangName,routingKey2,null,msg.getBytes());
channel.basicPublish(exchangName,routingKey3,null,msg.getBytes());
channel.close();
connection.close();
}
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TopicCustomer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);//自动重连
connectionFactory.setNetworkRecoveryInterval(3000);//自动重连时间间隔3秒
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//声明一个test_topic_exchange名称、类型为topic、持久化的交换机
String exchangName = "test_topic_exchange";
String exchangeType = "topic";
//路由键规则,*表示占一位、#表示模糊查询如数据库后置%效果
String routingKey = "topic.*";
channel.exchangeDeclare(exchangName,exchangeType);
//声明一个队列
String queueName = "test_topic_queue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exchangName,routingKey);
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println(new String(body,"UTF-8"));
}
});
}
}
3、交换机类型:fanout ,如下只要是相同的exchange不管routingkey都将接受到消息
public class FanoutCustomer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);//自动重连
connectionFactory.setNetworkRecoveryInterval(3000);//自动重连时间间隔3秒
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//声明一个test_fanout_exchange名称、类型为fanout、持久化的交换机
String exchangName = "test_fanout_exchange";
channel.exchangeDeclare(exchangName,"fanout",true);
//声明一个队列
String queueName1= "test_fanout_queue1";
String queueName2= "test_fanout_queue2";
channel.queueDeclare(queueName1,true,false,false,null);
channel.queueDeclare(queueName2,true,false,false,null);
//将队列绑定到交换机上,按照键test_fanout路由(其实这个键不起作用)
channel.queueBind(queueName1,exchangName,"test_fanout1");
channel.queueBind(queueName2,exchangName,"test_fanout2");
//创建消费者
Consumer consumer = new DefaultConsumer(channel){
@Override //处理接受到的消息
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println(new String(body,"UTF-8"));
}
};
//监听队列,回调消费处理方法方法
channel.basicConsume(queueName1,true,consumer);
channel.basicConsume(queueName2,true,consumer);
}
}
public class FanoutProduct {
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接工厂,并配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.25.128");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//创建一个连接
Connection connection = connectionFactory.newConnection();
//创建一个channel用通信
Channel channel = connection.createChannel();
//发布一条消息,设置的随意路由键
String msg = "test one message!";
channel.basicPublish("test_fanout_exchange","test_fanout",null,msg.getBytes());
channel.close();
connection.close();
}
}
六、管理端使用
针对于消费端开启手动签收消息(ACK),即 spring.rabbitmq.listener.simple.acknowledge-mode=manual
-
1、生产者发送成功消息到broker,消息未被消费,ready=1、unacked=0、total=1 如下图
-
2、生产成功发送消息到broker,然后消费端消费但未确认(ACK动作),ready=0、unacked=1、total=1 ,由于消费端未ACK,消息将重回MQ消息队列。
如下图:
-
3、生产成功发送消息到broker,然后消费端成功消费,ready=0、unacked=0、total=0 都为0如下图