1.RabbitMQ
是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
2.RabbitMQ应用场景
跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。
流量削峰一般在秒杀活动中应用广泛。
3.rabbitmq 有哪些重要的角色?
RabbitMQ 中重要的角色有:生产者、消费者和代理:
生产者:消息的创建者,负责创建和推送数据到消息服务器;
消费者:消息的接收方,用于处理数据和确认消息;
代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
4.rabbitmq 有哪些重要的组件?
ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。
Channel(信道):消息推送使用的通道。我们必须连接Rabbit服务,才能消费或者发布消息,
Exchange(交换器):用于接受、分配消息。
Queue(队列):用于存储生产者的消息。
RoutingKey(路由键):用于把生成者的数据分配到交换器上。
BindingKey(绑定键):用于把交换器的消息绑定到队列上。
5.rabbitmq的常见工作模式
- fanout
- direct
- topic
- headers
普通模式
一个队列只有一个消费者,生产者将消息发送到队列,消费者从队列中取出消息
Fanout Exchange:直接将消息转发到所有binding的对应queue中,这种exchange在路由转发的时候,忽略Routing key。
订阅模式
一个交换机绑定多个消息队列,每个消息队列有一个消费者监听,生产者发送的消息可以被每一个消费者接收
Direct Exchange:将消息中的Routing key与该Exchange关联的所有Binding中的Routing key进行比较,如果相等,则发送到该Binding对应的Queue中。
路由模式
一个交换机绑定多个消息队列,每个消息队列都有自己唯一的key,每一个消息队列有一个消费者监听
Topic Exchange:将消息中的Routing key与该Exchange关联的所有Binding中的Routing key进行对比,如果匹配上了,则发送到该Binding对应的Queue中。
Headers Exchange:将消息中的headers与该Exchange相关联的所有Binging中的参数进行匹配,如果匹配上了,则发送到该Binding对应的Queue中。
6.参数详解
Virtual host:属于哪个Virtual host。
Name:名字,同一个Virtual host里面的Name不能重复。
Durability: 是否持久化,Durable:持久化。Transient:不持久化。
Auto delete:当最后一个绑定(队列或者exchange)被unbind之后,该exchange自动被删除。
Internal: 是否是内部专用exchange,是的话,就意味着我们不能往该exchange里面发消息。
Arguments: 参数,是AMQP协议留给AMQP实现做扩展使用的。
alternate_exchange配置的时候,exchange根据路由路由不到对应的队列的时候,这时候消息被路由到指定的alternate_exchange的value值配置的exchange上
Virtual host
7.Spring对RabbitMQ的整合
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
application.yaml
spring:
rabbitmq:
host: 101.200.72.***
username: hdh
password: ******
port: 5672
virtual-host: /msg
template:
retry:
#重试开启
enabled: true
#10秒后重试
initial-interval: 10000ms
#最大的重试周期
max-interval: 30000ms
#重试周期倍数 initial-interval*multiplier
multiplier: 2
#默认的交换机的名称(不写使用默认的)
exchange:
#开启生产者消息发送失败重试
publisher-confirms: true
如何发送消息到队列或者交换机
@Autowired
private AmqpTemplate amqpTemplate;
String msg="hello spring boot mq";
//向交换机或者队列发送消息 队列、交换机必须存在
this.amqpTemplate.convertAndSend("spring.MQ.queue","a.b",msg);
如何处理消息
不需要创建RabbitMQ的消息队列
@Component
public class StatementQueueExchangeCopy {
@RabbitListener(queues = "spring.MQ.queue")
private void listen(String msg) {
System.out.println("[接受消息]" + msg);
}
}
需要在程序启动的时候创建RabbitMQ的消息队列
@Component
//程序运行时创建好队列
public class StatementQueueExchange {
@RabbitListener(bindings = @QueueBinding(
//创建队列spring.MQ.queue 默认持久化
value = @Queue(value = "spring.MQ.queue", declare = "true"),
//创建交换机spring.MQ.exchange
exchange = @Exchange(
value = "spring.MQ.exchange",
type = ExchangeTypes.TOPIC
),
//绑定规则
key = {"#.#"}
))
private void listen(String msg) {
System.out.println("[接受消息]" + msg);
}
}
Spring默认开启了消息持久化
8.手写一个RabbitMQ消息的生产和消费
消息生产
public class Send {
private final static String EXCHANGE_NAME = "durable_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
//获取到连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明exchange,指定类型fanout 交换机持久化
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT, true);
String message = "helle everyone";
//发布消息到exchange交换机
for (int i = 1; i < 100; i++) {
channel.basicPublish(EXCHANGE_NAME, "", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
System.out.println("生产者send:" + message);
}
channel.close();
connection.close();
}
}
消息消费
public class Receive {
private final static String EXCHANGE_NAME = "durable_exchange";
private final static String QUERY_NAME = "durable";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//获取队列 队列持久化
channel.queueDeclare(QUERY_NAME, true, false, false, null);
//将队列和交换机绑定
channel.queueBind(QUERY_NAME, EXCHANGE_NAME, "");
//定义对队列的消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println("消费者1:" + msg);
}
};
//ack确认机制
channel.basicConsume(QUERY_NAME, true, consumer);
}
}