RocketMQ如何保证消息不丢失
RocketMQ的消息想要确保不丢失,需要生产者、消费者以及Broker的共同努力,缺一不可。
首先在生产者端,消息的发送分为同步、异步两种和单向发送(单向发送不保证成功,不建议使用),在同步发送消息的情况下,消息的发送会同步阻塞等待 Broker 返回结果,在 Broker 确认收到消息之后,生产者才会拿到 SendResult。如果这个过程中发生异常,那么就说明消息发送可能失败了,就需要生产者进行重新发送消息。
try { SendResult sendResult = producer.send(msg); // 同步发送消息,只要不抛异常就是成功。 if (sendResult != null) { //重试逻辑 }}catch (Exception e) { //重试逻辑}
异步发送的时候,会有成功和失败的回调,这还是需要在失败回调中处理重试确保成功
// 异步发送消息, 发送结果通过callback返回给客户端。producer.sendAsync(msg, new S ...
RocketMQ如果丢消息了,可能的原因是什么
发送端用了单向发送RocketMQ 提供了一种发送方只负责发送消息,不等待服务端返回响应且没有回调函数触发,即只发送请求不等待应答的发送方式,叫做单向发送。
producer.sendOneway(msg);
用了这种发送方式,是非常有可能发送不成功,导致消息丢失
未处理发送失败的情况
如果使用了同步发送或者异步发送,但是没有处理好异常的情况,没有进行合理的重试,那么也会导致消息丢失
同步
try { SendResult sendResult = producer.send(msg); // 同步发送消息,只要不抛异常就是成功。 if (sendResult != null) { //忽略失败 }}catch (Exception e) { //忽略异常}
异步
// 异步发送消息, 发送结果通过callback返回给客户端。producer.sendAsync(msg, new SendCallback() { @Override public void ...
如果重复消费了,可能是什么原因导致的
RocketMQ重复消费,指的是同一个消息重复来了多次
Consumer返回给Broker消费失败(常见
不管是因为什么情况了,是真的消费失败了,还是出现了异常了,还是明明消费成功了,但是你错误的返回了失败等等情况,只要你给 RocketMQ 返回的是 RECONSUME_LATER,那么消息就会重投,有重投就会有重复消费。
Consumer消费处理超时了(常见)
不只是返回失败的情况,如果消费方法执行时间过长,RocketMQ 可能判定消费者失联,也一样会重投消息。 那就和上面的情况一样了。
消息发重了(常见)
这种比较常见的,因为有的时候我们调用 MQ 发送消息的时候,因为网络抖动或者异常,我们会把一些实际成功的消息重发一遍,那么就会有两条一模一样的消息,那么对于消费者来说就可能会重复消费了。
广播模式
这不是重复消费,而是 RocketMQ 的广播模式特性,他就是会把一条消息发送给所有的消费者,但是如果大家处理的逻辑都一样, 那么和重复消费的表现是一样的。
RocketMQ如何保证消息的顺序性
和Kafka只支持同一个 Partition 内消息的顺序性一样,RocketMQ 中也提供了基于队列(分区)的顺序消费。即同一个队列内的消息可以做到有序,但是不同队列内的消息是无序的
当我们作为 MQ 的生产者需要发送顺序消息时,需要在 send 方法中,传入一个 MessageQueueSelector。
MessageQueueSelector 中需要实现一个 select 方法,这个方法就是用来定义要把消息发送到哪个 MessageQueue 的,通常可以使用取模法进行路由:
SendResult sendResult = producer.send(msg, new MessageQueueSelector() { @Override//mqs:该Topic下所有可选的MessageQueue //msg:待发送的消息 //arg:发送消息时传递的参数 public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { Integer id = (Inte ...
普通消息、顺序消息的区别,在什么场景会用到
普通消息,就是很普通,没有什么特别的, 和顺序消息相比的话,就是消息发送到 MQ 后,消费者不保证严格按照发送顺序消费。消息可能会被乱序消费。
顺序消息是指生产者按照顺序发送消息,消费者也必需严格按顺序消费的消息。一般是通过绑定同一个分区来实现的。
顺序消息一般用在对顺序性有严格要求的场景中,比如是涉及到状态流转,尤其是那种状态流转的时间间隔很短的。
比如贷款的还款成功消息和结清消息。交易的确认收货消息和订单完结消息,基本都是同时发生的。如果系统是分别要处理这两个消息,那么可能就需要先处理确认收货,再处理订单完结,否则就会出问题的。
但是大多数情况下,我们用顺序消息的并不多,因为这只对收消息的应用有好处,而对发送消息的应用没有什么好处,反而增加了复杂性(而且还有吞吐量低、维护成本高等缺点)。而要做顺序消息,又需要发送者来做代码改造,所以很多系统都不愿意改,比如交易系统,不可能这么改的。
所以,实际上的大多数场景,并不一定会直接用MQ的顺序消息,而是倾向于消费者自己排序
前置状态判断
作为一个消费支付消息和发货消息的系统,我们可以基于这个 beforeStatus 来判断和我系统中的当前 ...
本地缓存
所谓本地缓存,就是和应用服务器在一起的缓存工具,将需要缓存的数据放到本地缓存中,可以大大的提升访问速度
数据结构
一般来讲,为了提升缓存的效率,通常采用 Key-Value 结构进行数据存储,也就是说,缓存中的数据保存和读取都需要有一个 Key,通过 Key 来读取固定的缓存的 Value
线程安全
本地缓存一定要考虑线程安全的问题,因为大多数情况下本地缓存都是一个全局可访问的变量,那么就会有多个线程同时访问,所以线程安全问题不容忽视
对象上限
因为是本地缓存,而本地内存中的数据是要占用 JVM 的堆内存的,所以内存是有上限要求的,如果无限存储,最终一定会导致 OOM 的问题
清除策略
为了避免 OOM 的问题,一般会考虑在缓存中增加清除策略,通过一定的手段定期的清理掉一些数据,来保证内存占用不会过大,常见清除策略主要有有 LRU(最近最少使用)、FIFO(先进先出)、LFU(最近最不常用)、SOFT(软引用)、WEAK(弱引用)等
过期时间
有了清除策略并不能保证百分百的可以删除数据,极端情况会会使得某些数据一直无法删除。这时候就需要有一种机制,能够保证某些 K-V 一定可以删除。通 ...
Nacos如何实现的配置变化客户端可以感知到
客户端与配置中心的数据交互方式其实无非就两种,要么推 ,要么就是拉
推的模式就客户端和服务端建立 TCP 长链接,当服务端数据发生变化,立即通过这个已经建立好的长连接将数据推送到客户端。长链接的优点是实时性,一旦数据变动,客户端立即就能感知到。但是缺点就是服务端需要维护大量的 TCP 连接,这会占用大量的内存和 CPU 资源,同时也容易受到网络抖动等因素的影响。
拉的模式就是客户端轮询,通过不断轮询的方式检查数据是否发生变化,变化的话就把数据拉回来。轮询的优点是实现比较简单,但弊端也显而易见,轮询无法保证数据的实时性,并且轮询方式对服务端还会产生压力。
在 Nacos 1.x 版本中采用的是长轮询,不是长连接,也不是轮询,是长轮询(Long Polling)。
其实就是把长连接和轮询综合了一下,就是说客户端发起轮询,但是不立即返回,而是 hold 一段时间,这段时间保持着一个有效连接,超时或者变化再返回,然后再发起一次轮询。
在 Nacos 2.0 中,采用 gRPC 长连接。
长轮询和长连接
长轮询是一种实现异步消息通信的机制,它通常用于客户端向服务器端请求某个资源时, ...
MySQL只操作同一条记录发生死锁
MySQL只操作同一条记录发生死锁数据库的锁锁的是索引,并不是记录
当我们在事务中,更新一条记录的时候,如果用到普通索引作为条件,那么会先获取普通索引的锁,然后再尝试获取主键索引的锁。那么这个时候,如果刚好有一个线程,已经拿到了这条记录的主键索引的锁后,同时尝试在该事务中去拿该记录的普通索引的锁。这时候就会发生死锁。
update my_table set name = 'hollis',age = 22 where name = "hollischuang";这个SQL会先对name加锁, 然后再回表对id加锁。-----select * from my_table where id = 15 for update;update my_table set age = 33 where name like "hollis%";以上SQL,会先获取主键的锁,然后再获取name的锁。
为了避免这种死锁情况的发生,可以在应用程序中设置一个规定的索引获取顺序,例如,只能按照主键索引->普通索引的顺序获取锁,这样就可以避免不 ...
Redis大Key问题
Redis大Key问题Big Key 是 Redis 中存储了大量数据的 Key,不要误以为 big key 只是表示 Key 的值很大,他还包括这个 Key 对应的 value 占用空间很多的情况,通常在 String、list、hash、set、zset 等类型中出现的问题比较多
其中 String 类型就是字符串的值比较大,而其他几个类型就是其中元素过多的情况
Redis 的 Big Key 可能存在以下几个危害:
影响性能:由于 big key 的 values 占用的内存会很大,所以读取它们的速度会很慢,会影响系统的性能。
占用内存: 大量的 big key 也会占满 Redis 的内存,让 Redis 无法继续存储新的数据,而且也会导致 Redis 卡住
内存空间不均匀:比如在 Redis 集群中,可能会因为某个节点上存储了 Big Key,导致多个节点之间内存使用不均匀。
影响 Redis 备份和恢复:如果从 RDB 文件中恢复全量数据时,可能需要大量的时间,甚至无法正常恢复。
搜索困难:由于大 key 可能非常大,因此搜索 key 内容时非常困难,并且可能需要花费较长 ...
ElasticSearch倒排索引
ElasticSearch倒排索引在 ElasticSearch 中,倒排索引是一种常用的索引结构,用于快速搜索文档中的某个词汇。
倒排索引的结构与传统的索引结构相反,传统的索引结构是由文档构成的,每个文档包含了若干个词汇,然后根据这些词汇建立索引。而倒排索引是由词汇构成的,每个词汇对应了若干个文档,然后根据这些文档建立索引。
对于一个包含多个词汇的文档,倒排索引会将每个词汇作为一个关键字(Term),然后记录下该词汇所在的文档编号(Document ID)及该词汇在文档中的位置(Term Position)。这样,当用户输入一个关键字时,就可以快速地查找到包含该关键字的文档编号,然后通过文档编号再查找到对应的文档内容。
倒排索引的优点在于它可以快速定位包含关键字的文档,而且可以支持复杂的搜索操作,如词组搜索、通配符搜索等。同时,由于倒排索引是由词汇构成的,因此在进行数据分析和统计时也非常有用。在 ElasticSearch 中,倒排索引是一种非常重要的索引结构,它被广泛应用于搜索引擎、日志分析、推荐系统等领域。
