1,消息中间件(MQ)
关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
功能项 | Kafka(1.1.0版本) | RabbitMQ(3.6.10版本) | RocketMQ(4.2.0) |
---|---|---|---|
单机吞吐量 | 10w级,支持高吞吐 | 万级 | 10w级 |
消息可靠性 | 经过参数优化配置,做到0丢失 | 基本不丢 | 经过参数优化配置,做到0丢失 |
优先级队列 | 不支持 | 支持,建议优先级大小设置在0-10之间 | 支持,维护不同的优先级队列,根据message的优先级发送到对于的队列中 |
延迟队列 | 不支持 | 支持 | 支持 |
死信队列 | 不支持 | 支持 | 支持 |
重试队列 | 不支持 | 支持 | 支持 |
消费模式 | pull + push | pull + push | pull+长轮询 |
广播消费 | 支持,kafka对于广播消息的支持更加正统 | 支持 | 支持 |
批量消息 | 支持,生产者异步发送 | 支持 | 支持,生产者同步发送 |
消息回溯 | 支持.Kakfa支持按照offset和timestamp两种维度进行消息回溯 | 不支持,一旦被确认消费就会被删除 | 支持 |
消息堆积 | 支持,海量消息堆积,堆积能力和磁盘大小挂钩 | 支持,但是内存达到阀值时,性能会受影响 | 支持海量消息堆积 |
持久化方式 | 消息队列,segment方式 | 支持 | 消息队列 |
消息追踪 | 不支持。消息追踪可以通过外部系统来支持,但是支持粒度没有内置的细腻。 | 支持 | 支持 |
消息过滤 | 客户端级别的支持,可用过kafka stream进行消息过滤 | 不支持,但是二次封装一下也较简单 | 支持,可通过message tag、属性进行过滤 |
多租户 | 不支持 | 支持 | 不支持 |
多协议支持 | 只支持定义协议,目前几个主流版本之间存在兼容性问题 | AMQP | JMS ,OpenMessaging |
跨语言支持 | 当前版本采用Java编写,支持多种语言的客户端 | 采用Erlang编写,支持多种语言客户端 | Java, C++, Go |
流量控制 | 支持client和user级别,通过主动设置可将流控作用于生产者或消费者 | 流量控制基于credit-base算法,是内部被动触发的保护机制,作用于生产者层面 | RocketMQ提供了针对于不同维度的流量控制 |
消息顺序性 | 支持普通的顺序消息,即对于单个分区的消息发送和消费是有序的,但是不保证不重复 | 顺序性的条件比较苛刻,需要单线程发送,单线程消费并且不采用延迟队列、优先级队列等一些高级功能,从某种意义上来书不支持顺序性 | 支持普通的顺序消息和严格的顺序消息 |
幂等性 | 支持单个Producer单个分区的会话幂等性 | 不支持 | 不支持,不解决消息的重复问题 |
事务性消息 | 最新版支持事务消息 | 支持 | 最新版的metaq支持事务消息 |
高可用和容错 | 使用partition的副本机制和isr选举机制保证高可用 | 普通集群非高可用,可用镜像模式和主备集群 | 通过broker的master和slave实现高可用 |
定时消息 | 不支持 | 支持 | 支持 |
负载均衡 | 客户端消费者负载均衡,需要一个broker作为coordinator | 默认是轮询 | 客户端负载均衡,支持平均和轮询分配 |
刷盘策略 | 默认是异步刷盘 | 默认是内存存储 | 默认同步刷盘 |
1)kafka
1>概念
Kafka是一种高吞吐量的分布式发布订阅消息系统,是观察者模式的一种实现。
Kafka是一个分布式消息队列。
2>场景
两个需要通信的系统中,如果数据生产者和消费者之间的速度差异大,可以通过kafka作为中间件,削峰。
- 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
- 消息系统:解耦和生产者和消费者、缓存消息等。
- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
- 流式处理:比如spark streaming和storm
- 事件源
2)RabbitMQ
- Producer(数据的发送方)
一个Message有两个部分:payload(有效载荷)和label(标签)。
payload:传输的数据。
label是exchange的名字或者说是一个tag,它描述了payload,而且RabbitMQ也是通过这个label来决定把这个Message发给哪个Consumer。
AMQP(ActiveMQ)仅仅描述了label,而RabbitMQ决定了如何使用这个label的规则。 - Consumer(数据的接收方)
把queue比作是一个有名字的邮箱。当有Message到达某个邮箱后,RabbitMQ把它发送给它的某个订阅者即Consumer。当然可能会把同一个Message发送给很多的Consumer。在这个Message中,只有payload,label已经被删掉了。对于Consumer来说,它是不知道谁发送的这个信息的,就是协议本身不支持。当然了,如果Producer发送的payload包含了Producer的信息就另当别论了。 - Queue(消息队列)
提供了FIFO的处理机制,具有缓存消息的能力。rabbitmq中,队列消息可以设置为持久化,临时或者自动删除。
设置为持久化的队列,queue中的消息会在server本地硬盘存储一份,防止系统crash,数据丢失设置为临时队列,queue中的数据在系统重启之后就会丢失 设置为自动删除的队列,当不存在用户连接到server,队列中的数据会被自动删除 - Exchange(消息路由)
Exchange类似于数据通信网络中的交换机,提供消息路由策略。rabbitmq中,producer不是通过信道直接将消息发送给queue,而是先发送给Exchange。一个Exchange可以和多个Queue进行绑定,producer在传递消息的时候,会传递一个ROUTING_KEY,Exchange会根据这个ROUTING_KEY按照特定的路由算法,将消息路由给指定的queue。和Queue一样,Exchange也可设置为持久化,临时或者自动删除。 - Binding(绑定)
把exchange和queue按照路由规则绑定起来 - Routing Key(路由关键字)
exchange根据这个关键字进行消息投递 - VHost(虚拟主机)
一个broker里可以开设多个vhost,用作不同用户的权限分离 - Channel(消息通道)
在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
3)RocketMQ
- Producer(消息生产者)
将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。 - Consumer(消息消费者)
消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。 - NameServer服务(服务发现和路由)
提供了轻量级的服务发现和路由。每个NameServer服务记录完整的路由信息,提供一致的读写服务,支持快速存储扩展。 - Broker
通过提供轻量级主题和队列机制来处理消息存储。它们支持Push和Pull模型,包含容错机制(2个副本或3个副本),提供了极强的峰值处理里能力和按照时间顺序存储数以百万记的消息存储能力,此外,代理提供了灾难恢复、丰富的度量统计和警报机制,这些都是在传统的消息传递系统中缺乏的 - CommitLog
消息存储持久化咋commitLog中,一个broker只有一个commitLog,采用append的方式写入消息,为顺序写 - ConsumerQueue(消费者队列)
没有全量消息,存储的是在CommitLog中的偏移量延迟队列
4)优点
- 解耦
- 异步
耗时操作异步处理,不影响主流程。 - 削峰填谷
高并发场景下平衡系统压力:将峰值压力分散到较长时间段内,提高系统稳定性和可靠性。同时也提高了低谷期资源利用率。
2,架构
1)Producer(生产者)
向Kafka集群(Broker)中一个或多个topic push消息;
- 每条消息都被追加(append)到分区(patition)中,属于顺序写磁盘;顺序写磁盘效率比随机写内存要高,保障kafka吞吐率。
2)Consumer(消费者)
- 订阅消息
消费者可以订阅一个或多个主题(topic),并从Broker拉(pull)相同topic的数据消费。 - 提交偏移量(Offset)
将Offset提交回kafka以保证消息的顺序性和一致性。
1>Consumer Group(消费者组,CG)
逻辑上的一个订阅者。
- 每个Consumer属于一个特定的Consumer Group(可指定group name,若不指定属于默认的group)。
- 同一个Group中,只能有一个Consumer消费消息。多个Group可以同时消费一个partition。
如果需要实现广播,只要每个consumer有一个独立的CG就可以了。
要实现单播只要所有的consumer在同一个CG。 - Kafka中同一消费者组不能同时消费一个分区,只能去消费不同分区,因此在生产环境中,
consumer数量 = 分区数
。
2>topic(主题)
承载消息的逻辑容器,本质就是一个目录,而topic是由一些Partition Logs(分区日志)组成。
- topic包含一个或多个分区(partition);
对应 server.properties 中的num.partitions=3
配置; - 同一个topic的不同partition存储在不同的broker上;
3>Parition(分区):高吞吐量的核心设计。
一个有序不变的消息队列,每个patition物理上对应一个文件夹(该文件夹存储该patition的所有消息和索引文件。
- 每个broker可以存储一个或多个topic的partition。
高可用、容错能力. - 消费顺序:只保证按一个partition中的顺序消费,不保证一个topic的整体(多个partition间)的顺序。
- 副本(replication)
每个partition有若干个副本(replication):1个Leader和若干个Follower。server.properties 配置中的default.replication.factor=N
。
Leader负责生产者、消费者数据的发送和消费;
Follower负责同步Leader数据;
Leader故障时,Follower通过选举成为新的Leader。 - 分区的原则
1)指定了patition,直接使用;
2)未指定patition但指定key,通过对key的value进行hash出一个patition;
3)patition和key都未指定,使用轮询选出一个patition。
4>offset(偏移量)
每个partition中的每条消息都会被分配一个offset,单调递增且不变。
- 用于监控数据消费的位置,故障恢复后继续消费;
- kafka的存储文件都是按照
offset.kafka
来命名;
用offset做名字的好处是方便查找。例如查询位于2049的位置,只要找到2048.kafka的文件即可。the first offset是00000000000.kafka
。
3)Broker(Kafka集群)
已发布的消息保存在一组服务器中,称之为Kafka集群;每个节点的kafka实例都是一个代理(Broker)。
1> Controller
角色类似于其他分布式系统Master的角色。
运行在broker上,但是一个集群同时只能存在一个 Controller,且与数据节点在一起。
2>数据清理
无论消息是否被消费,kafka都会保留所有消息。有两种策略可以删除旧数据:
- 基于时间:log.retention.hours=168
- 基于大小:log.retention.bytes=1073741824
需要注意的是,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。
3,原理
1)kafka高性能原理
1>消息发送
- 批量发送;
将多个消息打包成一个批次发送,减少了网络传输和磁盘写入的次数。 - 消息压缩
提高io效率。 - 异步发送;
生产者只管发,不需要等待消息确认。
可能导致数据丢失,可以通过调整参数避免丢失:比如,减少刷盘间隔,减少刷盘数据量大小。时间越短,性能越差,可靠性越好(尽可能可靠)。 - 并行发送;
数据分布在不同的分区(Partitions)中,生产者可以并行发送数据。
2>消息存储
- 零拷贝
- 磁盘顺序写入
partition有序,批量顺序写入磁盘,节省了磁盘寻址时间、减少写入次数。
同时partition分为多个segment存储,方便删除(直接删除segment)。 - 页缓存(pageCache)
数据存储在磁盘,保证数据的可靠性、提高数据堆积能力。
每次访问数据时,将数据加载到页存(而不是jvm的堆内存),如果生产消费速度相同,数据会直接通过pageCache交互数据,不需要经过磁盘IO。 - 稀疏索引
kafka存储消息用分段的日志文件,每个分段都有索引文件,这个文件每隔一定数量的消息才创建一个索引点。
这样的稀疏索引减小了索引体积,提高了加载效率。
数据结构:Bitmap Index(位图索引),存储偏移量是否有数据。 - 分区和副本
数据分散到多个节点进行处理,从而实现了分布式的高可用性和负载均衡。
3>消息消费
- 消费组
实现了消息的负载均衡和容错处理。 - 并行消费
不同消费者独立消费不同的分区,并行消费。 - 批量拉取
可以一次拉取多个消息进行消费。减少网络消耗。
2)如何保证消息不丢失?
消息发送过程:
- Producer发送消息给broker;
- broker同步消息并持久化数据;
- consumer从broker消费(pull)
目前kafka默认消息交付保证at least once,消息不丢但是回重复传递。
1>丢失场景
ack取值说明:ack配置越高,吞吐量越低。
ack取值 | 说明 |
---|---|
0 | 发送出去不接受确认信息 |
1 | 只要leader回复确认信息即可 |
all或者-1 | 代表kafka集群必须全部回复确认信息 |
- ack=0,消息丢失无感知
unclean.leader.election.enable=true
允许选举OSR作为leader
由于OSR本身就数据不全,如果leader rash后,ISR中没有follower,就会从OSR中选举leader。
AR (Assigned Replicas,分区中的副本)
ISR (In Sync Replicas):与leader保持一定程度同步的副本(包括leader副本在内)组成 。follow副本相对于leader副本而言会有一定程度的滞后,这个范围可以通过参数进行配置。
OSR (Out-of-Sync Replied):于leader副本同步滞后过多的副本(不包括leader副本)。
AR = ISR + OSR:正常情况下,所有的follower副本都应该与leader 副本保持 一定程度的同步,即AR=ISR,OSR集合为空。
2>producer解决方式
ack=all
或者ack=2、3
;重试次数retries>1;unclean.leader.election.enable=false- 设置min.insync.replicas > 1
副本指定必须确认写操作成功的最小副本数量,值越大越可靠;相当于ISR副本的数量。
如果不能满足生产者会直接抛异常。只有ack=-1或者all时这个参数才生效。 - 失败的offset单独记录
放入db或者缓存,异常恢复后可单独处理恢复。
3>consumer解决方式
- 先commit再处理消息。
commit offset之后出现异常,消息处理时发生异常,会导致数据丢失。 - 先处理消息再commit。
不容易丢数据,但是可能导致数据重复消费问题。==》保持接口的幂等性可以处理此类问题。 - 保存offset,成功后才更新偏移量。
4>broker的刷盘去解决问题
数据发送到broker后是先保存在缓存中,后面再刷硬盘里。
在kafka中设置缓存刷到硬盘的时间间隔缩短,减少数据丢失概率。
3)如何避免重复消费?
比起丢数据,重复消费数据问题更小。客户端在接受kafka数据时注意建立幂等机制。
4)pull方式消费消息的优缺点
consumer采用pull(拉)模式从broker中读取数据。
1>优点
- 控制消费速率;
相比起来,push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。从而导致消息堆积引起网络堵塞和消费者拒绝服务。 - 可以批量拉取,也可以单条拉取。
- 可以设置不同的提交方式,实现不同的传输语义。
三种方式:至少一次、最多一次、精准一次。
2>缺点
- 如果kafka没有数据,导致consumer空循环,消耗资源。
解决方式:
通过参数配置,consumer拉取数据为空或者没有达到一定数量时进行阻塞。==》等待数据量到了指定的字节数,以确保大的传输大小。
5)ZK作用
kafka前期版本用来管理kafka集群、数据存储。
kafka2.8开始引入KRaft协议,逐渐减少对zk的依赖;4.0版本不再支持zk模式。
1>zk模式kafka:push模式
- 每个broker都可以做controller角色,谁先在zk注册谁就剩controller 节点。
controller 会在zk建立一个临时节点。 - 其它broker节点
watch
/controller znode
节点,如果掉线旧重新注册。 - controller节点会从zk中拉取当前集群所有的元数据信息,然后推送给集群里面所有的kafka 分区,分区会自动存储数据到磁盘,并异步传输到消费者。
可能会导致大量无效或重复的消息。
zk节点说明:
节点 | 功能 | 原理 |
---|---|---|
/brokers/ids | 注册broker,保存broker信息:物理地址、版本、启动时间 | 在zk中创建临时节点,broker会发心跳给zk |
/brokers/topics | 注册topic,保存topic信息。 每个topic节点下包含一个partitions节点; 每个partitions节点下由保存了一个state节点; state保存着当前leader分区和ISR的brokerID,同时维护ISR列表。 | zk中创建的临时节点 |
/consumers/[group_id]/owners/[topic]/[broker_id-partition_id] | 维护消费者和分区的注册关系,保存消费者组消费的是哪个partition | zk中临时节点。如果消费者有挂掉,zk进行剔除并重新分区。 |
/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id] | 分区消息的消费进度offset |
2>KRaft模式:pull模式
把集群中节点进行划分,有controller 和 broker 两种角色。
- controller 角色节点
内部组成一个由KRaft 协议(Kafka基于Raft 协议改造过的协议)构成的逻辑集群,统筹管理集群里面的状态信息。
一般为奇数台。 - broker角色节点
从controller 节点同步拉取集群状态信息到本地进行重放,从而完成整个集群的统一管理。
可以从1-N 扩展。 - offset
消费者维护,表示已经消费的消息序号,当消费者拉取消息时,kafka会返回未消费的消息。避免发送大量无效或重复的消息,减少资源的浪费。
新版本数据的存储:
数据 | 存储位置 |
---|---|
offset | 存放在一个专门的topic中的partition里 |
3>Kafka 不依赖zk的优点:
- 支持更多的分区、更高的性能
Kafka 集群规模不再受限制,Zookeeper 模式下单集群在百万topic 级别下会遇到性能瓶颈,主要是受限于跟ZK 交互。 - 更快速地切换controller,提高集群重启恢复速度。
以前要从Zookeeper 拉数据,如果partitions 总量很大,这个恢复过程会很长。 - 可以避免Controller缓存的元数据和Zookeeper存储的数据不一致带来的一系列问题。
- 更少的依赖,简化Kafka 部署
无需其他组件即可提供服务
6)rebalance机制(重平衡机制)
使用kafka的消息量很大的情况下,容易出现rebalance。rebalance期间partition的读写阻塞,直至rebalance完成。降低rebalance次数,能提高性能。
1>概念
消费者组下的消费者与topic下的partition重新匹配的过程。
目的:实现消费者的负载均衡和高可用性。
2>场景
- 增加或删除broker
需要重新调整消费者和partition的分配。 - 增加或删除consumer
- consumer消费超时(可能挂了)
对应的partition没法消费,需要重新调整。
==》设置消费超时阈值 - group订阅的topic数目变化。
==》业务低峰期做topic\partition的变化操作。 - gourp订阅的分区数变化。
==》同3
3>触发条件
- 消费者组成员数量发生变化;
- 订阅主题数量发生变化;
- 订阅主题的分区数发生变化。
4>步骤
- 暂停消费
确保不会出现消息丢失或者重复消费。 - 计算分区分配方案
根据consumer group、consumer、topic的partition数量,计算每个consumer应该分配的partition,实现分区负载均衡。 - 通知消费者
一旦分区分配方案确定,发送每个消费者可消费的分区列表,邀请他们加入消费组。 - 重新分配分区
设置broker分区方案:重新分配topic的partition给各个消费者。 - 恢复消费
5>coordinator机制
针对场景1和2,是由coordinator(协调者)进行rebalance的。
coordinator一般是leader节点所在的broker,通过心跳监测consumer是否超时。具体步骤:
- coordinator心跳监测出有异常,心跳通知所有consumer进行rebalance;
- consumer请求coordinator加入消费组,coordinator选举产生leader consumer(不是主从的概念,而是有更多的责任)。
- leader consumer从coordinator获取所有的可用consumer,发送syncGroup(consumer和partition的分配信息)给到coordinator
- coordinator通过心跳机制分发syncGroup给所有的consumer。
针对场景3和4,由于topic和partition的变更broker无法感知,coordinator也就无法感知。由leader consumer监控topic变化,通知coordinator进行rebalance。
rebalance导致的重复消费问题:
consumer1宕掉之后,消费了一半的消息没有提交offset;rebalance之后进行了二次消费。此时consumer1恢复后进行了重复消费。
解决方案:
每次rebalance时标记Generation,rebalance成功后+1,consumer1再次提交offset时发现Generation值不同,则拒绝提交。
3,用法
可以使用spring的kafka,依赖如下:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
原生用法如下。
1)添加依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>1.1.1</version>
</dependency>
2)yml配置
一般不在yml里配置 在java里配置,见3)
spring:
kafka:
properties:
max.request.size: 10097152
zookeeper.connection.timeout.ms: 100000
consumer:
bootstrap-servers: 10.10.10.10:9092
enable-auto-commit: true
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
properties:
max.request.size: 10097152
max.partition.fetch.bytes: 10097152
group-id: demo-id
3)生产者
Kafka中序列化:
Java类型 | 序列化 | 反序列化 |
---|---|---|
int | IntegerSerializer | IntegerDeserializer |
long | LongSerializer | LongDeserializer |
double | DoubleSerializer | DoubleDeserializer |
byte | BytesSerializer | BytesDeserializer |
byte | ByteArraySerializer | ByteArrayDeserializer |
byte | ByteBufferSerializer | ByteBufferDeserializer |
String | StringSerializer | StringDeserializer |
@Configuration
public class KafkaConfig {
//"ip1:9092,ip2:9092,ip3:9092"
@Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
@Bean
public Properties getProperties(){
Properties properties = new Properties();
properties.put("bootstrap.servers", bootstrapServers);
properties.put("acks", "all");
properties.put("retries", 0);
properties.put("batch.size", 16384);
properties.put("lingers.ms", 1);
properties.put("buffer.memory", 33554432);
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
return properties;
}
@Bean
public KafkaProducer<String, String> kafkaProducer(@Autowired @Qualifier("getProperties") Properties kafkaProperties) {
return new KafkaProducer<String, String>(kafkaProperties);
}
}
@Autowired
private Producer<String, String> producer;
producer.send(new ProducerRecord<String, String>("luowenhui", JSONObject.toJSONString(userinfo)));
注意:给kafka里送的消息,一般一条条的发送数据,这样可以轮询分发给不同的consumer,提高性能。对于有关联的数据可以选择批量发送。
4)消费者
@Configuration
public class KafkaConfig {
@Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
public Map<String, Object> kafkaProperties(String consumerGroup) {
Map<String, Object> properties = new HashMap<>();
String consumerClientId = "consumer-data" + "-" + UUID.randomUUID();
properties.put(ConsumerConfig.CLIENT_ID_CONFIG, consumerClientId);
log.info("consumer-client-id: {}", consumerClientId);
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
properties.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup);
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, Boolean.TRUE);
// properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 300);
// properties.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 600 * 1000);
properties.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, 10 * 1024 * 1024);
// properties.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 19 * 1024 * 1024);
properties.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 19 * 1000);
properties.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, 10 * 1024 * 1024);
properties.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, 20 * 1000);
// properties.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, 45 * 1000);
properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 60 * 1000);
if (Boolean.TRUE.equals(Boolean.valueOf(kafkaLdapEnable))) {
properties.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, kafkaLdapSecurityProtocol);
properties.put(SaslConfigs.SASL_MECHANISM, kafkaLdapSaslMechanism);
properties.put(SaslConfigs.SASL_JAAS_CONFIG, kafkaLdapSaslJaasConfig);
}
return properties;
}
@Bean
public KafkaConsumer<String, String> kafkaConsumer(String consumerGroup, List<String> topicList) {
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(kafkaProperties(consumerGroup));
// kafkaConsumer.assign(Arrays.asList(new TopicPartition("luo", 0)));//按区接收
//消费者订阅的topic, 可同时订阅多个
kafkaConsumer.subscribe(topicList);
return kafkaConsumer;
}
}
assign方法和subscribe区别:
Kafka里的消费者组有两个使用的场景:
- 队列模式
同一组的消费者共同消费一个主题的所有消息,而且确保一条消息只被一个消费者处理。一个主题的所有的分区会和一个消费组的所有消费者做关联:每个消费者和一到多个分区做关联,接收它们的消息。反向说,一个分区只会与一个消费者关联,它的消息不会被其它的消费者接收。通过assign实现。 最开始只有一个消费者时,所有的分区都分配给了它。当消息的规模增加时,我们就需要扩展消费者的数量,水平扩展处理能力,一直可以达到每个消费者只关联一个分区。大于分区数的消费者是会处在空闲状态,因为没有分配任何的分区。 - 发布/订阅模式
创建不同的消费者组意味一个主题的消息会发送给所有订阅它的消费者组,然后消费者组依照前面共同协作的场景进行分配。这往往是因为我们有不同的应用需求,比如一批交易数据,资金系统、ERP系统会消费它而风险监控也需要同时消费它。这就实现了数据的透明异步共用。
while (true) {
ConsumerRecords<String, String> records = consumer0.poll(100);
for (ConsumerRecord<String, String> record : records) {//record.value()就是获取到的数据
}
}
5)kafka常用命令
Ctrl+C停止命令;
注意:Kafka 从 2.2 版本开始将 kafka-topic.sh 脚本中的 −−zookeeper 参数标注为 “过时”,推荐使用 −−bootstrap-server 参数。若读者依旧使用的是 2.1 及以下版本,请将下述的 --bootstrap-server 参数及其值手动替换为 --zookeeper zk1:2181,zk2:2181,zk:2181。一定要注意两者参数值所指向的集群地址是不同的。
1>系统操作
## 进入broker配置
[root@node1 kafka_2.11-1.1.1]# vi config/server.properties
## 启动kafka |
[atguigu@hadoop102 kafka]$ bin/kafka-server-start.sh config/server.properties ## 关闭集群
[atguigu@hadoop104 kafka]$ bin/kafka-server-stop.sh stop
2>topic操作
说明 | 命令 | 备注 |
---|---|---|
创建topic | ./kafka-topics.sh --zookeeper node01:2181 --create --topic topic_name --partitions 30 --replication-factor 2 | –replication-factor 定义副本数(副本数不能大于kafka的集群节点数,否则会报错); --partitions 定义分区数 |
删除topic | ./kafka-topics.sh --zookeeper node1:2181 --delete --topic test ./kafka-run-class.sh kafka.admin.DeleteTopicCommand --zookeeper node01:2181 --topic t_cdr | 需要server.properties中设置delete.topic.enable=true否则只是标记删除或者直接重启 |
查看topic列表 | ./kafka-topics.sh --zookeeper hadoop102:2181 --list | _consumer_offsets的topic里面存储着offset,kafka默认分为50个分区,分布再不同的主机上。每存入一次数据,_consumer_offsets+1 |
查看Topic详情 | ./kafka-topics.sh --zookeeper hadoop102:2181 --describe --topic first | –Leader:Leader的主机号 broker-id; --Replicas:副本所在的主机号 0 1 2 代表三个主机; --Isr:选举使用,并且是按数据性差异排序的,与leader差异性最小,排名越靠前,刚开始的leader是随机选取的 |
3>消息发送与消费
kafka-console-consumer.sh 脚本(在kafka安装目录的bin下)是一个简易的消费者控制台。该 shell 脚本的功能通过调用 kafka.tools 包下的 ConsoleConsumer 类,并将提供的命令行参数全部传给该类实现。
## 发送消息:控制台向topic生产数据
bin/kafka-console-producer.sh --broker-list node86:9092 --topic t_cdr
## 消费消息:控制台消费topic的数据 添加--from-beginning参数表示从头到尾的数据,不添加表示最新的数据
bin/kafka-console-consumer.sh --zookeeper node01:2181 --topic t_cdr --from-beginning
## 查看topic消费数据
bin/kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092 --property print.key=true --topic topicName
其他属性:
参数 | 值类型 | 说明 | 有效值 |
---|---|---|---|
–topic | string | 被消费的topic | |
–whitelist | string | 正则表达式,指定要包含以供使用的主题的白名单 | |
–partition | integer | 指定分区;除非指定’–offset’,否则从分区结束(latest)开始消费 | |
–offset | string | 执行消费的起始offset位置;默认值:latest | latest ;earliest ;<offset> |
–consumer-property | string | 将用户定义的属性以key=value的形式传递给使用者 | |
–consumer.config | string | 消费者配置属性文件;请注意,[consumer-property]优先于此配置 | |
–formatter | string | 用于格式化kafka消息以供显示的类的名称;默认值:kafka.tools.DefaultMessageFormatter | kafka.tools.DefaultMessageFormatter;kafka.tools.LoggingMessageFormatter;kafka.tools.NoOpMessageFormatter;kafka.tools.ChecksumMessageFormatter |
–property | string | 初始化消息格式化程序的属性 | `print.timestamp=true |
–from-beginning | 从存在的最早消息开始,而不是从最新消息开始 | ||
–max-messages | integer | 消费的最大数据量,若不指定,则持续消费下去 | |
–timeout-ms | integer | 在指定时间间隔内没有消息可用时退出 | |
–skip-message-on-error | 如果处理消息时出错,请跳过它而不是暂停 | ||
–bootstrap-server | string | 必需(除非使用旧版本的消费者),要连接的服务器 | |
–key-deserializer | string | ||
–value-deserializer | string | ||
–enable-systest-events | 除记录消费的消息外,还记录消费者的生命周期;(用于系统测试) | ||
–isolation-level | string | 设置为read_committed以过滤掉未提交的事务性消息;设置为read_uncommitted以读取所有消息;默认值:read_uncommitted | |
–group | string | 指定消费者所属组的ID | |
–blacklist | string | 要从消费中排除的主题黑名单 | |
–csv-reporter-enabled | 如果设置,将启用csv metrics报告器 | ||
–delete-consumer-offsets | 如果指定,则启动时删除zookeeper中的消费者信息 | ||
–metrics-dir | string | 输出csv度量值;需与[csv-reporter-enable]配合使用 | |
–zookeeper | string | 必需(仅当使用旧的使用者时)连接zookeeper的字符串。可以给出多个URL以允许故障转移 |
4>消费组
说明 | 命令 | 备注 |
---|---|---|
查询用户组 | ./kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --list | |
查看组详情 | ./kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --group MyGrp --describe | 可以查看消费挤压 |
修改偏移量 | ./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group you_consumer_group_name --topic you_topic_name --execute --reset-offsets --to-offset 80 | --to-latest 修改到最近 |
返回数据说明:
字段 | 说明 |
---|---|
HOST | |
CLIENT-ID | |
CURRENT-OFFSET | |
LOG-END-OFFSET | |
LAG | 未消费数量,若与预期不符表示有积压。 |
CONSUMER-ID |
6)kafka/config/server.properties配置文件
#broker的全局唯一编号,不能重复
broker.id=0
#删除topic功能使能
delete.topic.enable=true
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘IO的现成数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接收套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka运行日志存放的路径
log.dirs=/opt/module/kafka/logs
#topic在当前broker上的分区个数
num.partitions=1
#用来恢复和清理data下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment文件保留的最长时间,超时将被删除
log.retention.hours=168
#配置连接Zookeeper集群地址
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:218
属性 | 默认值 | 描述 |
---|---|---|
broker.id | 必填参数,broker的唯一标识 | |
log.dirs | /tmp/kafka-logs | Kafka数据存放的目录。可以指定多个目录,中间用逗号分隔,当新partition被创建的时会被存放到当前存放partition最少的目录。 |
port | 9092 | BrokerServer接受客户端连接的端口号 |
zookeeper.connect | null | Zookeeper的连接串,格式为:hostname1:port1,hostname2:port2,hostname3:port3。可以填一个或多个,为了提高可靠性,建议都填上。注意,此配置允许我们指定一个zookeeper路径来存放此kafka集群的所有数据,为了与其他应用集群区分开,建议在此配置中指定本集群存放目录,格式为:hostname1:port1,hostname2:port2,hostname3:port3/chroot/path 。需要注意的是,消费者的参数要和此参数一致。 |
message.max.bytes | 1000000 | 服务器可以接收到的最大的消息大小。注意此参数要和consumer的maximum.message.size大小一致,否则会因为生产者生产的消息太大导致消费者无法消费。 |
num.io.threads | 8 | 服务器用来执行读写请求的IO线程数,此参数的数量至少要等于服务器上磁盘的数量。 |
queued.max.requests | 500 | I/O线程可以处理请求的队列大小,若实际请求数超过此大小,网络线程将停止接收新的请求。 |
socket.send.buffer.bytes | 100 * 1024 | The SO_SNDBUFF buffer the server prefers for socket connections. |
socket.receive.buffer.bytes | 100 * 1024 | The SO_RCVBUFF buffer the server prefers for socket connections. |
socket.request.max.bytes | 100 * 1024 * 1024 | 服务器允许请求的最大值, 用来防止内存溢出,其值应该小于 Java heap size. |
num.partitions | 1 | 默认partition数量,如果topic在创建时没有指定partition数量,默认使用此值,建议改为5 |
log.segment.bytes | 1024 * 1024 * 1024 | Segment文件的大小,超过此值将会自动新建一个segment,此值可以被topic级别的参数覆盖。 |
log.roll.{ms,hours} | 24 * 7 hours | 新建segment文件的时间,此值可以被topic级别的参数覆盖。 |
log.retention.{ms,minutes,hours} | 7 days | Kafka segment log的保存周期,保存周期超过此时间日志就会被删除。此参数可以被topic级别参数覆盖。数据量大时,建议减小此值。 |
log.retention.bytes | -1 | 每个partition的最大容量,若数据量超过此值,partition数据将会被删除。注意这个参数控制的是每个partition而不是topic。此参数可以被log级别参数覆盖。 |
log.retention.check.interval.ms | 5 minutes | 删除策略的检查周期 |
auto.create.topics.enable | true | 自动创建topic参数,建议此值设置为false,严格控制topic管理,防止生产者错写topic。 |
default.replication.factor | 1 | 默认副本数量,建议改为2。 |
replica.lag.time.max.ms | 10000 | 在此窗口时间内没有收到follower的fetch请求,leader会将其从ISR(in-sync replicas)中移除。 |
replica.lag.max.messages | 4000 | 如果replica节点落后leader节点此值大小的消息数量,leader节点就会将其从ISR中移除。 |
replica.socket.timeout.ms | 30 * 1000 | replica向leader发送请求的超时时间。 |
replica.socket.receive.buffer.bytes | 64 * 1024 | The socket receive buffer for network requests to the leader for replicating data. |
replica.fetch.max.bytes | 1024 * 1024 | The number of byes of messages to attempt to fetch for each partition in the fetch requests the replicas send to the leader. |
replica.fetch.wait.max.ms | 500 | The maximum amount of time to wait time for data to arrive on the leader in the fetch requests sent by the replicas to the leader. |
num.replica.fetchers | 1 | Number of threads used to replicate messages from leaders. Increasing this value can increase the degree of I/O parallelism in the follower broker. |
fetch.purgatory.purge.interval.requests | 1000 | The purge interval (in number of requests) of the fetch request purgatory. |
zookeeper.session.timeout.ms | 6000 | ZooKeeper session 超时时间。如果在此时间内server没有向zookeeper发送心跳,zookeeper就会认为此节点已挂掉。 此值太低导致节点容易被标记死亡;若太高,.会导致太迟发现节点死亡。 |
zookeeper.connection.timeout.ms | 6000 | 客户端连接zookeeper的超时时间。 |
zookeeper.sync.time.ms | 2000 | H ZK follower落后 ZK leader的时间。 |
controlled.shutdown.enable | true | 允许broker shutdown。如果启用,broker在关闭自己之前会把它上面的所有leaders转移到其它brokers上,建议启用,增加集群稳定性。 |
auto.leader.rebalance.enable | true | If this is enabled the controller will automatically try to balance leadership for partitions among the brokers by periodically returning leadership to the “preferred” replica for each partition if it is available. |
leader.imbalance.per.broker.percentage | 10 | The percentage of leader imbalance allowed per broker. The controller will rebalance leadership if this ratio goes above the configured value per broker. |
leader.imbalance.check.interval.seconds | 300 | The frequency with which to check for leader imbalance. |
offset.metadata.max.bytes | 4096 | The maximum amount of metadata to allow clients to save with their offsets. |
connections.max.idle.ms | 600000 | Idle connections timeout: the server socket processor threads close the connections that idle more than this. |
num.recovery.threads.per.data.dir | 1 | The number of threads per data directory to be used for log recovery at startup and flushing at shutdown. |
unclean.leader.election.enable | true | Indicates whether to enable replicas not in the ISR set to be elected as leader as a last resort, even though doing so may result in data loss. |
delete.topic.enable | false | 启用deletetopic参数,建议设置为true。 |
offsets.topic.num.partitions | 50 | The number of partitions for the offset commit topic. Since changing this after deployment is currently unsupported, we recommend using a higher setting for production (e.g., 100-200). |
offsets.topic.retention.minutes | 1440 | Offsets that are older than this age will be marked for deletion. The actual purge will occur when the log cleaner compacts the offsets topic. |
offsets.retention.check.interval.ms | 600000 | The frequency at which the offset manager checks for stale offsets. |
offsets.topic.replication.factor | 3 | The replication factor for the offset commit topic. A higher setting (e.g., three or four) is recommended in order to ensure higher availability. If the offsets topic is created when fewer brokers than the replication factor then the offsets topic will be created with fewer replicas. |
offsets.topic.segment.bytes | 104857600 | Segment size for the offsets topic. Since it uses a compacted topic, this should be kept relatively low in order to facilitate faster log compaction and loads. |
offsets.load.buffer.size | 5242880 | An offset load occurs when a broker becomes the offset manager for a set of consumer groups (i.e., when it becomes a leader for an offsets topic partition). This setting corresponds to the batch size (in bytes) to use when reading from the offsets segments when loading offsets into the offset manager’s cache. |
offsets.commit.required.acks | -1 | The number of acknowledgements that are required before the offset commit can be accepted. This is similar to the producer’s acknowledgement setting. In general, the default should not be overridden. |
offsets.commit.timeout.ms | 5000 | The offset commit will be delayed until this timeout or the required number of replicas have received the offset commit. This is similar to the producer request timeout. |