- 原生Java客户端
Channel ch = null;
// …
ch.txSelect();
for (int i = 0; i < MSG_COUNT; ++i) {
try {
ch.basicPublish(“”, QUEUE_NAME,
MessageProperties.PERSISTENT_BASIC,
“nop”.getBytes());
ch.txCommit();
} catch (Exception exception) {
ch.txRollback();
}
}
- SpringBoot AMQP
首先提供一个事务管理器供SpringBoot使用。
@Bean
RabbitTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new RabbitTransactionManager(connectionFactory);
}
接下来,在消息生产者上面做两件事:添加@Transactional
并设置通信信道为事务模式:
@Service
public class MsgService {
@Autowired
RabbitTemplate rabbitTemplate;
@Transactional
public void send() {
rabbitTemplate.setChannelTransacted(true);
// rabbitTemplate.convertAndSend(…);
// int i = 1 / 0; 没有爆发异常,由spring提交事务,否则回滚(也就是不发生消息)
}
}
但是使用事务有两个问题。
首先channel长时间处于阻塞:发布者必须依次等待broker处理每条消息。
不过有时候发布者只要知道broker宕机时哪些消息尚未处理就足够了。
其次是事务实现的繁重性:每次提交都需要一个 fsync(),这需要很多时间才能完成。
发布 10000 条消息需要 4 多分钟(具体参数机器性能决定,总之确实非常慢)
一旦通道进入确认模式,代理将在处理消息时确认消息。
由于这是异步完成的,生产者可以流式发布而不用等待代理,代理也可以有效地批量写入磁盘。
- 原生Java客户端
// 消息追踪记录(如果需要线程安全并且有序,可以使用 ConcurrentSkipListMap )
HashMap<Long, String> map = new HashMap<>();
// 必须显式开启
channel.confirmSelect();
// 监听被退回的消息(如消息路由到队列失败)
channel.addReturnListener(returnMessage -> {
System.out.println("return : " + System.currentTimeMillis());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(new String(returnMessage.getBody()) + " publish fail!");
});
// 监听被到达或未到达交换机(exchange)的消息
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
map.remove(deliveryTag); // 发送成功,缓存清除掉
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.err.println(map.get(deliveryTag) + “not ack!”);
// 下面可以进行重新发送等逻辑
}
});
try {
Random random = new Random();
int idx = 0;
while (idx < 1000) {
String message = “from server…” + (++idx);
// 追踪记录
map.put(channel.getNextPublishSeqNo(), message);
// 发送消息
channel.basicPublish(“”, queue, MessageProperties.PERSISTENT_BASIC, message.getBytes(StandardCharsets.UTF_8));
TimeUnit.MILLISECONDS.sleep(random.nextInt(300) + 200);
}
} finally {
System.out.println(“以下为未成功发送的消息”);
// 可以进行重试逻辑
map.values().forEach(s -> System.out.println("not ack, may need publish again : " + s));
}
- SpringBoot AMQP
首先在配置文件中配置中开启消息发送方确认机制。
spring:
rabbitmq:
publisher-returns: true
publisher-confirm-type: correlated
publisher-confirm-type
有三种属性:
-
none:表示禁用发布确认模式,默认即此。
-
correlated:使用相关消息确认,回调中触发。
-
simple:使用
waitForConfirms()
和waitForConfirmsOrDie()
方法的进行确认。
然后配置回调的监听器:
@Configuration
public class PublisherConfirmConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
RabbitTemplate rabbitTemplate;
public PublisherConfirmConfig(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
//@Bean 此处无需注入
RabbitTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new RabbitTransactionManager(connectionFactory);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("correlationData : "+correlationData);
if (ack) {
System.out.println(“success”);
} else {
System.err.println("cause : "+cause);
}
}
@Override
public void returnedMessage(ReturnedMessage returned) {
System.err.println(returned);
}
@PostConstruct
public void initRabbitTemplate() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
}
}
发送消息:
// 注意必须传入 CorrelationData,否则没有根据去跟踪(原生client使用deliveryTag跟踪)
rabbitTemplate.convertAndSend(“q1”, (Object) s, new CorrelationData("correlation id = " + count));
============================================================================
==========================================================================
RabbitMQ的Client
RabbitMQ中channel
在消费消息(basicConsume(String queue, boolean autoAck, Consumer callback)
)的时候,指定的ack的含义如下:
- autoAck = true
当broker在消息发送后(写入TCP套接字后)此条消息就立即ack了,此条消息RabbitMQ服务器也不再保存了,
而丝毫不管收到消息的客户端是否处理。如果消费者在收到大量消息但没有处理的时候突然宕机了,那么那些未处理消息也就随着本地缓冲区的消失而消失了(服务器上也没有了)。
这种ack方式谨慎使用。
- autoAck = false
这种ack方式必须要求用户自己主动ack消息(channel.basicAck
),常常和prefetchCount配合使用(后面会介绍到)。
Spring AMQP
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/09f16ce8b23766539e247976aa582855.jpeg)
最后
作为过来人,小编是整理了很多进阶架构视频资料、面试文档以及PDF的学习资料,针对上面一套系统大纲小编也有对应的相关进阶架构视频资料
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://img-blog.csdnimg.cn/img_convert/09f16ce8b23766539e247976aa582855.jpeg)
最后
作为过来人,小编是整理了很多进阶架构视频资料、面试文档以及PDF的学习资料,针对上面一套系统大纲小编也有对应的相关进阶架构视频资料
[外链图片转存中…(img-SXbLZYsZ-1712537241522)]
[外链图片转存中…(img-suaXpcxL-1712537241522)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!