记录一条踩得坑,rabbitmq创建连接是时候希望通过配置文件管理连接地址,但是在配置类中获取不到配置文件参数
刚开始在网上找到如下代码所示创建连接的方法,但是获取不到地址、端口、用户名、密码
@Configuration
public class QueueConfig {
@Autowired
private RabbitAdmin rabbitAdmin;
@Value("${mq.rabbit.address}")
String address;
@Value("${mq.rabbit.username}")
String username;
@Value("${mq.rabbit.password}")
String password;
@Value("${mq.rabbit.virtualHost}")
String mqRabbitVirtualHost;
创建mq连接
@Bean(name = "connectionFactory")
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(mqRabbitVirtualHost);
connectionFactory.setPublisherConfirms(true);
//该方法配置多个host,在当前连接host down掉的时候会自动去重连后面的host
connectionFactory.setAddresses(address);
return connectionFactory;
}
}
查询发现@value读取单个属性,且不支持复杂类型。
(1)@Value(“#{configProperties[‘key’]}”)
(2)@Value(“${key}”)
@Component
public class Student{
@Value("${student.name}")
String name;
@Value("${student.age}")
int age;
}
@ConfigurationProperties支持批量注入文件属性
@ConfigurationProperties("spring.datasource.druid.second")
例如:@ConfigurationProperties(“student.dog.name”)是获取单个属性
@ConfigurationProperties(prefix = “student”)读取以student开头的多个属性,配置文件如下:
student:
name: zhang
age: 25
boss: true
birth: 1998/6/15
maps: {key1:value1,key2:value2}
list: [1,2,3,4,5]
dog:
name: 旺财
age: ${random.int[20]}
具体的@Value和@ConfigurationProperties的区别参考博客:
https://www.jianshu.com/p/82fe2a00a124
接着尝试使用@ConfigurationProperties来获取配置文件的属性,依然获取不到,都是null,想到是不是加载顺序的问题?这个猜想目前没有验证。最后通过使用类加载器来加载配置文件,创建一个rabbitmq.properties文件,配置rabbitmq服务连接地址、端口、用户名等信息,然后加载该文件获取。创建一个工具类去获取连接地址,代码如下:
public class RabbitmqUtils {
public static String addresses = null;
public static int port = 0;
public static String username = null;
public static String password = null;
static {
//创建配置文件对象
Properties properties = new Properties();
// //使用类加载器 加载配置文件
// //获取class文件
// Class cls = RedisUtils.class;
// //获取类加载器
// ClassLoader classLoader = cls.getClassLoader();
// //加载配置文件
// InputStream is = classLoader.getResourceAsStream("rabbitmq.properties");
// //获取配置文件
// properties.load(is);
properties.load(RedisUtils.class.getClassLoader().getResourceAsStream("rabbitmq.properties"));
addresses = properties.getProperty("spring.rabbitmq.addresses");
port = Integer.parseInt(properties.getProperty("spring.rabbitmq.port"));
username = properties.getProperty("spring.rabbitmq.username");
password = properties.getProperty("spring.rabbitmq.password");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
创建一个rabbitmq连接类,方便在其他服务连接使用
@Configuration
public class RabbitConnectionFactory {
//创建mq连接
@Bean(name = "connectionFactory")
public org.springframework.amqp.rabbit.connection.ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(RabbitmqUtils.addresses);
connectionFactory.setPort(RabbitmqUtils.port);
connectionFactory.setUsername(RabbitmqUtils.username);
connectionFactory.setPassword(RabbitmqUtils.password);
// connectionFactory.setVirtualHost(mqRabbitVirtualHost);
// connectionFactory.setPublisherConfirms(true);
//该方法配置多个host,在当前连接host down掉的时候会自动去重连后面的host
return connectionFactory;
}
}
此处rabbitmq连接地址有两个,host和addresses,host默认是连接本地rabbitmq服务器的地址,localhost,addresses是可以连接到指定的地址,可以配置多个地址,通过逗号分隔开。默认连接是第一个服务,第一个服务宕机后会自动连接第二个rabbitmq服务,这里有一个问题,第一个rabbitmq宕机后队列里的消息无法消费怎么办?rabbitmq的机制是当服务启动时会自动消费交换机队列里的消息,但是如果服务没有启动,此处的消息就不能及时消费。这里需要研究一下rabbitmq集群化部署数据同步问题,暂悬。这里配置多rabbitmq服务器时,用户名、密码、端口需要一致才会自动切换。
因此,为了配置多rabbitmq服务,第一个rabbitmq宕机后自动连接第二个rabbitmq服务,rabbitmq.properties配置如下:
#主机地址
spring.rabbitmq.addresses=连接地址1,连接地址2
#端口
spring.rabbitmq.port=5672
#用户名
spring.rabbitmq.username=username
#密码
spring.rabbitmq.password=password
注意,此处只有addresses可以用逗号分开写多个,且这几个服务的用户名、密码、端口、权限要完全一致,如果你把用户名、端口、密码也用逗号分开各自写,就会出现如下报错,连接登录失败,rabbitmq不会用逗号区分登陆的用户名和密码
2020-04-10 15:14:36.676 ERROR 11132 --- [io-9011-exec-28] b.common.exception.RRExceptionHandler : com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
二、记录一条新坑
微服务中,例如A服务中创建了队列发送了消息,在B服务中监听,rabbitmq默认的连接是localhost,这两个服务的连接应该是同一个rabbitmq的连接,否则A连接的是一个rabbitmq服务,B服务连接的是另一个rabbitmq服务,就会出现监听不到消息的情况。
A服务启动后控制台会打印rabbitmq连接信息如下:
2020-04-10 13:48:46.904 INFO 14940 --- [cTaskExecutor-1] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [连接地址1, 连接地址2]
B服务启动后控制台打印连接信息如下:
2020-04-10 13:48:46.904 INFO 14940 --- [cTaskExecutor-1] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [127.0.0.1]
此时只需要把上面的abbitmq连接类复制到对应服务的目录即可,服务启动时就会连接同一个rabbitmq。
rabbitmq的坑还有很多,比如消息监听失败、这个目前有一些解决方案,可以通过持久化、消息确认机制等方式来几尽可能将消息丢失率降到最低,但是不能100%保证消息完整,所以在使用时,可以酌情考虑如果对这种极低的消息丢失率可以容忍,那就可以放心的使用他,万物皆可MQ,是一个好工具。这里还有rabbitAdmin、RabbitTemplate使用上的区分以及如何动态创建绑定消息队列,后续更新。。。。