一.使用场景
电商中秒杀请求,属于瞬间大流量,同一时刻会有大量的请求涌入到系统中,可能导致系统挂掉。应付这种瞬间大流量的其中一种方式,便是利用消息队列。
二.用RabbitMQ 怎么处理
我们可以在“请求” -> “处理秒杀业务的接口” 中间架一层消息中间件做“缓冲”、“缓压”处理
默认情况一下,一个listener对应一个consumer,如果我们需要用 RabbitMQ 在 “请求” - “接口” 之间充当限流缓压的角色,那便需要我们对 RabbitMQ 提出更高的要求,即支持高并发的配置。一个listener 对应多个consumer。
三.rabbitmq-produce的改动
项目使用上一篇中的项目 rabbitmq-produce、rabbitmq-consumer
3.1 在rabbitmq-produce中,新增一个ConcurrentLimitRabbitConfig配置类
在 ConcurrentLimitRabbitConfig 里面 ,我们要定义多个消费者的监听器工厂。
ConcurrentLimitRabbitConfig 的代码如下:
package com.example.rabbitmqproduce.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 并发、限流配置
*/
@Configuration
public class ConcurrentLimitRabbitConfig {
private Logger logger = LoggerFactory.getLogger(ConcurrentLimitRabbitConfig.class);
@Autowired
private CachingConnectionFactory connectionFactory;
public final static String queueName = "Queue";
public static final String exchangeName = "exchange";
private static final String bindingKey = "Routing";
/**
* 定义监听器工厂
* 单一消费者
* @return
*/
@Bean(name = "singleListenerContainer")
public SimpleRabbitListenerContainerFactory listenerContainer(){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setConcurrentConsumers(1);
factory.setMaxConcurrentConsumers(1);
factory.setPrefetchCount(1);
factory.setTxSize(1);
return factory;
}
/**
* 定义监听器工厂
* 多个消费者
* @return
*/
@Bean(name = "multiListenerContainer")
public SimpleRabbitListenerContainerFactory multiListenerContainer(){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setMessageConverter(new Jackson2JsonMessageConverter());
//并发配置 最小5个,最大10个consumer
factory.setConcurrentConsumers(5);
factory.setMaxConcurrentConsumers(10);
//限流配置 consumer单位时间内接收到消息就是50条
factory.setPrefetchCount(50);
return factory;
}
@Bean
public Queue queue() {
return new Queue(queueName);
}
@Bean
public DirectExchange exchange() {
return new DirectExchange(exchangeName);
}
@Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(exchange()).with(bindingKey);
}
}
重点,是并发和限流的配置
3.2 新建一个消息生产者ConcurrentLimitProduce
ConcurrentLimitProduce 的代码如下:
package com.example.rabbitmqproduce.produce;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 并发、限流
*/
@Component
public class ConcurrentLimitProduce {
//注入一个AmqpTemplate来发布消息
@Autowired
private AmqpTemplate rabbitTemplate;
private Logger logger = LoggerFactory.getLogger(ConcurrentLimitProduce.class);
/**
* 发送消息
*/
public void sendMessage() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "hello!并发限流";
String createTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
Map<String,Object> map=new HashMap<>();
map.put("messageId",messageId);
map.put("messageData",messageData);
map.put("createTime",createTime);
logger.info("发送的内容 : " + map.toString());
//将消息携带绑定键值:directRouting 发送到交换机directExchange
rabbitTemplate.convertAndSend("exchange", "Routing", map);
}
}
三.rabbitmq-consumer的改动
3.1 新建一个消息消费者ConcurrentLimitConsumer类
ConcurrentLimitConsumer代码如下:
package com.example.rabbitmqconsumer.consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 并发、限流
* @RabbitListener(queues = "Queue") 监听名为Queue的队列
* 在@RabbitListener中,用containerFactory指定一个监听器工厂类 做并发、限流.
* 这里用的是:
* containerFactory = “multiListenerContainer”
*/
@Component
@RabbitListener(queues = "Queue", containerFactory = "multiListenerContainer")
public class ConcurrentLimitConsumer {
@Autowired
private AmqpTemplate rabbitmqTemplate;
private Logger logger = LoggerFactory.getLogger(ConcurrentLimitConsumer.class);
/**
* 消费消息
* @RabbitHandler 代表此方法为接受到消息后的处理方法
*/
@RabbitHandler
public void receiveMessage(Map msg){
logger.info("DirectExchange消费者接收到的消息 :" + msg.toString());
}
}
重点:在@RabbitListener中,用containerFactory指定一个监听器工厂类 做并发、限流.