SpringAMQP
异步处理消息如栈的监听容器,即监听消息 Listener container for asynchronous processing of inbound messages 使用RabbitTamplate进行消息的接收和发送 RabbitTemplate for sending and receiving messages 自动创建队列、交换机,绑定。 RabbitAdmin for automatically declaring queues, exchanges and bindings
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> <version>2.6.13</version> </dependency>
2、生产者
// 在生产者代码中使用RabbitTemplate来进行消息的发送,发送到hzy_queue_name队列 public class ProducerMQ { private RabbitTemplate rabbitTemplate; @Autowired private void setRabbitTemplate(RabbitTemplate rabbitTemplate) { this.rabbitTemplate = rabbitTemplate; } public void testProducer(){ String channelName = "test_change"; String queueName = "test_queue"; String message = "hello RabbitMQ !!!!"; rabbitTemplate.convertAndSend(queueName, message); } }
3、消费者
// 在消费者代码中实现消费逻辑,并绑定到hzy_queue_name该队列 @Component public class ConsumerMQ { @RabbitListener(queues = "hzy_queue_name") public void testConsumer(String message){ System.out.println(message); } }
工作队列中,没有交换机概念,即生产者/队列/消费者,该方式做到一条消息只能被一个消费者消费。
模拟一个队列有两个消费者时出现:不论两个消费者消费速度如何,消息总是平均分配给了两给消费者。
原因:由于RabbitMQ默认有一个“消息预取”机制,也就是当队列有消息时,消费者1和消费者2会提前将消息平均拿到,然后再处理。
解决:可以在application.yml配置文件中进行配置,配置键:preFetch,可以设置该值来限制预取消息的上限。该值默认时无上限,即有多少消息,则拿多少消息。
spring:
rabbitmq:
host: 192.168.81.3 # rabbitmq主机地
port: 5672 # rabbitmq连接端口
virtual-host: / # rabbitmq虚拟主机
username: hzy # rabbitmq用户名
password: 123456 # rabbitmq密码
listener:
simple:
prefetch: 1 # 设置消息预取上限为1,即处理完了在去拿下一条消息
发布订阅中,允许将同一消息发送给多个消费者。实现方式是新增了交换机(exchange)概念。
// 生产者代码 @Autowired private RabbitTemplate rabbitTemplate; @Test void fanoutExchange() throws Exception{ String exchangeName = "hzy.exchange"; String message = "hello everyone!"; rabbitTemplate.convertAndSend(exchangeName, "", message); } // 配置类代码 @Configuration public class FanoutConfig { // 声明交换机: hzy.exchange @Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange("hzy.exchange"); } // 声明队列1: hzy.queue1 @Bean public Queue fanoutQueue1(){ return new Queue("hzy.queue1"); } // 声明队列2: hzy.queue2 @Bean public Queue fanoutQueue2(){ return new Queue("hzy.queue2"); } // 将队列1绑定到交换机 @Bean public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange); } // 将队列2绑定到交换机 @Bean public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange); } } // 消费者代码 @RabbitListener(queues = "hzy.queue1") public void fanoutQueue1(String message){ System.out.println("广播消费者1收到消息: " + message); } @RabbitListener(queues = "hzy.queue2") public void fanoutQueue2(String message){ System.out.println("广播消费者2收到消息: " + message); }
// 生产者代码 @Test void directExchange() throws Exception{ String exchangeName = "direct.exchange"; String message = "hello blue!"; rabbitTemplate.convertAndSend(exchangeName, "blue", message); } // 消费者代码 @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "direct.queue1"), exchange = @Exchange(name = "direct.exchange", type = ExchangeTypes.DIRECT), key = {"red", "blue"} )) public void directQueue1(String message){ System.out.println("directQueue1: " + message); } @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "direct.queue2"), exchange = @Exchange(name = "direct.exchange", type = ExchangeTypes.DIRECT), key = {"red", "yellow"} )) public void directQueue2(String message){ System.out.println("directQueue2: " + message); }
解决了direct中如果后续我需要在订阅美国的天气,那么就需要重新添加有关美国的routingKey。
而在Topic中Queue与Exchange指定BindingKey时可以使用通配符来进行路由。
#:代指0个或多个单词
*:代指一个单词
code实现:
// 生产者代码 @Test void topicExchange(){ String exchangeName = "topic.exchange"; String message = "hhhhhhhhhhhhhhhh"; rabbitTemplate.convertAndSend(exchangeName, "paner.news", message); rabbitTemplate.convertAndSend(exchangeName, "china.news", message); rabbitTemplate.convertAndSend(exchangeName, "china.hello", message); } // 消费者代码 @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "topic.queue1"), exchange = @Exchange(name = "topic.exchange", type = ExchangeTypes.TOPIC), key = "china.#" )) public void topicQueue1(String message){ System.out.println("topicQueue1: " + message); } @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "topic.queue2"), exchange = @Exchange(name = "topic.exchange", type = ExchangeTypes.TOPIC), key = "#.news" )) public void topicQueue2(String message){ System.out.println("topicQueue2: " + message); }
在spring中使用RabbitMQ,rabbitTemplate默认只用的消息处理对象是由org.springframework.amqp.support.converter.MessageConverter来处理。默认的实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。该方式不仅效率比较差,而且序列化后的内容比较占用内存。不推荐。消息转换器需要自定义实现。
// 创建Bean代码 // 消息转换器测试队列 @Bean public Queue messageQueue(){ return new Queue("converter.queue"); } // 发送消息代码 @Test void messageConverterExchange(){ Map<String, Object> message = new HashMap<>(); message.put("name", "胡真勇"); message.put("age", 22); message.put("sex", "男"); rabbitTemplate.convertAndSend("converter.queue", message); } 消息:rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAADdAADc2V4dAAD 55S3dAAEbmFtZXQACeiDoeecn+WLh3QAA2FnZXNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoas lR0LlOCLAgAAeHAAAAAWeA==
<!-- 导入依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency>
// 注入消息转换器Bean
// 在主启动类中或配置类中编写
@SpringBootApplication
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class, args);
}
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
重新发送消息:{"sex":"男","name":"哈哈哈","age":22}
<!-- 导入依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency>
// 注入消息转换器Bean // 在主启动类中或配置类中编写 @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } @Bean public MessageConverter messageConverter() { return new Jackson2JsonMessageConverter(); } } // 消息接收监听器 @RabbitListener(queues = "converter.queue") public void converterQueue(Map<String, Object> message){ System.out.println("converterQueue: " + message); }
输出结果:converterQueue: {sex=男, name=哈哈哈, age=22}

浙公网安备 33010602011771号