概述

是什么

基于zookeeper协调的分布式日志系统(也可以当做MQ系统)

特点

  1. 快速持久化:可以在O(1)的系统开销下进行消息持久化;
  2. 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
  3. 完全的分布式系统:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡;
  4. 支持同步和异步复制两种高可用机制;
  5. 支持数据批量发送和拉取;
  6. 零拷贝技术(zero-copy):减少IO操作步骤,提高系统吞吐量;
  7. 数据迁移、扩容对用户透明;
  8. 无需停机即可扩展机器;
  9. 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制;

优点

  1. 客户端语言丰富:支持Java、.Net、PHP、Ruby、Python、Go等多种语言;
  2. 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
  3. 提供完全分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积;
  4. 支持批量操作;
  5. 消费者采用Pull方式获取消息。消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
  6. 有优秀的第三方KafkaWeb管理界面Kafka-Manager;
  7. 在日志领域比较成熟,被多家公司和多个开源项目使用。

缺点

  1. Kafka单机超过64个队列/分区时,Load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
  2. 使用短轮询方式,实时性取决于轮询间隔时间;
  3. 消费失败不支持重试;
  4. 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;
  5. 社区更新较慢。

性能分析

spring 事件驱动 kafka spring-kafka_apache

为什么用它

  1. 解耦
  2. 异步
  3. 削锋

使用场景

日志收集系统和消息系统。

kafka架构图

spring 事件驱动 kafka spring-kafka_kafka_02

基本名词概念

clusters

集群,由多个broker组成

broker

集群下的服务器,一个服务器就是一个broker。
broker包含多个topic的partition(分区)数据

topic

主题,类别,类似于rabbitmq上的交换机
topic包含多partition,多个replica

partition

一个topic会被拆分成多个分区,分布在不同的broker上,
partition中的数据是有序的,对数据顺序有要求的,建议分区数设置为1
多个partition可以增加topic的吞吐率
partition的数量尽量小于broker的数量

replica

副本,用于数据备份,异常恢复切换的,如果replica设置的是2,一个patition,就有一个leader(用于数据读写)跟一个follower(用于备份恢复切换)

producer

生产者,生产者发送数据到topic的分区中,生产者可以指定发送分区,也可以使用默认的轮训写入分区,保证数据的均匀分布

Consumer Group

消费组,包含多个消费者,用于topic的广播跟单播,每个topic下的patition只会分配给一个消费组下的一个consumer
广播:建立不同CG消费
单播:使用一个CG消费

Consumer

消费者,隶属于CG消费组下面,一个消费组下面的Consumer数量建议跟topic的分区数一个,或者是小于分区数,不可大于分区数,因为每个topic下的patition只会分配给一个消费组下的一个consumer,如果消费组下的consumer多余分区数,多出来的消费者分配不到分区,属于资源浪费

Leader

针对分区的副本的,一个分区的多个副本中,仅有一个是Leader,负责读写,其他都属于Follower

Follower

分区副本中除Leader外的其他副本,用于Leader失效后选举为新的Leader提供读写

Offset

- 分区中消息的位置信息
- 每个分区的消息都是顺序的
- 消费者管理自己的offset,并主动提交给broker服务器记录并保存下来,存放地址:__consumer_offsets topic下,CG,topic,partition作为key存放

message

消息体,一般都会持久化到硬盘,消费后不会删除,不会出现像mq那样的消费后消息丢失的情况,但是会定期删除,默认是7天删除

zookeeper

  • 一个分布式的,开放源码的分布式应用程序协调服务
  • 干的事情:

- 配置管理,名字服务,提供分布式同步以及集群管理。
- 用于管理kafka集群

coordinator

- 用于管理消费组
- 记录消费组信息

reblance

分区跟消费组的分配

触发重新reblance

  1. 有新的consumer加入
  2. 旧的consumer挂了
  3. 消费处理时间超过了默认的5分钟
  4. coordinator挂了,集群选举出新的coordinator
  5. topic的partition新加
  6. consumer调用unsubscrible(),取消topic的订阅

kafka参数

生产者

acks = 1
	batch.size = 16384
	bootstrap.servers = [127.0.0.1:9092]
	buffer.memory = 33554432
	client.id = 
	compression.type = none
	connections.max.idle.ms = 540000
	enable.idempotence = false
	interceptor.classes = []
	key.serializer = class org.apache.kafka.common.serialization.StringSerializer
	linger.ms = 0
	max.block.ms = 60000
	max.in.flight.requests.per.connection = 5
	max.request.size = 1048576
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	partitioner.class = class org.apache.kafka.clients.producer.internals.DefaultPartitioner
	receive.buffer.bytes = 32768
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	request.timeout.ms = 30000
	retries = 0
	retry.backoff.ms = 100
	sasl.client.callback.handler.class = null
	sasl.jaas.config = null
	sasl.kerberos.kinit.cmd = /usr/bin/kinit
	sasl.kerberos.min.time.before.relogin = 60000
	sasl.kerberos.service.name = null
	sasl.kerberos.ticket.renew.jitter = 0.05
	sasl.kerberos.ticket.renew.window.factor = 0.8
	sasl.login.callback.handler.class = null
	sasl.login.class = null
	sasl.login.refresh.buffer.seconds = 300
	sasl.login.refresh.min.period.seconds = 60
	sasl.login.refresh.window.factor = 0.8
	sasl.login.refresh.window.jitter = 0.05
	sasl.mechanism = GSSAPI
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	ssl.cipher.suites = null
	ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
	ssl.endpoint.identification.algorithm = https
	ssl.key.password = null
	ssl.keymanager.algorithm = SunX509
	ssl.keystore.location = null
	ssl.keystore.password = null
	ssl.keystore.type = JKS
	ssl.protocol = TLS
	ssl.provider = null
	ssl.secure.random.implementation = null
	ssl.trustmanager.algorithm = PKIX
	ssl.truststore.location = null
	ssl.truststore.password = null
	ssl.truststore.type = JKS
	transaction.timeout.ms = 60000
	transactional.id = null
	value.serializer = class org.apache.kafka.common.serialization.StringSerializer

消费者

auto.commit.interval.ms = 5000
	auto.offset.reset = latest
	bootstrap.servers = [127.0.0.1:9092]
	check.crcs = true
	client.id = 
	connections.max.idle.ms = 540000
	default.api.timeout.ms = 60000
	enable.auto.commit = true
	exclude.internal.topics = true
	fetch.max.bytes = 52428800
	fetch.max.wait.ms = 500
	fetch.min.bytes = 1
	group.id = test
	heartbeat.interval.ms = 3000
	interceptor.classes = []
	internal.leave.group.on.close = true
	isolation.level = read_uncommitted
	key.deserializer = class org.apache.kafka.common.serialization.StringDeserializer
	max.partition.fetch.bytes = 1048576
	max.poll.interval.ms = 300000
	max.poll.records = 500
	metadata.max.age.ms = 300000
	metric.reporters = []
	metrics.num.samples = 2
	metrics.recording.level = INFO
	metrics.sample.window.ms = 30000
	partition.assignment.strategy = [class org.apache.kafka.clients.consumer.RangeAssignor]
	receive.buffer.bytes = 65536
	reconnect.backoff.max.ms = 1000
	reconnect.backoff.ms = 50
	request.timeout.ms = 30000
	retry.backoff.ms = 100
	sasl.client.callback.handler.class = null
	sasl.jaas.config = null
	sasl.kerberos.kinit.cmd = /usr/bin/kinit
	sasl.kerberos.min.time.before.relogin = 60000
	sasl.kerberos.service.name = null
	sasl.kerberos.ticket.renew.jitter = 0.05
	sasl.kerberos.ticket.renew.window.factor = 0.8
	sasl.login.callback.handler.class = null
	sasl.login.class = null
	sasl.login.refresh.buffer.seconds = 300
	sasl.login.refresh.min.period.seconds = 60
	sasl.login.refresh.window.factor = 0.8
	sasl.login.refresh.window.jitter = 0.05
	sasl.mechanism = GSSAPI
	security.protocol = PLAINTEXT
	send.buffer.bytes = 131072
	session.timeout.ms = 10000
	ssl.cipher.suites = null
	ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
	ssl.endpoint.identification.algorithm = https
	ssl.key.password = null
	ssl.keymanager.algorithm = SunX509
	ssl.keystore.location = null
	ssl.keystore.password = null
	ssl.keystore.type = JKS
	ssl.protocol = TLS
	ssl.provider = null
	ssl.secure.random.implementation = null
	ssl.trustmanager.algorithm = PKIX
	ssl.truststore.location = null
	ssl.truststore.password = null
	ssl.truststore.type = JKS
	value.deserializer = class org.apache.kafka.common.serialization.StringDeserializer

使用方式

spring-kafka官方文档

注意点

重复消费问题

max.poll.interval.ms 两次poll操作允许的最大时间间隔。单位毫秒。默认值300000(5分钟)。

如果设置的手动提交offset,消费端要控制处理时间在五分钟内,否则无法提交offset,导致重复消费

kafka manager是不支持数据推送的

kafka消费是通过消费者先从coordinator获取offset之后,进行offset偏移,请求kafka集群获取消息的,数据本身存在硬盘上,如果消费失败了又自动提交了offset。

通常想要补偿:是重新推送失败消息到kafka,但是目前无法kafka自带的kafka manager是没有补推功能的

所以大家消费kafka的时候要思考降级方案

消费失败降级处理

  • 手动提交的话,反复失败,卡住其他正常数据
  • 自动提交,失败的数据怎么重新消费,kafka是消费者根据offset进行消费的,coordinator中保存的消费组的offset已经是最新的offset了

阿里云:

消息队列Kafka版是按分区逐条消息顺序向前推进消费的,如果消费端拿到某条消息后执行消费逻辑失败,例如应用服务器出现了脏数据,导致某条消息处理失败,等待人工干预,那么有以下两种处理方式:

1. 失败后一直尝试再次执行消费逻辑。这种方式有可能造成消费线程阻塞在当前消息,无法向前推进,造成消息堆积。

2. 由于消息队列Kafka版没有处理失败消息的设计,实践中通常会打印失败的消息或者存储到某个服务(例如创建一个Topic专门用来放失败的消息),然后定时检查失败消息的情况,分析失败原因,根据情况处理。

提高消费速度

提高消费速度有以下两个办法:

  • 增加Consumer实例个数。
  • 可以在进程内直接增加(需要保证每个实例对应一个线程,否则没有太大意义),也可以部署多个消费实例进程;需要注意的是,实例个数超过分区数量后就不再能提高速度,将会有消费实例不工作。

  • 增加消费线程。
  • 增加Consumer实例本质上也是增加线程的方式来提升速度,因此更加重要的性能提升方式是增加消费线程,最基本的步骤如下:
  1. 定义一个线程池。
  2. Poll数据。
  3. 把数据提交到线程池进行并发处理。
  4. 等并发结果返回成功后,再次poll数据执行。