常用组件之MQ

同步通讯的问题:

微服务间基于feign的调用就是同步调用,必须等待,吞吐量不高、新增需求就得原来的代码、

 异步调用:事件驱动

 什么是mq(消息队列)

rabbitMQ、kafka、rocketMQ、ActiveMQ

 

 简单消息队列demo步骤

 AMQP

AMQP(Advanced Message Queuing Protocol)是一种网络协议,用于在应用程序之间进行异步消息通信。它提供了一种可靠的、异步的方式来发送和接收消息,以及管理消息队列。

SpringAMQP:

发送、接受步骤

引入依赖、配置rabbitMQ的地址和各种信息;

发送方:利用RabbitTemplate发送消息

接受方:利用注解@RabbitListener接受消息

work模型:

通过设置prefetch控制消费者预取的数量,同一个消息只能被一个消费者消费

 发布订阅模型:

一个消息能被多个消费者共同消费

发布者------》交换机-----》队列------》消费者

 FanoutExchange
DirectExchange

TopicExchange

 消息转换器

springAMQP底层使用的是jdk的序列化和反序列化,可以覆盖替换 

使用Jackson2Json

 MQ高级

生产者可靠性

生产者重连机制:有的时候由于网络波动,可能会出现客户端连接MQ失败的情况。通过配置我们可以开启连接失败后的重连机制:

 但是SpringAMQP提供的重试机制是阻塞式的重试,会影响业务性能。

生产者确认:

三种ACK;1、消息投递到了MQ,但是路由失败,通过publisherReturn返回路由异常原因,然后返回ACK;2、临时消息投递到了MQ,并且入队成功,返回ACK;3、持久消息投递到了MQ,并且入队完成持久化,返回ACK

 

MQ可靠性

 在默认情况下,rabbitMq会将接收到的信息保存在内存中以降低消息收发的延迟。

一旦MQ宕机,内存中的消息会丢失;内存空间有限,当消费者故障或处理过慢,会导致消息积压,引发Mq阻塞。

数据持久化

消息分为持久化消息和非持久化消息,非持久化在内存中堆积多了就会发生pageOut阻塞将消息写入内存中,不太友好。

lazy queue

接先将消息写入磁盘,之后再读取,但是非常快速,因为IO做了优化。

内存中的消息数量也有限制。

消费者可靠性

消费者确认机制

一般就是配置的spring自动模式

 消费失败处理机制
失败重试机制

 重试次数耗尽的消息的处理策略:

1.直接删除,默认

2.立即入队

3.将失败消息重新投递到指定交换机

 

 业务幂等性

是指有一些操作重复执行几遍业务最终结果也是一致的。

比如查询操作、删除操作

需要保证业务的幂等性

可以给每一条消息增添唯一ID,等消息成功处理后,将消息ID存储数据库,等到下次消息过来时判断ID是否已经存在于数据库中。

上面这种方式比较依赖性能,消耗资源;

也可以在业务中保证幂等性,比如修改状态时,要先判断是否是需要修改的状态再进行下一步操作

总结

消息延迟

什么是延迟消息

一个消息发送后,消费者不能立刻收到消息,而是在指定时间过后才能收到消息,延迟任务

死信交换机

延迟消息插件

定时任务,一般是在内存维护一个时钟,属于cpu密集型

kafka

kafaka快速入门

MQ系统:将一个系统(生产者)的消息发送到MQ中进行排队,然后发动另外一个系统(消费者)进行消费的系统

异步、解耦、消峰

kafka的设计目标是高吞吐、低延迟和可扩张,主要关注消息传递而不是消息处理。所以,并没有支持死信队列、顺序消息等高级功能。

集群容错性高稳定性要求高于数据安全性要求,即可以丢失一些数据但是不能使服务不可用。

注意
分组消费

同一个消息可以被不同消费者组重复消费,但是一个消息在一个消费者组内只能被消费一次

        kafka的存储消息的offset存储在服务端,但是offset的推进由客户端来实现(consumer.comit),如果通过consumer.poll()来拉取消息后没有做提交处理,那么kafka服务端的offset不会更新,同一个消费组的不同消费者也能消费到这些消息。    

  • 一个消费者组内,一个分区在同一时刻只能被一个消费者消费。
  • 一个消费者可以消费一个或多个分区,这取决于消费者组的成员数量和分区的数量。
  • 当消费者组的成员数量少于分区数量时,会有消费者消费多个分区。
  • 当消费者组的成员数量多于分区数量时,会有消费者处于空闲状态,没有分区消费。
  • 不同的消费者组可以消费同一个主题下的同一个分区。这意味着同一个消息可以被不同的消费者组重复消费。

        如果kafka服务端没有记录offset可以通过设置auto_offset_reset来控制读取消息的开始位置,earliest、latest、none、anything else等

offset管理

因为offset在服务端管理的话客户端非常被动,所以可以将每个partion分区的offset存储在redis等第三方工具中,进行更新管理,如果读取到的消息offset<=redis中的offset那么这个消息就是已经消费过的消息,可以实现消息的幂等性

消息路由
生产者发送消息到partition
partitioner_class_config设置

        默认是没有配置的此时如果,消息带有key的话,相同key发送到一个partition中,如果没有携带key的话都发送到一个partition分区中知道,大小达到了BATCH_SIZE_CONFIG

        还有轮询、自定义

顺序消费
  1. 设置发送的路由,把需要顺序消费的消息都发送到一个partition中
消费者绑定partition消费
partition_assignmment_stratengy_config
  • range:一个topic下有10个partition(0-9)一个消费者组下有三个Consumer。range策略就会将0-3、4-6、7-9,这样分
  • round-robin:轮询分配策略会0-3-6-9;1-4-7;2-5-8这样分
  • sticky:粘性策略。在开始分区时,尽量保持分区的分配均匀;分区的分配尽可能的和 分配的保持一致。比如在range分区的情况下,第三个Consumer的服务宕机了,那么按照stikcy策略,就会保持 其他两个原有的分区分配情况。然后将宕机的7-9分区尽量平均的分配到另外两个consumer上。保持数据稳定性。
  • 自定义
生产者缓存机制

        生产者发送消息会先发送(存储)到本地的accmulator对象(一个deque)中,启动sender对象,等消息大小到达Batch_size_config在发送。其中deque就和消费者关联。

        发送消息也不是一次性发送而是有一个inflightBatches缓存消息,然后再一批次发送,等待应答

生产者应答机制

        客户端进行设置,ACK_CONFIG=====>由客户端来确定是否开启应答机制。

ack_config=0:客户端不会关心消息是否发送成功,发送一次即可。吞吐量最高,安全性最低。

ack_config=1:只需要等到kafka集群中的leader的应答即可。数据一致性不太好

ack_config=all:  必须等到集群中的所有结点的应答才可。网络消费较高。

还有介于1和all之间的:min.insycn.replicas 即不需要等多所有结点的应答.

生产者消息幂等性

分布式数据传递过程中的三个数据语义:

  • at-least-once:至少一次,可以保证数据不丢失,但是又不能保证数据不重复;(我存钱)
  • at-most-once:最多一次,保证数据不重复,但是又不能保证数据不丢失;     (银行收钱)
  • exactly-once:精确一次,

Kafka为了保证消息的发送的Exactly-once语义,增加了几个概念:

PID:每个新的Producer在初始化的过程中国会被分配一个唯一的PID。这个pid对用户是不可见的

Sequence Number: 对于每个pid,这个producer会针对partition维护一个sequenceNumber。这是一个从0开始单调递增的数字。当producer要往同一个partiton发送消息时,这个sequenceNumber就会+1.然后随着消息一起发往Broker。

Broker端则会针对每个<PID,Partition>维护一个序列号(SN),只有对应的SequenceNumber = SN+1时,Broker才会接受消息,同时将SN更新为SN+1。否则,SequenceNumber过小就会认为消息已经写入了,不需要重复写入。而如果sequenceNumber过大,就会认为中间可能有数据丢失了。对生产者就会抛出一个OutOfOrderSequenceNumber。

生产者消息压缩机制

        当生产者往Broker发送消息时,还会对每个消息进行压缩,从而降低Producer到Broker的网络数据传输压力,同时也降低了Broker的数据存储压力。COMPRESSION_TYPE_CONFIG参数

消息事务机制

        通过生产者消息幂等性问题,能够解决单生产者消息写入单分区的幂等性问题。但是,如果是要写入多个分区呢?比如生产者一次性发送多条消息,然后给不同的笑嘻嘻指定不同的key。这批消息就有可能写入多个Partition,而这些Partition是分布在不同Broker上的。这意味着,Producer需要对多个Broker同时保证消息的幂等性。

        Kafka的事务消息还会做两件事情:

  • 一个TransactionId只会对应一个PID,如果当前一个Producer的事务没有提交,而另外一个新的Producer保持相同的TransactionId,这是旧的生产者会立即失效,无法继续发送消息。
  • 跨会话事务对齐,如果某个Producer实例异常宕机了,事务没有被正常提交。那么新的TransactionId相同的Producer实例会对旧的事务进行补齐。保证旧事务要么提交,要么终止。这样新的Producer实例就可以以一个正常的状态开始工作。

 java高并发

传统的synchronized锁和JUC的Lock的区别

利用lock进行精确唤醒

什么是锁,锁的是啥

线程安全的List、set、map

阻塞队列

 同步队列:SynchronousQueue

线程池、七大参数、四大拒绝策略

IO密集型、CPU密集型

(最大线程数要大于那些十分耗IO的线程数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值