为什么要用消息队列?
异步通信,流量削峰,延迟队列等等。
RabbitMQ
基本概念
特点:
- 高可靠性,易扩展,高可用,功能丰富等;
- 支持大多数的编程语言;
- 遵循AMQP协议,使用Erlang编写;
- 也支持MQTT等其他协议。
基本逻辑架构图
4种交换器
fanout交换器
以广播的形式把消息发送到与该队列绑定的队列中去。
direct直接交换器
把消息发送到与routeKey完全匹配的队列中。
topic主题交换器
它是对direct交换器进行了扩展,它是按照一定的规则进行匹配。BingingKey可以存在两种字符 * ,# 进行模糊查询,其中* 用于代表一个单词,# 用于代表一个或者多个单词(也可以是0个)。
header交换器
它不依赖于routeKey的匹配规则来路由信息,而是根据消息的headers属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送的消息到交换器时, RabbitMQ会获取到该消息的headers,对比其中的键值对是否完全匹配队列和交换器绑定时指定的键 值对,如果匹配,消息就会路由到该队列。headers类型的交换器性能很差,不实用。
RabbitMQ的存储机制
存储机制
- 持久化消息和非持久化消息
- 但是这两种方式都会被写入磁盘
持久化消息会在消息到达队列时,就在磁盘中备份一份,当内存吃紧时,消息从内存中清 除。这会提高一定的性能。
非持久化消息一般只放在内存中,当内存压力大时,数据刷盘处理,以节省内存空间。如果要使得消息能持久化,就必须先使队列持久化
队列索引和消息存储
队列索引(index)
索引维护队列的落盘消息的信息,如消息存储地点,是否被消息者接收、是否被消费者ack等。
每个队列都有对应的索引。
消息存储(store)
消息以键值对的形式存储到文件中,一个虚拟主机上的所有队列共用一块存储。存储分为持久化存储和短暂存储,短暂存储的内容再broker重启后丢失。
消息(消息头,消息体,属性)可以直接存储在index中,也可以直接存储在store中,最佳的方式时较小的消息放在index中,较大的消息放在store中。
默认小于4096B的完整消息就可以放在index中,大于的放到store中。
下载、安装、配置RabbitMQ
原生的RabbitMQ写法
public class Producter {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
// 设置主机ip或者名称
factory.setHost("182.92.100.77");
//设置虚拟主机 url /解析为%2f
factory.setVirtualHost("/");
// 设置用户名
factory.setUsername("root");
// 设置密码
factory.setPassword("1111");
// 设置端口,rabbitmq默认为5672
factory.setPort(5672);
// 创建tcp连接 ,可以用工厂,也可以通过线程池创建
Connection connection = factory.newConnection();
// 创建信道
Channel channel = connection.createChannel();
// 创建消息队列
// 队列名
// 是否持久化
// 是否专属
// 是否自动删除
// 队列的配置 使用默认
channel.queueDeclare("myqueue", false, false, true, null);
// 交换器名称
// 交换器类型
// 是否持久化
// 是否自动删除
// 交换器的配置 默认
channel.exchangeDeclare("myex", BuiltinExchangeType.DIRECT, false, false, null);
// 绑定交换器和队列
// 队列名称
// 交换器名称
// 路由key
channel.queueBind("myqueue","myex","hello");
// 发送消息
// 交换器名称
// 路由key
// 属性
// 发送的消息
channel.basicPublish("myex","hello",null,"hello world 2".getBytes());
// 关闭信道和连接
channel.close();
connection.close();
}
}
public class GetConsumer {
public static void main(String[] args) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException, IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
// 通过uri方式连接
factory.setUri("amqp://root:1111@182.92.100.77:5672/%2f");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 队列名称 ,是否自动确认
GetResponse myqueue = channel.basicGet("myqueue", true);
byte[] body = myqueue.getBody();
System.out.println(new String(body));
channel.close();
connection.close();
}
}
整合springboot
@Configuration
public class RabbitMQConfig {
public static final String QUEUE_NAME = "myQueue";
public static final String EXCHANGE_NAME = "myExchange";
/**
* 创建消息队列
* queue: QueueName, //队列名称
* durable: true, //队列是否持久化.false:队列在内存中,服务器挂掉后,队列就没了;true:服务器重启后,队列将会重新生成.注意:只是队列持久化,不代表队列中的消息持久化!!!!
* exclusive: false, //队列是否专属,专属的范围针对的是连接,也就是说,一个连接下面的多个信道是可见的.对于其他连接是不可见的.连接断开后,该队列会被删除.注意,不是信道断开,是连接断开.并且,就算设置成了持久化,也会删除.
* autoDelete: false, //如果所有消费者都断开连接了,是否自动删除.如果还没有消费者从该队列获取过消息或者监听该队列,那么该队列不会删除.只有在有消费者从该队列获取过消息后,该队列才有可能自动删除(当所有消费者都断开连接,不管消息是否获取完)
* arguments: null //队列的配置
* @return
*/
@Bean
public Queue myQueue() {
return new Queue(QUEUE_NAME);
}
/**
* 创建交换器
* 参数含义分别是:交换器名称、交换器类型(这里省略,使用的是默认类型的交换器)、是否持久化(默认持久化)、是否自动删除(默认不删除)、交换器熟悉(Map集合)
* @return
*/
@Bean
public Exchange myExchange() {
return new DirectExchange(EXCHANGE_NAME,false,false,null);
}
@Bean
public Binding myBind() {
return new Binding(QUEUE_NAME, Binding.DestinationType.QUEUE, EXCHANGE_NAME, "direct.biz.ex",null);
}
}
@Component
public class ConsumerController {
@RabbitListener(queues = "myQueue")
public void service(String message) {
System.out.println("消息" + message);
}
}
@RestController
public class ProController {
@Autowired
private AmqpTemplate rabbitTemplate;
@GetMapping("/send/{message}")
public String sendMessage(@PathVariable String message) {
rabbitTemplate.convertAndSend("myExchange","direct.biz.ex",message);
return "ok";
}
}
配置文件
server.port=8001
spring.application.name=springboot_rabbitmq
spring.rabbitmq.host=主机号
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=root
spring.rabbitmq.password=1111
spring.rabbitmq.port=5672
整合到springcloud
- 配置文件
- 创建队列、交换器等的代码写在config包下
- 队列、交换器的名称一般以常量形式,放在常量类中。