RabbitMQ_1.0
MQ概述
MQ(message Queue)消息队列,即传输过程中保存消息的容器。一般用于分布式系统之间进行通信。
分布式系统通信方式:RPC(远程调用)和第三方完成间接通信(MQ)
- MQ优势:应用解耦、异步提速、削峰填谷(中间件来承载请求,一次能处理请求的峰值变高了,系统处理请求速度是不变的)
- 劣势:系统可用性降低(MQ坏掉了)、复杂度提高(要处理消息传递的顺序、丢失情况)、一致性问题(保证消息处理的一致 不能B成功了 C失败了)
什么时候用MQ?
- 生产者不需要从消费者那边得到反馈。(消费者不用给生产者数据,影响接下来的操作)
- 容许短暂的不一致性(数据处理不同步,MQ告诉用户系统已经处理好了 但其实MQ之后才将消息给订单系统去处理)
- 利大于弊(毕竟管理MQ也要成本)
RabbitMQ
AMQP
Advanced Message Queuing Protocol (高级消息队列协议),网络协议的一种,是应用层协议的一个开放标准。
JMS
java消息服务应用程序接口(可以理解为JDBC面向数据库通信,JMS面向消息队列通信)
简单模式
引入依赖
<dependencies>
<!--rabbitmq java客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
生产者
public class Producer_HelloWorld {
public final static String QUENE_NAME="hello_world";
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置参数
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/lxw");
factory.setUsername("lxw");
factory.setPassword("lxw");
//创建连接Connection
Connection connection = factory.newConnection();
//创建Channel
Channel channel = connection.createChannel();
/*
创建队列Queue
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
参数:
1.queue:队列名称
2.durable:是否持久化,当MQ重启之后,还存在
3.exclusive:
是否独占,只能有一个消费者监听队列
当connection关闭时,是否删除队列
4.outDelete:是否自动删除(没有consumer时)
5.arguments:参数
如果有队列则不创建,没有就创建
*/
channel.queueDeclare(QUENE_NAME,true,false,false,null);
/*
发送消息
basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
参数:
1.exchange:交换机名称。简单模式下使用默认""
2.routingKey:路由名称
3.props:配置信息
4.body:发送消息数据
*/
String body="Hello RabbitMQ!";
channel.basicPublish("", QUENE_NAME, null, body.getBytes());
//释放资源
channel.close();
connection.close();
}
}
消费者
public class Consumer_HelloWorld {
public final static String QUEUE_NAME="hello_world";
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2. 设置IP
factory.setHost("localhost");
factory.setPort(5672);
factory.setVirtualHost("/lxw");
factory.setUsername("lxw");
factory.setPassword("lxw");
//3. 创建连接 Connection
Connection connection = factory.newConnection();
//4. 创建Channel
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,true,false,false,null);
// 6.定义队列的消费者,并设置消息处理
Consumer consumer = new DefaultConsumer(channel){
/**
* @param consumerTag 消息者标签,在channel.basicConsume时候可以指定
* @param envelope 消息包的内容,可从中获取消息id,消息routingKey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
* @param properties 属性信息
* @param body 消息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg=new String(body);
System.out.println("接受到消息:"+msg);
}
};
/**
* 7.监听队列
* basicConsume(String queue, boolean autoAck, Consumer callback)
* queue: 队列名称
* autoAck:设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息
* callback 消息接收到后回调
*/
channel.basicConsume(QUEUE_NAME,true,consumer);
//不关闭资源,消费者应该一直监听消息
}
}
工作模式
work queue模式
在一个队列中,多个消费者共同消费同一个队列中的消息,一个消息只能被一个消费者获取。多个消费者存在竞争关系。
生产者
for(int i=0;i<10;i++){
String body=i+"Hello RabbitMQ!";
channel.basicPublish("", QUEUE_NAME, null, body.getBytes());
}
消费者(Consumer_WorkQueues1 和Consumer_WorkQueues2)
/*
执行两个消费者,监听统一队列 接着执行生产者
消费者1:
接受到消息:0Hello RabbitMQ!
接受到消息:2Hello RabbitMQ!
接受到消息:4Hello RabbitMQ!
接受到消息:6Hello RabbitMQ!
接受到消息:8Hello RabbitMQ!
消费者2:
接受到消息:1Hello RabbitMQ!
接受到消息:3Hello RabbitMQ!
接受到消息:5Hello RabbitMQ!
接受到消息:7Hello RabbitMQ!
接受到消息:9Hello RabbitMQ!
*/
Pub/Sub订阅模式(Fanout)
相比于之前的模式,多了一个Exchange角色(交换机),生产者P的消息不再直接发送到队列中,而是优先发给交换机X。
交换机:接收生产者发送的消息。处理消息(递交给某个特别队列,递交给所有队列,丢弃消息)。这里要提一点:交换机只负责转发消息,它不能存储消息。如果没有任何队列和交换机绑定或者没有符合路由规则的队列,消息将会丢失。
交换机类型
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key的队列
- Topic:通配符,把消息交给符合routing pattern的队列
/*
创建交换机
String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments
参数:
1.exchange:交换机名称
2.type:交换机类型
3.durable:是否持久化
4.autoDelete:自动删除
5.internal:内部使用,一般是false
6.arg:参数
*/
String exchangeName="test_fanout";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);
//创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//绑定队列和交换机
channel.queueBind(queue1Name,exchangeName,"");//这里的空字符串是routingkey
channel.queueBind(queue2Name,exchangeName,"");
String body="日志信息:张三调用了findAll方法";
//发送消息
channel.basicPublish(exchangeName,"",null,body.getBytes());
Routing路由模式(direct)
在路由模式中,添加了一个功能 (只能订阅一部分消息)。不同的消息被不同的队列消费。 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key),消息会转发给符合routingkey的队列。
和Fanout模式区别不大,就是多了一个RoutingKey用来限定交换机和队列的绑定
Topics通配符模式
与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符,更加灵活。
Routingkey
一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: goods.error
通配符规则:
#:匹配一个或多个词
*:匹配不多不少恰好1个词
SpringBoot整合RabbitMQ
springboot-producer
//1.引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
//2.编写配置信息
application.yml
# 配置RabbitMQ的基本信息
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
//3.编写配置类
@Configuration
public class RabbitMQConfig {
public static final String EXCHANGE_NAME="boot_topic_exchange";
public static final String QUEUE_NAME="boot_queue";
//交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//Queue 队列
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(QUEUE_NAME).build();
}
//队列和交换机绑定关系
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue,@Qualifier("bootExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
}
//3.注入RabbitTemplate 发送消息
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
//注入RabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"boot.hh","hello rabbitMQ~");
}
}
springboot-consumer
//坐标(依赖) 配置文件一样不再赘述
@Component
public class RabbitMQListener {
@RabbitListener(queues = "boot_queue")
public void ListenerQueue(Message message){
System.out.println(new String(message.getBody()));
}
}
小结
除此之外,RabbitMQ还有一些高级特性、应用问题以及集群的搭建需要去了解,将在之后学习并总结出知识点来。"流水不腐,户枢不蠹"谨此以自律。