在生产环境中,由于 Spring 对 RabbitMQ 提供了一些方便的注解,所以首先可以使用这些注解。例如:
具体这些注解的使用,可以参考这里的代码:点这里
首先,生产环境下的 RabbitMQ 可能不会在生产者或者消费者本机上,所以需要重新定义 ConnectionFactory,即:
@Bean
ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setUsername(userName);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(vhost);
return connectionFactory;
}
这里,可以重新设置需要连接的 RabbitMQ 的 ip,端口,虚拟主机,用户名,密码。
然后,可以先从生产端考虑,生产端需要连接 RabbitMQ,那么可以通过 RabbitTemplate 进行连接。 Ps:(RabbitTemplate 用于生产端发送消息到交换机中),如下代码:
@Bean(name="myTemplate")
RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(integrationEventMessageConverter());
template.setExchange(exchangeName);
return template;
}
在该代码中,new RabbitTemplate(connectionFactory);
设置了生产端连接到RabbitMQ,template.setMessageConverter(integrationEventMessageConverter());
设置了 生产端发送给交换机的消息是以什么格式的,在 integrationEventMessageConverter()
代码中:
public MessageConverter integrationEventMessageConverter() {
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
return messageConverter;
}
如上 Jackson2JsonMessageConverter
指明了 JSON。上述代码的最后 template.setExchange(exchangeName);
指明了 要把生产者要把消息发送到哪个交换机上。
有了上述,那么,我们即可使用 rabbitTemplate.convertAndSend("spring-boot", xxx);
发送消息,xxx 表示任意类型,因为上述的设置会帮我们把这些类型转化成 JSON 传输。
接着,生产端发送我们说过了,那么现在可以看看消费端:
对于消费端,我们可以只创建 SimpleRabbitListenerContainerFactory
,它能够帮我们生成 RabbitListenerContainer,然后我们再使用 @RabbitListener 指定接收者收到信息时处理的方法。
@Bean(name="myListenContainer")
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setMessageConverter(integrationEventMessageConverter());
factory.setConnectionFactory(connectionFactory());
return factory;
}
这其中 factory.setMessageConverter(integrationEventMessageConverter());
指定了我们接受消息的时候,以 JSON 传输的消息可以转换成对应的类型传入到方法中。例如:
@Slf4j
@Component
@RabbitListener(containerFactory = "helloRabbitListenerContainer",queues = "spring-boot")
public class Receiver {
@RabbitHandler
public void receiveTeacher(Teacher teacher) {
log.info("##### = {}",teacher);
}
}
可能出现的问题:
在生产环境中,我们需要考虑万一生产者挂了,消费者挂了,或者 rabbitmq 挂了怎么样。一般来说,如果生产者挂了或者消费者挂了,其实是没有影响,因为消息就在队列里面。那么万一 rabbitmq 挂了,之前在队列里面的消息怎么办,其实可以做消息持久化,RabbitMQ 会把信息保存在磁盘上。
做法是可以先从 Connection 对象中拿到一个 Channel 信道对象,然后再可以通过该对象设置 消息持久化。
这里 Spring 有自动重连机制。
每个Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中,Consumer出错了,异常退出了,而数据还没有处理完成,那么 非常不幸,这段数据就丢失了。因为我们采用no-ack的方式进行确认,也就是说,每次Consumer接到数据后,而不管是否处理完 成,RabbitMQ Server会立即把这个Message标记为完成,然后从queue中删除了。
如果一个Consumer异常退出了,它处理的数据能够被另外的Consumer处理,这样数据在这种情况下就不会丢失了(注意是这种情况下)。
为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack。而应该是在处理完数据后发送ack。
在处理数据后发送的ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以去安全的删除它了。
如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer。这样就保证了在Consumer异常退出的情况下数据也不会丢失。