springboot2与rabbitmq整合

MQ作为消息队列中间件,经常会被我们用到各种环境中,例如:异步处理、削峰、解耦等。RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件),轻量、高性能、健壮、可伸缩,是经常被使用的MQ之一。

简介

在spring中使用rabbitmq,我们可以借助Spring AMQP模块。Spring AMQP包含两部分内容:spring-amqp和spring-rabbit。spring-amqp不依赖任何特定AMQP代理实现,只为提供基于AMQP的通用抽象。目前,spring针对spring-amqp的通用抽象,只基于rabbitmq做了实现,也就是这里的spring-rabbit。在spring中使用rabbitmq是非常简单的,利用提供的AmqpTemplete或者RabbitTemplete就能够很好的完成任务,至于springboot中就更加的简单了,相关的配置已经自动完成,你可以直接使用它提供的相关的工具类完成工作。具体内容就不多说了,下面我们先来一个项目了解一下。

项目搭建

在springboot中使用rabbitmq,我们需要引入spring-boot-starter-amqp,如下:

pom.xml


   org.springframework.boot
   spring-boot-starter-amqp

application.properties:

#rabbitmq配置信息
spring.rabbitmq.host=localhost
spring.rabbitmq.username=php
spring.rabbitmq.password=960202

RabbitmqConfig ,@Configuration表明该文件是一个配置文件,可以被spring加载到,该文件用于配置交换机、队列、绑定等,@EnableRabbit用于开启rabbitmq。内容如下:

@Configuration
@EnableRabbit
public class RabbitmqConfig {
    @Bean("test_queue")
    public Queue queue() {
        return new Queue("test_queue");
    }
}

Producer 生产者,RabbitTemplate是spring amqp模块提供给我们用于消息的发送与接受。使用如下:

@Component
public class Producer {
    private final RabbitTemplate rabbitTemplate;
    @Autowired
    public Producer(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }
    public void produce(String message) {
        this.rabbitTemplate.convertAndSend("test_queue", message);
    }
}

Consumer 消费者,@RabbitListener用作异步消息监听,是极其优雅的一种消息消费方式:

@Component
public class Consumer {
    @RabbitListener(queues = "test_queue")
    public void consume(String message) throws IOException {
        System.out.println("接受到消息:" + message);
    }
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
    @Resource
    private Producer producer;
    @Test
    public void contextLoads(){
        this.producer.produce("hello world");
    }
}

大家可以发现,在springboot中使用rabbitmq是如此的简单,几个注解加上rabbitmq配置就可以解决,至于内部是如何实现的,接下来是我们需要了解的重点了。另外在这里我没有介绍rabbitmq一些常见的知识点,例如:exchange(交换机),binding(绑定),routing key(路由键)等,这些东西都是比较基础的,大家应该都比较清楚,若有需要,后续我再写一篇文章,详细介绍这些内容,本文不再描述。

源码分析

初步使用都是比较简单的,看看demo就可以写出来,但是只有了解其内部,我们才能掌握的更加熟练,遇到问题时,才能够迎刃而解,最主要的是避免项目使用过程中留坑。首先了解一下rabbitmq的属性配置内容:

RabbitProperties.java 

@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {
	//ip地址
	private String host = "localhost";
	//端口
	private int port = 5672;
	//用户名
	private String username = "guest";
	//密码
	private String password = "guest";
	//ssl认证信息
	private final Ssl ssl = new Ssl();
	//虚拟主机
	private String virtualHost;
	//多个rabbitmq地址,以逗号分隔
	private String addresses;
	//心跳检测 s(默认)
	@DurationUnit(ChronoUnit.SECONDS)
	private Duration requestedHeartbeat;
	//发布确认  防止消息在到达rabbitmq服务器时丢失(true,假如消息和队列是持久化的,则会在消息存入磁盘后返回确认消息)
	private boolean publisherConfirms;
	//发布返回 若消息不能正确路由到任何队列,false就会直接丢弃消息,true则会把消息返回给生产者,可以通过returnListener监听return事件
	private boolean publisherReturns;
	//连接超时时间
	private Duration connectionTimeout;
	//缓存配置 具有两种缓存方式:channel、connection 默认使用channel
	private final Cache cache = new Cache();
	//监听配置 默认两种监听容器:SIMPLE、DIRECT 默认使用SIMPLE
	private final Listener listener = new Listener();
	//templete配置 内含retry重试机制配置
	private final Template template = new Template();
}

当我们知道rabbitmq的配置属性之后,心里大致就知道了具体有哪些东西,然后我们就可以分别了解这些内容,当这些东西都了解之后,也差不多对springboot中rabbitmq的运用了解清楚了。

CachingConnectionFactory

配置信息我们已经知道了,现在我们就来看看这些配置信息是在什么时候,什么地方使用的。进入rabbitmq的自动配置类RabbitAutoConfiguration,其中存在如下片段:

/**
*仅当上下文中不存在ConnectionFactory Bean时才初始化
*该类创建CachingConnectionFactory,用于缓存connection以及channel
*默认采用channel缓存级别,缓存connection 1个,channel 25个
*/
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {
	@Bean
	public CachingConnectionFactory rabbitConnectionFactory(
		RabbitProperties properties,
		ObjectProvider connectionNameStrategy)
		throws Exception {
		PropertyMapper map = PropertyMapper.get();
		//根据RabbitProperties信息封装CachingConnectionFactory对象
		CachingConnectionFactory factory = new CachingConnectionFactory(
			getRabbitConnectionFactoryBean(properties).getObject());
		map.from(properties::determineAddresses).to(factory::setAddresses);
		map.from(properties::isPublisherConfirms).to(factory::setPublisherConfirms);
		map.from(properties::isPublisherReturns).to(factory::setPublisherReturns);
		RabbitProperties.Cache.Channel channel = properties.getCache().getChannel();
		map.from(channel::getSize).whenNonNull().to(factory::setChannelCacheSize);
		map.from(channel::getCheckoutTimeout).whenNonNull().as(Duration::toMillis)
			.to(factory::setChannelCheckoutTimeout);
		//设置缓存模式(connection/channel)
		RabbitProperties.Cache.Connection connection = properties.getCache()
			.getConnection();
		map.from(connection::getMode).whenNonNull().to(factory::setCacheMode);
		//缓存大小(仅当缓存模式为connection时有效)
		map.from(connection::getSize).whenNonNull()
			.to(factory::setConnectionCacheSize);
		map.from(connectionNameStrategy::getIfUnique).whenNonNull()
			.to(factory::setConnectionNameStrategy);
		return factory;
	}
}

注意:上述我们所说的缓存,并不是channel默认只能使用25个,而是说只会缓存25个,假如使用的时候超过25个,超过的部分在使用结束后将会直接关闭。当然,若想要channel数量受到限制,可以设置channelCheckoutTimeout和channelCacheSize属性,channelCheckoutTimeout>0时,获取channel超过时间,将会抛出AmqpTimeoutException异常。

RabbitTemplete

rabbitTemplete提供多种消息发送和接受API,并且提供MessageConverter。

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
	PropertyMapper map = PropertyMapper.get();
	RabbitTemplate template = new RabbitTemplate(connectionFactory);
	//设置消息转换器
	MessageConverter messageConverter = this.messageConverter.getIfUnique();
	if (messageConverter != null) {
		template.setMessageConverter(messageConverter);
	}
	//开启mandatory  spring.rabbitmq.template.mandatory为null则取spring.rabbitmq.publisher-returns的值
	template.setMandatory(determineMandatoryFlag());
	RabbitProperties.Template properties = this.properties.getTemplate();
    //若开启重试机制,则将重试机制添加到RabbitTemplate
	if (properties.getRetry().isEnabled()) {
		template.setRetryTemplate(createRetryTemplate(properties.getRetry()));
	}
	//receive()操作超时时间
	map.from(properties::getReceiveTimeout).whenNonNull().as(Duration::toMillis)
		.to(template::setReceiveTimeout);
	//sendAndReceive()操作超时时间
	map.from(properties::getReplyTimeout).whenNonNull().as(Duration::toMillis)
		.to(template::setReplyTimeout);
	//设置默认交换机  缺省""
	map.from(properties::getExchange).to(template::setExchange);
	//设置默认路由键  缺省""
	map.from(properties::getRoutingKey).to(template::setRoutingKey);
	return template;
}

重试机制配置

在消费消息的时候,可能因为网络波动或其它原因导致消息手动ack,或者处理超时等,导致消息消费失败,这时我们不想直接抛出异常,而是重新获取消息进行消费,这时就可以引入重试机制(注意数据一致性),如下内容:

private RetryTemplate createRetryTemplate(RabbitProperties.Retry properties) {
        PropertyMapper map = PropertyMapper.get();
        RetryTemplate template = new RetryTemplate();
	//默认最大重试3次
	SimpleRetryPolicy policy = new SimpleRetryPolicy();
	map.from(properties::getMaxAttempts).to(policy::setMaxAttempts);
	template.setRetryPolicy(policy);
	ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
	//重试初始间隔时间 (默认0.1s)
	map.from(properties::getInitialInterval).whenNonNull().as(Duration::toMillis)
	   .to(backOffPolicy::setInitialInterval);
	//间隔时间倍数 (默认2)  下一次间隔时间=上一次间隔时间*multiplier
	map.from(properties::getMultiplier).to(backOffPolicy::setMultiplier);
	//重试最大间隔时间 (默认30s)
	map.from(properties::getMaxInterval).whenNonNull().as(Duration::toMillis)
       .to(backOffPolicy::setMaxInterval);
	template.setBackOffPolicy(backOffPolicy);
	return template;
}

RabbitAdmin

提供RabbitAdmin, 用于对交换机、队列、绑定做管理。

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
@ConditionalOnMissingBean
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
	return new RabbitAdmin(connectionFactory);
}

MessageListenerContainer

spring amqp模块中提供了两种容器供大家使用:SimpleMessageListenerContainer和DirectMessageListenerContainer,容器提供对监听的启动和停止。SimpleMessageListenerContainer中每个consumer采用专用线程,可以配置多个队列,消费者使用同一个线程处理多个队列的消息。SimpleMessageListenerContainer的并发由concurrentConsumers和maxConcurrentConsumers动态控制,concurrentConsumers代表初始固定的消费者数量,maxConcurrentConsumers代表最大的消费者数量,假设消息监听采用的是@RabbitListener,则可以通过设置concurrency=2-10达到控制消费者数量的效果。DirectMessageListenerContainer与SimpleMessageListenerContainer最大的不同,在于为每个消费者提供一个单独的channel并且消费者的逻辑不是在自己的线程池中处理的,而是在rabbit 客户端线程池处理,可以有效减少线程上下文切换所带来的资源消耗。另外DirectMessageListenerContainer在运行时添加队列或者是移除队列的性能最好,因为SimpleMessageListenerContainer会先移除所有的消费者再重新创建,而DirectMessageListenerContainer只会处理受到影响的消费者。

@RabbitListener

消息异步监听,我们可以直接在方法上通过使用该注解进行监听某个或者多个队列,例如:

    @RabbitListener(queues = "test_queue")
    public void consume(String message) throws IOException {
        System.out.println("接受到消息:" + message);
    }

@RabbitListener支持多个属性,例如:containerFactory设置容器工厂,queues设置监听队列,concurrency设置消费者并发数量等,还有一些其它的属性,大家可以自己慢慢探索。假如我们想让某个监听手动ack,可以配置此参数containerFactory,为该监听配置独立的容器工厂,虽然我们可以通过spring.rabbitmq.listener.simple.acknowledge-mode或者spring.rabbitmq.listener.direct.acknowledge-mode配置手动ack,但它是全局共享的。

总结

springboot2与rabbitmq的初步介绍,就到此为此了,目前没有给大家说有哪些具体的使用,只是简单的介绍了有哪些内容,限于篇幅,后面我们专门来描述在现实项目中,我们应该如何来使用这些内容。若上述内容有不对的地方,望大家能够指正,共同学习,共同进步。

 

你可能感兴趣的:(SpringBoot)