zoukankan      html  css  js  c++  java
  • 开发中遇到的问题SpringRabbitMq的多实例注册问题

    前情

    在这里记录一下在实际开发中,因为对SpringBoot的自动配置不熟悉所导致的一个问题。开始在项目中有RabbitMq客户端的使用,基于SpringBoot的自动装配机制,所有直接在配置文件里配置了RabbitMq连接所使用的配置属性,在这之前都是正常运行的。然后再一次需求开发中,需要连接另一个RabbitMq,发送消息,这时候,就是简单的在系统中创建了一个RabbitMq的客户端实例对象,然后再配置文件中添加了另一套属性配置,服务正常启动运行。但是在后来的运行过程中发现,总是有消息无法投递,报错,这时候发现确实出了问题。

    排查问题

    发现问题后,首先查看了出错的消息,发现是之前的mq消息无法投递,经过跟踪,发现投递消息的地址Ip不对,但是配置文件里面的配置属性是正确的,这时候意识到属性配置根本没有起作用,为什么不起作用,是因为属性根本没有被相关实例加载,这时候想到了SpringBoot的自动装配机制,很可能是SpringBoot的自动装配机制导致了之前的配置不起作用了。

    根本原因

    由于SpringBoot的自动装配机制,导致我们可以很方便的使用第三方集成功能,但是也导致了我们很容易忽略的一个问题,首先我们看一下RabbitMQ自动装配类:
    由于我们添加了新的rabbitMQ属性配置,为了让新的属性配置生效,所以在项目中新增了新的ConnectionFactory配置,由于框架内的RabbitConnectionFactoryCreator生效的条件是@ConditionalOnMissingBean(ConnectionFactory.class),即不存在ConnectionFactory时,默认的配置才会生效,这导致原来的RabbitMQ Template中的配置失效了,只有新增的配置工厂生效了。

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ RabbitTemplate.class, Channel.class })
    @EnableConfigurationProperties(RabbitProperties.class)
    @Import(RabbitAnnotationDrivenConfiguration.class)
    public class RabbitAutoConfiguration {
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnMissingBean(ConnectionFactory.class)
    	protected static class RabbitConnectionFactoryCreator {
    
    		@Bean
    		public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
    				ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
    				ObjectProvider<CredentialsRefreshService> credentialsRefreshService,
    				ObjectProvider<ConnectionNameStrategy> connectionNameStrategy,
    				ObjectProvider<ConnectionFactoryCustomizer> connectionFactoryCustomizers) throws Exception {
    			com.rabbitmq.client.ConnectionFactory connectionFactory = getRabbitConnectionFactoryBean(properties,
    					resourceLoader, credentialsProvider, credentialsRefreshService).getObject();
    			connectionFactoryCustomizers.orderedStream()
    					.forEach((customizer) -> customizer.customize(connectionFactory));
    			CachingConnectionFactory factory = new CachingConnectionFactory(connectionFactory);
    			PropertyMapper map = PropertyMapper.get();
    			map.from(properties::determineAddresses).to(factory::setAddresses);
    			map.from(properties::getAddressShuffleMode).whenNonNull().to(factory::setAddressShuffleMode);
    			map.from(properties::isPublisherReturns).to(factory::setPublisherReturns);
    			map.from(properties::getPublisherConfirmType).whenNonNull().to(factory::setPublisherConfirmType);
    			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);
    			RabbitProperties.Cache.Connection connection = properties.getCache().getConnection();
    			map.from(connection::getMode).whenNonNull().to(factory::setCacheMode);
    			map.from(connection::getSize).whenNonNull().to(factory::setConnectionCacheSize);
    			map.from(connectionNameStrategy::getIfUnique).whenNonNull().to(factory::setConnectionNameStrategy);
    			return factory;
    		}
    
    		private RabbitConnectionFactoryBean getRabbitConnectionFactoryBean(RabbitProperties properties,
    				ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
    				ObjectProvider<CredentialsRefreshService> credentialsRefreshService) {
    			RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
    			factory.setResourceLoader(resourceLoader);
    			PropertyMapper map = PropertyMapper.get();
    			map.from(properties::determineHost).whenNonNull().to(factory::setHost);
    			map.from(properties::determinePort).to(factory::setPort);
    			map.from(properties::determineUsername).whenNonNull().to(factory::setUsername);
    			map.from(properties::determinePassword).whenNonNull().to(factory::setPassword);
    			map.from(properties::determineVirtualHost).whenNonNull().to(factory::setVirtualHost);
    			map.from(properties::getRequestedHeartbeat).whenNonNull().asInt(Duration::getSeconds)
    					.to(factory::setRequestedHeartbeat);
    			map.from(properties::getRequestedChannelMax).to(factory::setRequestedChannelMax);
    			RabbitProperties.Ssl ssl = properties.getSsl();
    			if (ssl.determineEnabled()) {
    				factory.setUseSSL(true);
    				map.from(ssl::getAlgorithm).whenNonNull().to(factory::setSslAlgorithm);
    				map.from(ssl::getKeyStoreType).to(factory::setKeyStoreType);
    				map.from(ssl::getKeyStore).to(factory::setKeyStore);
    				map.from(ssl::getKeyStorePassword).to(factory::setKeyStorePassphrase);
    				map.from(ssl::getKeyStoreAlgorithm).whenNonNull().to(factory::setKeyStoreAlgorithm);
    				map.from(ssl::getTrustStoreType).to(factory::setTrustStoreType);
    				map.from(ssl::getTrustStore).to(factory::setTrustStore);
    				map.from(ssl::getTrustStorePassword).to(factory::setTrustStorePassphrase);
    				map.from(ssl::getTrustStoreAlgorithm).whenNonNull().to(factory::setTrustStoreAlgorithm);
    				map.from(ssl::isValidateServerCertificate)
    						.to((validate) -> factory.setSkipServerCertificateValidation(!validate));
    				map.from(ssl::getVerifyHostname).to(factory::setEnableHostnameVerification);
    			}
    			map.from(properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
    					.to(factory::setConnectionTimeout);
    			map.from(properties::getChannelRpcTimeout).whenNonNull().asInt(Duration::toMillis)
    					.to(factory::setChannelRpcTimeout);
    			map.from(credentialsProvider::getIfUnique).whenNonNull().to(factory::setCredentialsProvider);
    			map.from(credentialsRefreshService::getIfUnique).whenNonNull().to(factory::setCredentialsRefreshService);
    			factory.afterPropertiesSet();
    			return factory;
    		}
    
    	}
    
    	@Configuration(proxyBeanMethods = false)
    	@Import(RabbitConnectionFactoryCreator.class)
    	protected static class RabbitTemplateConfiguration {
    
    		@Bean
    		@ConditionalOnMissingBean
    		public RabbitTemplateConfigurer rabbitTemplateConfigurer(RabbitProperties properties,
    				ObjectProvider<MessageConverter> messageConverter,
    				ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) {
    			RabbitTemplateConfigurer configurer = new RabbitTemplateConfigurer();
    			configurer.setMessageConverter(messageConverter.getIfUnique());
    			configurer
    					.setRetryTemplateCustomizers(retryTemplateCustomizers.orderedStream().collect(Collectors.toList()));
    			configurer.setRabbitProperties(properties);
    			return configurer;
    		}
    
    		@Bean
    		@ConditionalOnSingleCandidate(ConnectionFactory.class)
    		@ConditionalOnMissingBean(RabbitOperations.class)
    		public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
    			RabbitTemplate template = new RabbitTemplate();
    			configurer.configure(template, connectionFactory);
    			return template;
    		}
    
    		@Bean
    		@ConditionalOnSingleCandidate(ConnectionFactory.class)
    		@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
    		@ConditionalOnMissingBean
    		public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
    			return new RabbitAdmin(connectionFactory);
    		}
    
    	}
    
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass(RabbitMessagingTemplate.class)
    	@ConditionalOnMissingBean(RabbitMessagingTemplate.class)
    	@Import(RabbitTemplateConfiguration.class)
    	protected static class MessagingTemplateConfiguration {
    
    		@Bean
    		@ConditionalOnSingleCandidate(RabbitTemplate.class)
    		public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) {
    			return new RabbitMessagingTemplate(rabbitTemplate);
    		}
    
    	}
    
    }
    

    解决方案

    为了使两个配置都生效,我们可以在配置中配置两套RabbitMQ ConnectionFactory 和Rabbit Template,这两就会有两个实例同时生效了。

  • 相关阅读:
    带你封装自己的『权限管理』框架
    一夜搞懂 | JVM 线程安全与锁优化
    一夜搞懂 | Java 内存模型与线程
    一夜搞懂 | JVM 字节码执行引擎
    一夜搞懂 | JVM 类加载机制
    一夜搞懂 | JVM GC&内存分配
    一文洞悉JVM内存管理机制
    Redis 的基本数据类型 和 基础应用场景
    MyISAM 和 InnoDB 索引结构及其实现原理
    一次性搞懂 PHP 中面向对象的所有知识点。
  • 原文地址:https://www.cnblogs.com/nangonghui/p/15712402.html
Copyright © 2011-2022 走看看