什么是MQ?
Message Queue 消息队列 ,在消息的传递过程中保存消息的容器。
父亲==》书架《==儿子
好处:应用解耦,异步提速,限流削峰
使用成本:引入复杂度,最终一致性,高可用性
何时使用:
生产者不需要从消费者处获得反馈
能够容忍短暂的不一致性
效果要大于副作用
应用场景
应用解耦
场景:用户下单之后,订单系统要通知库存系统
传统方式:订单系统直接调用库存系统提供的接口,如果库存系统出现故障会导致订单系统失败
异步提速
场景:用户注册成功之后,需要发送注册邮件及注册短信提醒
限流削峰
场景:现在我们每个月要搞一次大促,大促期间的并发可能会很高的,比如每秒3000个请求,假设现在有两台 机器处理请求,并且每台机器只能每次处理1000个请求,那多出来的1000个请求,可能就把我们整个系统给搞 崩掉;
解决办法:把请求存放在消息队列中,机器A和机器B只需要根据自己能够处理的请求数去消息队列中拿数据, 这样即便有每秒有1万个请求,也不会把整个系统给搞崩;
日志处理
场景:系统中存在海量日志需要处理,大吞吐、还需要支持流式处理,那就得上Kafka
常用MQ比对
ActiveMQ
老牌消息队列,使用Java语言编写,对JMS支持最好,采用多线程并发,资源消耗比较大,由于历史悠久,所以包袱也重,版本更新异常缓慢,集群模式下还需要依赖Zookeeper实现,最新架构的产品被命名为 ApolloMQ,目前国内使用率越来越少;
RabbitMQ
RabbitMQ结合erlang语言本身的并发优势,性能较好、延时低、吞吐量到万级,社区活跃度也比较高,但是难以定制和掌控,不利于做二次开发和维护;RabbitMQ集群动态扩展会比较麻烦,如果数据量没有那么大、项目 规模也不是特别大的话,优先选择功能比较完备的RabbitMQ;
Kafka
Kafka的特点其实很明显,超高的吞吐量[基于 Pull-拉模式来处理消息消费],ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。唯一的一点劣势是有可能消息重复消费,对数据准确性会造成极其轻微的影 响,在大数据领域中以及日志采集中,这点轻微影响可以忽略。这个特性天然适合大数据实时计算以及日志收集,如果你的项目中有日志采集功能,首选肯定是kafka;
RocketMQ
天生为互联网领域、金融线而生,对于可靠性要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰, 在大量交易涌入时,后端可能无法及时处理的情况,RocketMQ在稳定性上可能更值得信赖,这些业务场景在阿里双11已经经历了多次考验,如果你的业务有上述这种高并发场景,建议可以选择RocketMQ,另外,它是Java语言实现的,可以自己阅读源码、基于RocketMQ定制自己公司的MQ;
其他MQ
ZeroMQ[快、大吞吐量,仅提供非持久性队列,down机则丢失数据]、IBM MQ、Redis[支持MQ功 能,可以当做一个轻量级的队列服务来使用
Kafka架构设计
理解了上图,基本上就明白Kafka的整体架构,先来肢解各个组件
Boker-服务器
Kafka作为一个中间件,是帮我们存储和转发消息的,它做的事情就像中介,所以Kafka的服务也叫做Broker,默认是 9092的端口,生产者和消费者都需要跟这个Broker建立一个连接,才可以实现消息的收发。
Record-记录
客户端之间传输的数据叫做消息,或者叫做Record [记录];在客户端的代码中,Record可以是一个KV键值对;生产 者对应的封装类是ProducerRecord,消费者对应的封装类是ConsumerRecord;消息在传输的过程中需要序列化,所 以代码里面要指定序列化工具;消息在服务端的存储格式 [RecordBatch和Record]
Producer-生产者
发送消息的一方叫做生产者,接收消息的一方叫做消费者;Kafka为了提升消息发送速率,生产者不是逐条发送消息 给Broker,而是批量发送的,多少条发送一次由一个参数决定
Consumer-消费者
Kafka消费者获取消息的模式为:Push,消费者可以自己控制一次到底获取多少条消息
Topic-主题
生产者发送消息,要指定发给哪个队列。消费者接收消息,要指定从哪个队列接收;在Kafka里面,这个队列叫做 Topic,是一个逻辑的概念,可以理解为一组[不同业务用途的消息] 消息的集合;生产者发送消息时,如果Topic不存 在,会自动创建,由一个参数控制
Partition-分区
如果说一个Topic中的消息太多,会存在什么问题? 不方便横向扩展并发或者负载的问题 解决方法就是把一个Topic进行拆分,Kafka引入了一个Partition[分区]的概念,一个Topic可以划分成多个分区,分区 在创建Topic的时候指定,每个Topic至少有一个分区
Partition思想上有点类似于分库分表,实现的也是横向扩展和负载的目的
分区Replica副本机制
如果Partition的数据只存储一份,在发生网络或者硬件故障的时候,该分区的数据就无法访问或者无法恢复了, Kafka在0.8的版本之后增加了副本机制,每个Partition可以有若干个Replica[副本],副本必须在不同的Broker上面, 一般我们说的副本包括其中的主节点,由replication-factor指定一个Topic的副本数
如图:部署3个Broker,该Topic有3个分区,每个分区一共3个副本
Partition副本有Leader[红色]和Follower[非红色Partition]的概念。Leader在哪台机器是不一定的,通过选举产生的; 生产者发消息、消费者读消息都是针对Leader,Follower的数据是从Leader同步过来的。
Segment-段
Kafka的数据是放在后缀.log的文件中的,如果一个Partition只有一个log文件,消息不断地追加,这个log文件也会变 得越来越大,这个时候要检索数据效率就很低了;所以干脆把Partition再做一个切分,切分出来的单位就叫做段 [Segment],实际上Kafka的存储文件就是划分成段来存储的。
Consumer Group-消费者组
如果生产者生产消息的速率过快,会造成消息在Broker中堆积,影响Broker性能,通过增加消费者的数量,可以缓解消息在Broker上的堆积,问题又来了,这么多消费者,要确保大家去消费同一个Topic,Kafka引入了一个Consumer Group消费组的概念,在代码中通过group id来配置。消费同一个Topic的消费者不一定是同一个组,只有group id相同的消费者才是同一个消费者组,我们使一个消费者组去消费一个Topic的内容,就可以达到提高消费吞吐量的目 的;另外,同一个Group中的消费者,不能消费相同的Partition,Partition要在组内消费者间进行分配,如果想要同时消费同一个Partition的消息,只能通过其他的消费者组来消费;
Consumer Offset-偏移量
Partition中的消息是顺序写入的,被读取之后不会被删除,如果消费者挂了或者下一次读取,想要接着上次的位置读 取消息,或者从某个特定的位置读取消息,需要确保不会出现从头消费、重复消费的情况; Kafka的解决方案:因为消息是有序的,就可以对消息进行编号,用来标识一条唯一的消息,这个编号我们就把它叫 做Offset-偏移量;Offset记录着下一条将要发送给Consumer的消息的序号,这个消费者跟Partition之间的偏移量没有 保存在ZK,而是直接保存在服务端 [0.9版本以前维护在ZK中,但是ZK本身不适合大量写入,后来做了改动,将数据维护在kafka的_consumer_offsets主题下];
高级特性
消息幂等性
概念
如果消费失败了,消息需要重发,但在不清楚消费者是不是真的消费失败的情况下,就有可能会出现消息重复的情况;消息重复需要在消费端解决,也就是在消费者实现幂等性;考虑到所有的消费者都要做相同实现,Kafka干脆在 Broker实现了消息的重复性判断,解放消费者;去重肯定要依赖于生产者消息的唯一标识,不然是没办法知道是不是同一条消息。
两个重要机制
Producer ID - 幂等性的生产者每个客户端都有一个唯一编号;
Sequence Number - 幂等性的生产者发送的每条消息都会带相应的SN(若SN<服务端记录的min值,即消息重 复)
局限性
但Sequence Number并非全局有序,不能保证所有时间上的幂等,它的作用范围是有限的:
**只能保证单分区上的幂等性 **
只能实现单会话上的幂等性(会话:Producer进程的一次运行)
即:不允许生产者在一次会话中向同一个Patition发送相同的消息;若要实现多个分区的消息的原子性,就要用到 Kafka的事务机制。
生产者事务
Kafka 0.11.0.0引入生产者事务特性,通过事务,Kafka可以保证跨生产者会话的消息幂等发送;事务实现的前提是幂 等性。
何时需要开启事务:
**发送多条消息 (单体架构:1个Producer、Broker、Topic) **
**发送消息到多个Topic或者多个Partition(集群架构,不同的服务器[Broker]) **
消费后发送消息(消费者和生产者在同一块代码中,从上游接收消息,经过处理后发给下游,要保证接收消息 和发送消息同时成功)
版权归原作者 一条酸菜鱼2 所有, 如有侵权,请联系我们删除。