1. AMQP协议
(1)二进制协议
(2)消息模型:核心exchange->message queue之间的绑定关系
(3)概念:
message 由两部分组成: properties和body,在properties中存放的是对消息的消息的修饰,如优先级、延迟。在body中存放的就是消息内容。
virtual host:一个逻辑隔离的虚拟地址。每个虚拟主机都含有若干个exchange和message queue。
exchange:交换机
bingding:exchange于message之间建立消息连接
router key:路由规则,虚拟机确定他如何路由到特定的队列上。
queue:将消息发给cosumer。
2.rabbitmq安装
安装参考:https://blog.csdn.net/qq_35688140/article/details/100191976
3. 命令行的操作和管控台操作
基础操作
rabbitmqctl stop_app
关闭应用
rabbitmqctl start_app
开启应用
rabbitmqctl status
节点状态
rabbitmqctl add_user username password
新增用户
rabbitmqctl list_users
列出所有用户
rabbitmqctl delete_users username
删除用户
rabbitmqctl clear_permissions -p vhostpath username
清除用户权限
rabbitmqctl list_user_permissions username
列出用户权限
rabbitmqctl change_password username newpasswor
d 修改密码
rabbitmqctl set_permissions -p vhostpath username ".*" ".* " ".*"
设置用户权限
虚拟主机
rabbitmqctl add_vhost vhostpath
创建虚拟主机
rabbitmqctl delete_vhost vhostpath
删除虚拟主机
rabbitmqctl list_vhosts
列出所有的虚拟主机
rabbitmqctl list_permissions -p vhostpath
列出虚拟主机上所有的权限
队列
rabbitmqctl list_queues
查看所有队列信息
rabbitmqctl -p vhostpath purge_queue blue
清除队列里的消息
高级操作
rabbitmqctl reset
移除所有的数据,再关闭rabbitmq之后执行
rabbitmqctl jion_cluster <cluster_node> [--ram]
组成集群命令
rabbitmqctl cluster_status
查看集群状态
rabbitmqctl change_cluster_node_type disc | ram
修改数据存储方式(磁盘|内存)
rabbitmqctl forget_cluster_node [--offline]
忘记节点(将无法使用的节点忘掉,可以在rabbitmq没有启动时使用该命令修改集群信息)
4.spring整合rabbitmq
pom.xml
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
producer
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Procuder {
public static void main(String[] args) throws Exception {
//1 创建一个ConnectionFactory, 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.48.142");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
// 设置
connectionFactory.setUsername("admin");
connectionFactory.setPassword("123456");
//2 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3 通过connection创建一个Channel
Channel channel = connection.createChannel();
//4 通过Channel发送数据
for(int i=0; i < 5; i++){
String msg = "Hello RabbitMQ!";
//1 exchange 2 routingKey
// 如果不指定exchange就变成直连的方式,routingKey就是队列的名称
channel.basicPublish("", "test001", null, msg.getBytes());
}
//5 记得要关闭相关的连接
channel.close();
connection.close();
}
}
consumer
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
public class Consumer {
public static void main(String[] args) throws Exception {
//1 创建一个ConnectionFactory, 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.48.142");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("123456");
//2 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3 通过connection创建一个Channel
Channel channel = connection.createChannel();
//4 声明(创建)一个队列
String queueName = "test001";
// 参数1:队列名 参数2:设置为true启动以后队列仍可以使用 参数3: 独占队列,只有当ip可以连接该队列 参数4:自动删除
channel.queueDeclare(queueName, true, false, false, null);
//5 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6 设置Channel
// 参数1:需要去消费的队列 参数2:自动签收数据 参数3:消费者
channel.basicConsume(queueName, true, queueingConsumer);
while(true){
//7 获取消息
Delivery delivery = queueingConsumer.nextDelivery();//Delivery是一个封装类,用于获取消息
String msg = new String(delivery.getBody());
System.out.println("消费端: " + msg);
//Envelope envelope = delivery.getEnvelope();
}
}
}
测试:
5. 交换机
交换机更具routingkey将消息分发给不同的queue,queue的监听者就可以收到消息。
durability
:是否将消息持久化到磁盘。
auto delete
:当exchange上的所有的队列都被删除以后,exchange自动被删除。
5.1 直连:
通过routingkey,将消息发送给与routingkey相同的queuename的队列上。
5.2 topic交换机
根据routingkey将消息发给不同的queue,exchange中设置了routingkey和queue之间的路由规则。
如图:当路由规则是队列1与usa.#绑定,routingkey是usa.news和usa.weather的消息就会被发给这个队列1。
5.3 fanout
该交换机因为没有路由规则的匹配,所以速度是最快的。是广播模式发送消息。
5.4 header
根据消息的header头部进行匹配路由。使用场景较少。
6.springboot实现四种交换机模式
springboot整合rabbitmq
<!--rabbitmq springboot版本-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
application.prperties
########################################################################
# rabbitmq
########################################################################
spring.rabbitmq.host=192.168.48.142
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
# 虚拟多个rabbitmq默认写法,即使guest也可以访问该目录
spring.rabbitmq.virtual-host=/
#消费者最大连接数量
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
#每次连接取一个线程读写
spring.rabbitmq.listener.simple.prefetch= 1
#消费者启动
spring.rabbitmq.listener.simple.auto-startup=true
#消费者消费数据失败重试
spring.rabbitmq.listener.simple.default-requeue-rejected= true
#重试的配置
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.multiplier=1.0
四种交换机的配置(路由设置):将交换机与队列对应起来
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MQConfig {
// 直连模式
public static final String QUEUE_NAME = "queue";
// 交换机模式
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String TOPIC_EXCHANGE = "topicExchage";
// 广播类型交换机
public static final String FANOUT_EXCHANGE = "fanoutxchage";
// header模式
public static final String HEADER_QUEUE = "header.queue";
public static final String HEADERS_EXCHANGE = "headersExchage";
//秒杀
public static final String QUEUE_Miaosha = "miaosha_Queue";
/**
* 直连(direct模式)
*
* @return
*/
@Bean
public Queue queue() {
return new Queue(QUEUE_NAME, true); // 消息队列名称为queue,true表示持久化
}
/**
* 交换机模式
*/
@Bean
public Queue topicQueue1() {
return new Queue(MQConfig.TOPIC_QUEUE1, true);
}
@Bean
public Queue topicQueue2() {
return new Queue(MQConfig.TOPIC_QUEUE2, true);
}
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(MQConfig.TOPIC_EXCHANGE);
}
// 表示路由key是topic.key1的消息,进入交换机绑定到topicQueue1()消息队列上
@Bean
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("topic.key1");
}
// 表示路由key是topic.#的消息,进入交换机绑定到topicQueue2()消息队列上(#是占位符,表示0个以上的数据)
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.#");
}
/**
* fanout模式:广播
*/
@Bean
public FanoutExchange fanoutExchage() {
return new FanoutExchange(FANOUT_EXCHANGE);
}
// topicQueue1和topicQueue2都绑定到了这个广播类型的交换机上
@Bean
public Binding FanoutBinding1() {
return BindingBuilder.bind(topicQueue1()).to(fanoutExchage());
}
@Bean
public Binding FanoutBinding2() {
return BindingBuilder.bind(topicQueue2()).to(fanoutExchage());
}
/**
* header模式:指定消息体的头部信息,匹配这一头部信息才可以将数据发入到绑定的消息队列上
*/
@Bean
public HeadersExchange headersExchage() {
return new HeadersExchange(HEADERS_EXCHANGE);
}
@Bean
public Queue headerQueue1() {
return new Queue(HEADER_QUEUE, true);
}
// whereAll(map).match() 表示只有满足这个map中的<key,value>出现才会往消息队列中放入数据
@Bean
public Binding headerBinding() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("header1", "value1");
map.put("header2", "value2");
return BindingBuilder.bind(headerQueue1()).to(headersExchage()).whereAll(map).match();
}
}
接收者
@Service
public class MQReceiver {
private static Logger logger = LoggerFactory.getLogger(MQReceiver.class);
/**
* direct模式
* @param message
*/
@RabbitListener(queues = MQConfig.QUEUE_NAME) // 监听消息队列中的消息
public void receive(String message){
logger.info("收到消息:"+ message);
}
/**
* 交换机模式,和fanout模式
* @param message
*/
@RabbitListener(queues=MQConfig.TOPIC_QUEUE1)
public void receiveTopic1(String message) {
logger.info(" topic queue1 message:"+message);
}
@RabbitListener(queues=MQConfig.TOPIC_QUEUE2)
public void receiveTopic2(String message) {
logger.info(" topic queue2 message:"+message);
}
/**
* hedder模式,因为sender的数据message为byte[],此处与之匹配
*/
@RabbitListener(queues=MQConfig.HEADER_QUEUE)
public void receiveHeaderQueue(byte[] message) {
logger.info(" header queue message:"+new String(message));
}
}
发送者
@RunWith(SpringRunner.class)
@SpringBootTest
public class MQSender {
private static Logger logger = LoggerFactory.getLogger(MQReceiver.class);
@Autowired
AmqpTemplate amqpTemplate; // 操作rabbitmq的工具类
/**
* direct模式
* 发送message到rabbitmq
*/
@Test
public void send(){
String message = "直连 ";
logger.info("发送消息:"+message);
amqpTemplate.convertAndSend(MQConfig.QUEUE_NAME, message); // 向MQConfig.QUEUE_NAME发送数据message
}
/**
* 交换机模式:向指定的路由发送数据
*/
@Test
public void sendTopic() {
String message = "交换机 ";
// 向路由topic.key1和topic.key2发送数据
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key1", message+"1");
amqpTemplate.convertAndSend(MQConfig.TOPIC_EXCHANGE, "topic.key2", message+"2");
}
/**
* fanout模式:广播类型的交换机
*/
@Test
public void sendFanout() {
String message = "广播类型交换机 ";
amqpTemplate.convertAndSend(MQConfig.FANOUT_EXCHANGE, "", message);
}
/**
* header模式:匹配header头部信息的交换机
*/
@Test
public void sendHeader() {
String message = "head类型交换机 ";
//给消息体初始化,并设置消息体的头部
MessageProperties properties = new MessageProperties();
properties.setHeader("header1", "value1");
properties.setHeader("header2", "value2");
Message obj = new Message(message.toString().getBytes(), properties);
// 发送带头部的message消息体
amqpTemplate.convertAndSend(MQConfig.HEADERS_EXCHANGE, "", obj);
}
}
测试: