MQ(消息中间件专题)

Kafka

MQ概念

MQ是什么:Message Queue 消息队列,在消息的传输过程中用于保存消息的容器

MQ应用场景

1. 应用解耦

2. 异步提速

3. 限流削峰

常用MQ比对

Kafka概述与环境搭建

Kafka 概述

Kakfa是一个分布式的基于发布/订阅模式的消息队列(message queue),主要应用于大数据的实时处理领域。具有以下特性
1、高吞吐量、低延迟
2、持久性、可靠性
3、高并发
4、容错性
5、可扩展性
6、消息顺序性

Kafka应用场景
1.消息队列
2.日志收集
3.网站活性跟踪
4.运营指标监控
5.流式处理

KafKa 资源

GITHUB:https://github.com/apache/kafka
英文文档:https://kafka.apache.org/documentation/
中文文档:https://kafka.apache.org/34/documentation.html

环境安装

KafKa 架构设计

Broker

  • Broker就是Kafka的服务器,用于存储和管理消息,默认是9092的端口
  • 生产者和Broker建立连接,将消息发送到服务器上、存储起来
  • 消费者跟Broker建立连接,订阅和消费服务器上存储的消息

Record

  • 客户端之间传输的数据叫做消息,在Kafka中也叫Record(记录)
  • Record在客户端中是一个KV键值对(ProducerRecord、ConsumerRecord)
  • Record在服务端中的存储格式也是KV键值对(RecordBatch 或Record)

Producer

  • 发送消息的一方叫做生产者
  • Kafka为提升消息发送速率,生产者默认采用批量发送的方式发送消息至Broker
  • 一次发送多少条由参数 batch.size 决定

Consumer

  • 订阅、接收消息的一方叫做消费者
  • 消费者端获取消息有两种模式:Pull模式[拉]、Push模式[推]
  • Pull模式,消费者可以自己控制一次到底获取多少条消息(max.poll.records)

Topic

  • Topic(主题)是一个逻辑概念,可以理解为一组消息的集合
  • 生产者和消费者通过Topic进行消息的写入和读取
  • 生产者发送消息时,若Topic不存在,是否自动创建:auto.create.topics.enable

Partintion

  • 所谓分区(Partition)就是把一个Topic分成几个不同的部分
  • 一个Topic可以在创建时划分成多个分区
  • 若没有指定分区数,默认分区数为1,通过参数可修改(num.partitions)
  • Kafka中修改分区的规则:可加、不可减

Replica 机制

  • Replica(副本)是Partition(分区)的副本,每个分区可以有若干个副本
  • 副本必须在不同的Broker节点上,副本包括了主从节点(Leader、Follower)
  • 服务端可以通过参数控制默认副本数(offsets.topic.replication.factor)

Segment

  • Segment(段)的目的是:将一个分区中的数据划分、存储到不同的文件中
  • 每个Segment至少由1个数据文件和2个索引文件构成,3个文件是成套出现的
  • 引入段带来的意义:加快查询效率;删除数据时减少逐条IO
  • Segment大小控制:按时间周期生成(log.roll.hours);按文件大小生成(log.segment.bytes)

Consumer Group

  • 使用消费者组,提升消费效率和吞吐量
  • 同一个Group中的消费者,不能消费相同的Partition(group id相同,在一个组中)

Consumer Offset

  • Offset(偏移量)的目的在于:记录消费者的消费位置
  • Kafka现行版本将Offset保存在服务器(__consumer_offsets)主题中

Kafka架构图示

Kafka 特性 

磁盘顺序I/O
索引

Broker端原理数据存储
Offset索引、时间戳索引、稀疏索引

批量读写和压缩算法

收发消息时批量处理
压缩算法进行压缩后传递

零拷贝

 

Kafka Java API

Java API

Kafka与Springboot集成

Kafka 高阶功能

消息幂等性

开启生产者幂等性: enable.idempotence = true(默认值)

幂等性生产者发送消息流程总结:
1、Producer端发送消息(消息本身、PID、Sequence Number) 
2、Broker端接收到消息(将消息和PID、Sequence Number一起保存)
3、若ACK响应失败,生产者重试,再次发送消息

生产者事务

Kafka事务API相关方法

  • initTransactions() 初始化事务
  • beginTransaction() 开启事务
  • commitTransaction() 提交事务
  • abortTransaction() 中止事务
  • sendOffsetsToTransaction()

Producer 原理

1 生产者发送消息流程

消息发送流程分析

Kafka在发送消息的过程中, 主要涉及两个线程 main线程和 sender线程。
在main线程中创建了一个双端队列 RecordAccumulator. main线程将消息发送给 RecordAccumulator。
Sender线程不断从RecordAccumulator 中拉取消息发送到Kafka Broker。

拦截器

拦截器的作用是实现消息的定制化(类似于Spring Interceptor、MyBatis的插件、Quartz的监听器)。

拦截器使用. KafkaProducer.java

    public Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback) {
        ProducerRecord<K, V> interceptedRecord = this.interceptors.onSend(record);
        return this.doSend(interceptedRecord, callback);
    }

自定义拦截器

/**
 * 自定义拦截器
 */
public class ChargingInterceptor implements ProducerInterceptor<String, String> {
    // 发送消息的时候触发
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        System.out.println("1分钱1条消息,不管那么多反正先扣钱");
        return record;
    }

    // 收到服务端的ACK的时候触发
    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
        System.out.println("消息被服务端接收啦");
    }

    @Override
    public void close() {
        System.out.println("生产者关闭了");
    }

    // 用键值对配置的时候触发
    @Override
    public void configure(Map<String, ?> configs) {
        System.out.println("configure...");
    }
}

装配自定义拦截器

// 添加拦截器
List<String> interceptors = new ArrayList<>();
interceptors.add("com.mq.kafka.javaapi.interceptor.ChargingInterceptor");
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);

 序列化

接下来是利用指定的工具对key和value进行序列化:

pros.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer"); pros.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");

分区器

分区指定

int partition = this.partition(record, serializedKey, serializedValue, cluster);

一条消息会发送到哪个partition呢?它返回的是一个分区的编号,从0开始。有四种情况:

  • 指定了partition——直接将指定的值直接作为partiton值。
  • 没有指定partition,自定义了分区器——将使用自定义的分区器算法选择分区。
  • 没有指定partition,没有自定义分区器,但是key不为空——使用默认分区器DefaultPartitioner,将key的hash值与topic的partition数进行取余得到partition值;
  • 没有指定partition,没有自定义分区器,但是key是空的——第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与topic可用的partition总数取余得到 partition值,也就是常说的round-robin算法。

2 生产者数据可靠性保证-ACK机制

服务器端响应策略

生产者的消息是不是发出去就完事了?如果说网络出了问题,或者说kafka服务端接收的时候出了问题,这个消息发送失败了,生产者是不知道的。所以,kafka服务端应该要有一种响应客户端的方式,只有在服务端确认以后,生产者才发送下一轮的消息,否则重新发送数据。
  服务端什么时候才算接收成功呢?因为消息是存储在不同的partition里面的,所以是写入到partition之后响应生产者。当然,单个partition (leader)写入成功,还是不够可靠,如果有多个副本,follower 也要写入成功才可以。
  为了安全性考虑,Kafka会等待所有的follower全部完成同步,才发送ACK给客户端,延迟相对来说高一些,但是节点挂掉的影响相对来说小一些,因为所有的节点数据都是完整的。

ISR机制(in-sync replica set)

        然而以上方案仍然存在问题:假设leader收到数据,所有follower都开始同步数据,但是有一个follower出了问题,没有办法从leader同步数据。按照这个规则,leader就要一致等待,无法发送 ack。
        从概率的角度来讲,这种问题肯定是会出现的,就是某个follower出问题了,怎么解决呢?所以我们的规则就不能那么粗暴了,把规则改一下,不是所有的follower都有权利 让我等待,而是只有那些正常工作的follower同步数据的时候我才会等待。
        我们应该把那些正常和leader保持同步的replica维护起来,放到一个动态set里面,这个就叫做in-sync replica set (ISR)。现在只要ISR里面的follower同步完数据之后,我就给客户端发送ACK。如果一个follower长时间不同步数据,就要从ISR剔除。同样,如果后面这个follower重新与leader保持同步,就会重新加入ISR。时间间隔由以下参数决定:

replica.lag.time.max.ms=10000

ACK应答机制

        当然,如果所有的数据都一视同仁,而且这种策略只能由服务端决定,这就不是很灵活了。有一些数据丢了无所谓,我只想要快,不管它落没落盘同没同步,怎么办呢?
  Kafka为客户端提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择相应的配置。

  • acks=0: producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据;
  • acks=1(默认): producer 等待 broker 的 ack, partition 的 leader 落盘成功后 返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;
  • acks=-1 (all) : producer等待 broker 的 ack, partition 的 Ieader和 follower全部落盘成功后才返回ack。

三种机制,性能依次递减(producer吞吐量降低),数据健壮性则依次递增。我们可 以根据业务场景使用不同的参数。

Broker原理

文件存储结构

Partition分区

  • 为了实现横向扩展,把不同的数据存放在不同的Broker上,同时降低单台服务器的访问压力,我们把一个Topic中的数据分隔成多个Partition;
  • 每个Partition中的消息是有序的,顺序写入,但是全局不一定有序;
  • 在服务器上,每个Partition都有一个物理目录(TopicN)后面的数字代表分区

Replica副本

  • 为了提高分区的可靠性,Kafka设计了副本机制
  • 副本数必须小于等于节点数,而不能大于Broker的数量
  • Leader对外提供读写服务,Follower唯一的任务就是从Leader异步拉取数据

谁是Leader

Broker中的副本分布

实际副本分布规则是由AdminUtils.scala——assignReplicasToBrokers决定的,具体规则如下:

  1. 副本因子不能大于Broker的个数;
  2. 第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
  3. 其他分区的第一个副本放置位置相对于第0个分区依次往后移 (nextReplicaShift)。

这样设计可以提高容灾能力。在每个分区的第一个副本错开之后,一般第一个分区的第一个副本(按Broker编号排序)都是leader。leader是错开的,不至于一挂影响太大。

Segment

为了防止Log不断追加导致文件过大,导致检索消息效率变低,一个Partition又被划分成多个Segment来组织数据

log.segment.bytes:根据日志文件大小  默认1GB
log.roll.hours、log.roll.ms:根据时间戳差值
log.index.size.max.bytes:根据索引文件大小

每个Segment由一个log文件和2个index文件组成 

索引

在Kafka中设计了两种索引:偏移量索引文件记录的是。offset和消息物理地址(在log文件中的位置)的映射关系。时间戳索引文件记录的是时间戳和offset的关系

Kafka索引文件中记录的Offset不是连续的,而是采用了稀疏索引。至于这个索引有多稀疏,由以下参数决定:

log.index.interval.bytes=4096

只要写入的消息超过了 4KB,偏移量索引文件index和时间戳索引文件.timeindex就会増加一条索引记录(索引项)。这个值设置越小,索引越密集。值设置越大,索引越稀疏。相对来说,越稠密的索引检索数据更快,但是会消耗更多的存储空间。越的稀疏索引占用存储空间小,但是插入和删除时所需的维护开销也小。Kafka索引的时间复杂度为O(log2n)+O(m),n是索引文件里索引的个数,m为稀疏程度。
  第二种索引类型是时间戳索引。时间戳有两种,一种是消息创建的时间戳,一种是消费在Broker追加写入的时间。到底用哪个时间由以下参数控制:

 log.message.timestamp.type=CreateTime

 默认是创建时间。如果要改成日志追加时间,则修改为LogAppendTime。査看最早的10条时间戳索引:

./kafka-dump-log.sh —files /tmp/kafka-logs/mytopic-0/00000000000000000000.timeindex|head -n 10

那么Kafka如何基于索引快速检索消息?比如我要检索偏移量是10002673的消息。

  • 消费的时候是能够确定分区的,所以第一步是找到在哪个segment中。Segment 文件是用base offset命名的,所以可以用二分法很快确定(找到名字不大于10002673 的 最大segment)。
  • 这个segment有对应的索引文件,它们是成套出现的。所以现在要在索引文件中根据offset找position。
  • 得到position之后,到对应的log文件开始査找offset,和消息的offset进行比较,直到找到消息。

 

消息保留[清理]机制

开关与策略

由于Kafka的日志并没有在消费后马上删除,随着时间的推移,日志越来越多,需要有相应的清理策略来保证系统的健康,日志清理的开关由以下参数控制:

log.cleaner.enable=true

Kafka提供了两种清理方式,分别是删除delete和压缩compact,由以下参数控制:

 log.cleanup.policy=delete

删除策略

Kafka可以通过定时任务实现日志数据的删除,默认5分钟执行一次

log.retention.check.interval.ms=300000

删除数据的配置项

log.retention.hours(默认值是168个小时,时间戳超过的数据会被删除)

log.retention.minutes(默认值是空,优先级比小时高)

log.retention.ms(默认值是空,优先级比分钟高)

若产生消息的速度不均匀,有时多、有时少,就可以根据日志大小删除

log.retention.bytes(表示所有日志文件的总大小,默认值是-1,代表不限制大小)

log.segment.bytes(对单个Segment文件大小进行限制,默认值1G)

压缩策略

若设置为compact,则表示不清除日志,只对日志数据进行压缩处理

Broker高可用架构

Controller选举

Controller(控制器)其实就是一个Broker,由它负责选举出新的Leader

分区副本Leader选举

Controller确定以后,开始进行leader选举。副本有3个概念:

  • Assigned-Replicas (AR):一个分区的所有副本
  • In-Sync Replicas (ISR):一个分区中跟leader数据保持一定程度的同步的副本
  • Out-Sync-Replicas (OSR):跟leader副本同步滞后过多的副本

只有ISR有资格参加leader选举。而且这个ISR不是固定不变的,它是一个动态的列表。
如果同步延迟超过30秒,就踢出ISR,进入OSR。如果赶上来了, 就加入ISR。默认情况下,当leader副本发生故障时,只有在ISR集合中的副本才有资格被选举为新的leader。极端情况下,如果ISR为空,则可以允许ISR之外的副本参与选举,由以下参数控制:

unclean.leader.election.enable=true

Kafka采用了类似于继位传嫡的选举协议,选择ISR中位置靠前的节点成为新Leader 

主从同步

Leader确定之后,客户端的读写只能操作leader节点。follower需要向leader同步数据。不同的raplica的offset是不一样的,同步到底怎么同步呢?

首先需要认识几个概念:
LEO (Log End Offset):下一条等待写入的消息的offset (最新的offset + 1),图中分别是9, 8, 6。
HW (Hign Watermark): ISR中最小的LEO。Leader会管理所有ISR中最小的 LEO作为HW,目前是6。

consumer最多只能消费到HW之前的位置(消费到offset 5的消息)。也就是说: 其他的副本没有同步过去的消息,是不能被消费的。
  从节点和主节点的同步过程

1、首先,Follower节点向Leader发送一个fetch请求
2、然后,Leader向Follower发送数据
3、接着,Follower接收到数据响应后,依次写入消息、并更新LEO值
4、最后,Leader更新HW(ISR最小的LEO)
5、循环上述过程,直至所有Follower完成数据同步

Kafka设计的ISR复制,既可以在保障数据一致性、又可以提供高吞吐量。 

Replica故障处理
follower故障

首先follower发生故障,会被先踢出ISR。follower恢复之后,从哪里开始同步数据呢?假设第1个replica宕机(中间这个)。

恢复以后,首先根据之前记录的HW (6),把高于HW的消息截掉(6、7)。然 后向leader同步消息。追上leader之后(30秒),重新加入ISR。 

leader故障

假设图中leader发生故障。首先选一个leader。因为replica 1 (中间这个)优先,它成为leader。为了保证数据一致,其他的follower需要把高于HW的消息截取掉(这里没有消息需要截取)。然后replica2同步数据。注意:这种机制只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。

Consumer原理

flush.message 有多少条消息就刷盘
flush.ms 间隔多少毫秒就刷盘

Offset的维护

Offset的存储

        我们知道在partition中,消息是不会删除的,所以才可以追加写入,写入的消息连续有序的。这种特性决定了 kafka可以消费历史消息,而且按照消息的顺序消费指定消息,而不是只能消费队头的消息。正常情况下,我们希望消费没有被消费过的数据,而且是从最先发送(序号小的) 的开始消费(这样才是有序和公平的)。
  那么对于一个partition,消费者组怎么才能做到接着上次消费的位置(offset)继续消费呢?肯定要把这个对应关系保存起来,下次消费的时候查找一下。

那么这个对应关系到底是保存在哪里的呢?
  首先肯定是不可能放在消费者本地的。因为所有的消费者都可以使用这个consumer group id,放在本地是做不到统一维护的,肯定要放到服务端。
  Kafka早期的版本把消费者组和partition的offset直接维护在ZK中,但是读写的性能消耗太大了。后来就放在一个特殊的topic中,名字叫—consumer_offsets,默认有 50 个分区(offsets.topic.num.partitions 默认是 50),每个分区默认一个replication。

那么这样一个特殊的Topic怎么存储消费者组gp-assign-group-1对于分区的偏移量的?Topic里面是可以存放对象类型的value的(经过序列化和反序列化)。这个Topic 里面主要存储两种对象:
  GroupMetadata:保存了消费者组中各个消费者的信息(每个消费者有编号)。
  OffsetAndMetadata:保存了消费者组和各个partition的offset位移信息元数据。

./kafka-console-consumer.sh —topic consumer_offsets --bootstrap-server 192.168.44.161:9093,192.168.44.161:9094,192.168.44.161:9095 -formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" —from-beginning

 

怎么知道一个consumer group的offset会放在这个特殊的Topic的哪个分区呢? 可以通过哈希取模计算得到:

Math.abs("gp-assign-group-1".hashCode()) % 50;

如果找不到Offset

如果增加了一个新的消费者组去消费—个topic的某个partion,没有offset的记录,这个时候应该从哪里开始消费呢? 由以下参数控制:

auto.offset.reset=latest

latest: 从最新的消息(最后发送的)开始消费。
earliest: 从最早的(最先发送的)消息开始消费。可以消费到历史消息。
none: 如果consumer group在服务端找不到offset会报错。 

Offset的更新

消费者组的Offset储存在Broker上,由消费者上报给Broker。并不是消费者组消费了消息,Offset就会更新,消费者必须要有一个commit (提交)的动作。消费者可以自动提交或者手动提交。由以下参数控制:

enable.auto.commit=true   自动提交

auto.commit.interval.ms=5000 (默认值5秒钟)自动提交的频率

enable.auto.commit=false 手动提交

consumer.commitSync() 手动同步提交 

consumer.commitAsync() 手动异步提交

Offset若不提交或提交失败,Broker的Offset就不会更新,消息会被重复消费 

消费者消费策略

消费策略

  • 使用消费者组,提升消费效率和吞吐量
  • 同一个Group中的消费者,不能消费相同的Partition

  • RangeAssignor:范围策略(按范围连续分配)
  • RoundRobinAssignor:轮询策略
  • StickyAssignor:粘滞策略

粘滞的分配策略较为复杂,它的核心思想是在分区重新分配时保证最小的移动(类似Redis的一致性hash思想,实现方式不同)。

第一次分配类似轮询,结果如下:
  C1:P0 P3 P6
  C2:P1 P4 P7
  C3:P2 P5

假设此时C2挂掉:
若按照RoundRobin,结果如下:
  C1:P0 P2 P4 P6
  C3:P1 P3 P5 P7

sticky结果如下(尽量保证P0 P3 P6 P2 P5不动):
  C1:P0 P3 P6 P1
  C3:P2 P5 P4 P7

  我们知道在分区分配时是会造成性能损耗的,若采用sticky分配策略可以尽可能减少性能损耗。该思想与Redis的一致性Hash是类似的,只是实现方式不同。

Rebalance 分区再均衡

1. 什么是ReBalance?

分区再均衡本质上是一种协议(管理Consumer与Partition的匹配关系)

2. 何时发生ReBalance?

消费者组的消费者数量发生变化

Topic的分区数发生变更

3. ReBalance 分区再均衡 

  • 确定协调者coordinator,通常由集群中负载最小的Broker承担。
  • 所有consumer向coordinator发送join group请求,确认自己是该组成员。
  • coordinator在所有consumer中确定leader(通常是第一个)并由leader确定分区分配结果。
  • coordinator向所有consumer发送分区分配结果

Kafka 为什么这么快

磁盘顺序I/O

Kafka的message是不断追加到本地磁盘文件末尾的,而不是随机的写入,这使得 Kafka写入吞吐量得到了显著提升。

索引机制

我们在写入日志的时候会建立关于Offset和时间的稀疏索引,提升了查找效率,这个上面已经提到过了。

批量操作和压紧

Kafka无论是生产者发送消息还是消费者消费消息都是批量操作的,大大提高读写性能。

零拷贝

RocketMQ

RocketMQ基本介绍

特点

  • 单队列中可靠的FIFO 和 严格的顺序传递
  • 支持:推(push)、拉(pull)两种消息消费模式Ø 单一队列百万消息的堆积能力
  • 支持多种消息协议
  • 分布式高可用的部署架构
  • 自带Dashboard

优势

  • 支持事务型消息(分布式)
  • 支持18个级别的延迟消息
  • 支持指定次数和时间间隔的失败消息重发
  • 支持Tag过滤,减少不必要的网络传输
  • 支持重复消费

RocketMQ环境安装

RocketMQ单机安装

RocketMQ集群安装

RocketMQ系统架构

主要构成

Producer

  • 消息生产者,负责生产消息
  • 定时从Name Server拉取消息,并且和Broker建立TCP的长连接
  • Producer只能往Master节点中写入数据
  • 发送消息时,通过MQ的负载均衡模块自动选择相应的Broker集群队列进行消息投递
  • RocketMQ中的消息生产者都是以Producer Group的形式出现
  • RocketMQ提供了多种发送方式:    同步发送, 异步发送,单向发送

Consumer

  • 消息消费者,负责消费消息
  • Consumer既可以从Master订阅消息,也可以从Slave订阅消息
  • RocketMQ中的消息消费者都是以Consumer Group的形式出现

关于消费者组,需要注意:
1、消费者组中Consumer的数量应该小于等于订阅Topic的Queue数量
2、一个组中的消费者必须订阅完全相同的Topic

Broker

  • 角色:消息中转,负责:存储消息、转发消息
  • RocketMQ的Broker同时还存储了消息相关的元数据
  • Broker有Master和Slave两种类型: Master可读可写; Slave可读不可写
  • 生产者生成消息后,存入Broker的队列中,消费者来消费
  • 消息并发量增加后,单个queue可能出现性能瓶颈,可以在Broker中使用多队列
  • 可以使用消息日志,提供消息的持久化存储
  • 为了提升Broker的可用性,还支持Master-Slave主从复制

Name Server

  • RocketMQ的服务注册中心(指挥中心)
  • Name Server是无状态节点,可集群部署
  • Name Server是一个Broker与Topic路由的注册中心,支持Broker的动态注册与发现

重要概念

Message - 消息

  • 消息系统所传输信息的物理载体,生产和消费数据的最小单位
  • 消息不可变性、消息落盘
  • 消息类型:Normal普通消息、FIFO顺序消息、Delay延时消息、Transaction事务消息

Topic - 主题

  • 消息的逻辑组织形式,真正存储消息的是队列
  • 一个Topic只收发一种类型的消息,用以实现业务数据的隔离
  • 生产环境下要规避主题的自动化管理

Tag - 标签

  • 消息的标签,用于区分同一主题下、不同类型的消息
  • Topic:消息一级分类(业务分类)、Tag:消息二级分类(子主题)

Queue - 队列

  • RocketMQ中消息存储和传输的实际容器,也是消息的最小存储单元
  • 单个队列内的消息天然存在顺序关系,队列是负载均衡过程中资源分配的基本单元

Sharding - 分片

  • 一个Topic在每个Broker上面的Queue的集合,目的在于水平扩展

RocketMQ工作流程

1)启动NameServer,NameServer启动后开始监听端口,等待Broker、Producer、Consumer连接。

2)启动Broker时,Broker会与所有的NameServer建立并保持长连接,然后每30秒向NameServer定时发送心跳包。

3)发送消息前,需要先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上以及在这些broker上创建几个Queue,在创建Topic时也会将Topic与Broker的关系写入到NameServer中。

)Producer发送消息,启动时与NameServer集群中的其中一台建立长链接,并从NameServer中获取路由信息,即当前发送的Topic消息的Queue与Broker的地址(IP+Port)的映射关系。然后根据算法策略选择一个Queue,与队列所在的broker建立长链接从而向broker发送消息。在获取到路由信息后,Producer会首先将路由信息缓存到本地,再每隔30s从NameServer更新一个路由信息。

5)Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取其所订阅Topic的路由信息,然后根据算法策略从路由信息中获取到其所要消费的Queue,然后直接跟Broker建立长连接,开始消费其中的消息。Consumer在获取到路由信息后,同样也会每30秒从NameServer更新一次路由信息。不过不同于Producer的是,Consumer还会向Broker发送心跳,以确保Broker的存活状态。

RocketMQ设计理念

  • Name Server 的极简设计
  • 高效的 IO 存储机制
  • 暴露设计缺陷(消费端需业务去重)

分布式事务管理

概念与思路

什么叫分布式事务

在本地事务的基础上,保证在分布式环境下数据的一致性

产生的原因?--- 分库分表;服务拆分

分布式事务的使用场景? ---支付;在线下单;跨行转账

RocketMQ事务消息思路

将本地事务和发消息放置在一个分布式事务中,保证两者要么都成功,要么都失败

RocketMQ事务消息主线流程:

1、发送一个半事务消息

2、执行本地事务

3、提交事务执行状态

事务消息实现过程

原理分析

TransactionListenner 接口

发送半消息流程[Producer端]

发送半消息流程[Broker端]

事务消息回查流程[Broker端]

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值