RabbitMQ消费端配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
listener:
simple:
# acknowledge-mode: manual # 手动确定(默认自动确认)
concurrency: 1 # 消费端的监听个数(即@RabbitListener开启几个线程去处理数据。)
max-concurrency: 10 # 消费端的监听最大个数
prefetch: 10
connection-timeout: 15000 # 超时时间
在消费端,配置prefetch
和concurrency
参数便可以实现消费端MQ并发处理消息,那么这两个参数到底有什么含义??
1. prefetch
每个customer会在MQ预取一些消息放入内存的LinkedBlockingQueue中,这个值越高,消息传递的越快,但非顺序处理消息的风险更高。如果ack模式为none,则忽略。如有必要,将增加此值以匹配txSize或messagePerAck。从2.0开始默认为250;设置为1将还原为以前的行为。
prefetch
默认值以前是1,这可能会导致高效使用者的利用率不足。从spring-amqp 2.0版开始,默认的prefetch
值是250,这将使消费者在大多数常见场景中保持忙碌,从而提高吞吐量。
不过在有些情况下,尤其是处理速度比较慢的大消息,消息可能在内存中大量堆积,消耗大量内存;以及对于一些严格要求顺序的消息,prefetch
的值应当设置为1。
对于低容量消息和多个消费者的情况(也包括单listener容器的concurrency配置)希望在多个使用者之间实现更均匀的消息分布,建议在手动ack下并设置prefetch=1
。
模拟:
生产者每次生产10条消息:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
publisher-confirms: true
publisher-returns: true
@RestController
public class RabbitMQController {
@Autowired
private RabbitTemplate rabbitTemplate;
//直接向队列中发送数据
@GetMapping("send")
public String send() {
for (int i = 0; i < 10; i++) {
String content = "Date:" + System.currentTimeMillis();
content = content + ":::" + i;
rabbitTemplate.convertAndSend("kinson", content);
}
return "success";
}
}
控制页面:
消费端直接预取了10条消息.png
2. concurrency
上面配置中,concurrency =1
,即每个Listener
容器将开启一个线程去处理消息。
在2.0版本后,可以在注解中配置该参数:
@Component
@Slf4j
public class CustomerRev {
//会覆盖配置文件中的参数。
@RabbitListener(queues = {"kinson"},concurrency = "2")
public void receiver(Message msg, Channel channel) throws InterruptedException {
// Thread.sleep(10000);
byte[] messageBytes = msg.getBody();
if (messageBytes != null && messageBytes.length > 0) {
//打印数据
String message = new String(msg.getBody(), StandardCharsets.UTF_8);
log.info("【消3】:{}", message);
}
}
}
启动服务:
可以看到MQ有两个消费者.png
即该Listener容器产生了两个线程去消费queue。如果在Listener配置了exclusive
参数,即确定此容器中的单个customer是否具有对队列的独占访问权限。如果为true,则容器的并发性必须为1。
关于线程池和消息队列的一些理解(参考文章)
1.两者内部都使用了队列,如阻塞队列、优先级队列;
2.使用线程池时应用服务器既充当生产者又充当消费者,也是消息队列中间件的实现者,使用消息队列时中间件、生产者、消费者可以部署在不同的应用机器上(当然也可以部署在一台服务器上但很少有人这么用);
3.出于第2点线程池更适合非分布式的系统,但在分布式架构下消息队列明显是更优项;
4.使用消息队列会带来额外的网络开销;
5.消息队列的耦合性更低,可扩展性更好,适用于弱一致性的场景,如对log日志的解耦;
6.消息队列自动实现消息的持久化,中间已经实现了大量功能,如消息转发、消息拒绝、消息重试,以及对消息的一些监控,例如消息的消费状态、消息的消费速率等,使用线程池如果需要很多功能还要自己去实现,例如需要执行状态需要打印队列数量、计算消息消费速度;
7.在不同系统间的服务调用(调用协议也可能不一致)线程池很难实现或开销很大,这时候消息队列可以屏蔽不同机器或不同协议的问题;
8.使用消息队列会提升系统的复杂度,网络抖动怎么办?最大队列长度怎么设置?超时时间又设置多少?Qos又设置为多少?消费者多少个比较合适?Channel cache size又该设置为多少?业务线可能都是用同一个Mq,你占资源太多,或者设计不当可能会导致整个Mq故障。