发送方:
1, 不关注队列是否存在,如果交换机直接这边生成了,也没有在这边做绑定啥的,那么mq那里是没有这个交换机的,要发送个请求,才会出来,如果在出来之前消费者服务启动是会报错,绑定不到交换机,进程挂掉(如果消费者也生成交换机,然后用bing绑定,交换机会立刻出来,并绑定成功)
2,如果没有交换机,也不会报错,不会影响启动,只有在调用的时候会报错,找不到交换机
3,如果已经存在交换机,发送的时候又从新定义此交换机的参数,会报错。发送会使用这次定义的属性,但是mq中的交换机不会变。所以会报错,发送跟交换机的属性不一样,重试几次之后,就不会发送了,发送失败。
消费方:1,如果绑定的交换机不存在,会报错,并且进程都起不来
2,如果绑定好不是默认的交换机后,交换机删除了,那么此队列会变成绑定默认队列,如果交换机在生成了,队列还是绑定的默认。如果再次绑定但是改变了队列信息,会报错,说队列信息不一样。如果没有改变信息,再次绑定,此队列会重新绑定到这个交换机
3,队列如果存在(交换机在存在),在从新绑定,如果只改变rootingkey那就是2个,如果是只改变队列信息,会报错2个队列信息不一样
//解绑 队列与交换机
@Test
public void unBind() throws Exception {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueUnbind("spring.test.*", "spring.test.exchange", "*.*");
}
// 设置到broker的确认(即是否到达exchange)
springbootConnectionFactory.setPublisherConfirmType(
CachingConnectionFactory.ConfirmType.CORRELATED);
// 没有队列接收不会触发这个,只有当异常时才会触发
springbootConnectionFactory.setPublisherReturns(true);
factory.addChannelListener(rabbitChannelListener);
factory.addConnectionListener(rabbitConnectionListener);
factory.setRecoveryListener(rabbitRecoveryListener);
// 交换器无法根据自身类型和路由键找到一个符合条件的队列时的处理方式 true:RabbitMQ会调用
// Basic.Return命令将消息返回给生产者 false:RabbitMQ会把消息直接丢弃
rabbitTemplate.setMandatory(true);
这里有三个监听器: ChannelListener 用于监听通道的创建和销毁
@Service
public class RabbitChannelListener implements ChannelListener {
@Override
public void onCreate(Channel channel, boolean b) {
log.info("=====onCreate channel: {}, transactional: {}", channel, b);
}
@Override
public void onShutDown(ShutdownSignalException signal) {
// 可根据isHardError判断是channel断开还是connection断开
if (signal.isHardError()) {
AMQImpl.Connection.Close close = (AMQImpl.Connection.Close) signal.getReason();
log.warn("Connection onShutDown replyCode: {}, methodId: {}, classId: {}, replyText: {}",
close.getReplyCode(), close.getMethodId(), close.getClassId(), close.getReplyText());
} else {
AMQImpl.Channel.Close close = (AMQImpl.Channel.Close) signal.getReason();
log.warn("Channel onShutDown replyCode: {}, methodId: {}, classId: {}, replyText: {}",
close.getReplyCode(), close.getMethodId(), close.getClassId(), close.getReplyText());
}
}
}
ConnectionListener 用于监听连接的创建和关闭
public class RabbitConnectionListener implements ConnectionListener {
@Override
public void onCreate(Connection connection) {
log.info("================onCreate: {}", connection);
}
@Override
public void onClose(Connection connection) {
log.info("================onClose: {}", connection);
}
@Override
public void onShutDown(ShutdownSignalException signal) {
log.info("================onShutDown: {}", signal);
}
}
RecoveryListener 监听自动重连的情况,这个listener没有测试出在什么场景会出现
public class RabbitRecoveryListener implements RecoveryListener {
@Override
public void handleRecovery(Recoverable recoverable) {
log.info("================handleRecovery: {}", recoverable);
}
@Override
public void handleRecoveryStarted(Recoverable recoverable) {
log.info("================handleRecoveryStarted: {}", recoverable);
}
}
当生产者和消费者在同一个服务里面,官方文档特别提醒,不要使用同一个factory
1,可以创建2个factory
2,//使用单独的发送连接,避免生产者由于各种原因阻塞而导致消费者同样阻塞
rabbitTemplate.setUsePublisherConnection(true);
关于失败重连模式
1,也可以调用对应的类对象,实现重连。比如java client 方法
ConnectionFactory factory = new ConnectionFactory();
factory.setAutomaticRecoveryEnabled(true); //设置网络异常重连
factory.setNetworkRecoveryInterval(10000);//设置 没10s ,重试一次
factory.setTopologyRecoveryEnabled(true);//设置重新声明交换器,队列等信息。
2,最简单方式,捕获异常,判断异常信息,重新连接。try.....,except,(catch)。
下面设置ssl里面的factory就是使用的这种
设置 ssl:@Bean(name = "cachingConnectionFactory") public ConnectionFactory cachingConnectionFactory(MqProperties mqProperties) { log.info("mq ssl open:{}", mqProperties.getMqSslOpen()); CachingConnectionFactory springbootConnectionFactory = new CachingConnectionFactory( getCommonConnectionFactory()); try { Map<String, String> map = getMqInfo(mqProperties); springbootConnectionFactory.setHost(map.get("mqIp")); springbootConnectionFactory.setPort(Integer.parseInt(map.get("mqPort"))); springbootConnectionFactory.setUsername(map.get("mqUser")); springbootConnectionFactory.setPassword(map.get("mqPwd")); log.info("mqConnectionFactory created, mq address is :{},port is {}", springbootConnectionFactory.getHost(), springbootConnectionFactory.getPort()); Connection connection = springbootConnectionFactory.createConnection(); connection.close(); return springbootConnectionFactory; } catch (Exception e) { log.info("create rabbitMq connection error:{}", e.getMessage()); } new Thread(() -> System.exit(1)).start(); return null; } private com.rabbitmq.client.ConnectionFactory getCommonConnectionFactory() { com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory = new com.rabbitmq.client.ConnectionFactory(); rabbitConnectionFactory.setVirtualHost("/"); rabbitConnectionFactory.useSslProtocol(getSslContext().orElseThrow(RuntimeException::new)); return rabbitConnectionFactory; } private Optional<SSLContext> getSslContext(MqProperties mqProperties) { ClassLoader classLoader = MqProperties.class.getClassLoader(); if (classLoader == null) { return Optional.empty(); } try (InputStream trustStoreStream = classLoader.getResourceAsStream("XXXX.jks")) { return sslContext(trustStoreStream, mqProperties.getMqTrustPass(), mqProperties.getProtocol()); } catch (Exception e) { log.error("getSslContext error:", e); } return Optional.empty(); } private Optional<SSLContext> sslContext(InputStream trustStoreStream, String mqTrustPass, String protocol) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException { char[] trustPassphrase = 解密出这个值(mqTrustPass).toCharArray(); KeyStore tks = KeyStore.getInstance("JKS"); tks.load(trustStoreStream, trustPassphrase); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(tks); // 初始化SSL上下文 SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(null, tmf.getTrustManagers(), null); return Optional.of(sslContext); } @Bean public RabbitTemplate rabbitTemplate(@Qualifier("cachingConnectionFactory") ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setConfirmCallback(((correlationData, ack, cause) -> { if (!ack) { log.error("correlationData:{} ack:false cause:{} ", correlationData, cause); } })); return rabbitTemplate; }