Kafka知识点

1.kafka是什么?

kafka最初是Linkedin公司,scala开发的一个分布式发布-订阅消息中间件,后来成为Apache项目之一,它的作用是解耦异步并发缓冲
解耦:生产者和消费者不用关心彼此具体的实现,只针对kafka中数据编程即可;
异步:生产者往kafka中生产一条数据,消费者不用立即消费,可以等到需要的时候再进行消费;
并发:一个生产者生产消息,可以被多个订阅Topic的消费者消费;
缓冲:上游有突发流量,kafka可以起到缓冲作用,消费者可以按自己节奏进行消费

2.kafka中的概念有哪些?

producer:一或多个生产者向kafka中生产数据
consumer:一或多个消费者从kafka中拉取数据,消费者不关心谁生产的数据,消费后不删除队列中数据;
broker代理人或者拉皮条的,kafka中一台服务器或者说是一台机器上用于管理磁盘上存储数据的进程(数据太多存在内存放不下,所以会有存在磁盘上),Producerst通过Brokers往指定Topic中写数据,Consumers通过Brokers从指定Topic拉取消息;
consumerGroup用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)。如果需要实现广播,只要每个consumer有一个独立的consumerGroup就可以了。要实现单播只要所有的consumer在同一个consumerGroup,在同一个组内多个消费者线程消费同一个topic数据,组员之间不能重复消费。
Topic:消息的分类存储;
offset:每个partition都由一系列有序的、不可变的message组成,这些消息被连续的追加到partition中。offset表示一个parition已经消费了多少条message。
Lag:表示一个parition有多少条message没有被消费,用于查看Kafka Consumer的消费延迟情况,通过HW(Consumer可见的Offset)-Current_Offset(Consumer消费到的具体Offset)。
base offset:起始位移
Hw:副本中最新一条已提交消息的位移。每个replica都有HW值,但只有leader中的HW才能作为标示信息,就是说当按照参数标准(0,-1,1)消息成功同步给follower replica后)才会更新HW的值,代表消息理论上已经不会丢失,认为“已提交”。
Leo:replica中下一条待写入消息的offset
Partation这是一个逻辑上的概念,落到磁盘上是一个partition的目录。partition的目录中有多个segment。一个Topic对应多个partition,一个partition对应多个segment。一个segment有默认的大小是1G。每个partition可以设置多个副本(replication),会从所有的副本中选取一个leader出来。所有读写操作都是通过leader来进行的。对于集群可以负载均衡;对于消费者可以提高并行度。
Partation Leader:partition的leader才会进行读写操作,folower仅进行复制。
Group Leader一个消费组的leader,负责分配partition,一般第一个加入Consumer Group的Consumer被称为leader,真正的rebalance执行者。

3. kafka中Controller的主要作用?

管理和协调Kafka集群,比如:

  • Topic分区leader选举:比如broker挂了,controller立即为受影响分区选举leader
  • 更新元数据:Topic分区leader选举或副本集合变化等发生后,controller会将元数据广播给所有broker
  • 创建、删除Topic请求:做法几乎都是在Zookeeper的/brokers/topics下创建znode,而Controller监听Path下的变更来执行真正的“创建topic”逻辑
  • 增加Topic分区数:通过kafka-reassign-partitions脚本
  • 扩展集群:Controller自动完成服务发现的工作

脑裂问题:kafka中只有一个Controller负责分区的leader选举、同步broker新增或删除消息等,但由于网络问题,可能同时有两个broker认为自己是controller,这时候就会出现脑裂,其他broker不知道该听从谁的。

如何解决脑裂问题:Controller epoch,当新的controller产生的时候就会在zk中生成一个新的、数值更大的controller epoch标识,并同步给其他的broker进行保存,这样当旧的controller发送指令时,其他broker就会忽略。

4. kafka中zookeeper的主要作用(0.8及以前)?

  • broker注册:每个Broker启动时,都会到zookeeper上进行注册,在/brokers/ids目录下创建临时节点,然后将IP地址和端口信息记录到该节点中,检测broker存活;
  • 维护分区信息与Broker的对应关系:比如临时节点/brokers/topics/login/3->2,表示Broker ID为3的一个broker,提供了2个分区用于对"login"这个Topic的消息存储;
  • 存储消费的状态信息(低版本的 kafka):比如group的配置信息、consumer信息、分区的消费进度Offset,Offset会定时地被记录到Zookeeper上,该消息分区在消费者下次重新消费时,保证能从之前的进度开始进行消费,然而由于Zookeeper自身存在一些问题比如单点故障,高版本的Kafka已经弱化了Zookeeper的这项功能,使用Kafka单独创建一个Topic存储消费信息;
。。。。。。当然zk还有其他一些功能,此处暂时先不深究了
所以,在zookeeper挂掉之后,可以正常的生产和消费消息,因为每个Broker有一个metaDataCache,cache有topic和partition的基本信息,但不能进行topic的创建、分区调整和删除等操作。

5. kafka如何保证数据的完全生产?

通过ack机制:broker对producer发来的数据已确认接收无误,表示数据已经保存到磁盘,request.required.acks有三个取值:
0:producer只管发,不等待broker返回确认消息,数据健壮性比较低,比如服务器故障导致leader挂掉,Producer发送的消息broker就收不到;
1:producer等topic中的partition leader保存成功返回确认后再发下一条,但是如果消息写入leader,但还没复制leader挂掉也会导致消息丢失,Kafka默认配置,吞吐量和可靠性的折中;
-1:producer等待ISR集合中所有副本都保存成功返回确认后再发下一条,健壮性增强,吞吐量降低。

6. kafka如何做到高可靠性?

从Topic的分区副本、producer发送到broker(ACK机制)、leader选举三个方面考虑:

  • 分区副本

      Kafka为了保证消息高可用、不丢失,一般会做3个备份,备份日志文件被称为replica,有leader replica 和follower replica,而follower replica存在的唯一目的就是防止消息丢失,不参与具体业务逻辑的交互。只有leader 才参与服务,follower只是候补,平时只是信息同步。 

  • ACK机制

      上面讲到的ack机制

  • leader选举

       在每个分区的leader都会维护一个ISR列表(下面有讲),在leader挂了的时候,并且unclean.leader.election.enable=false(关闭不完全的leader选举)的情况下,会从ISR列表中选取第一个follower作为新的leader,来保证消息的高可靠性。

综上所述,要保证kafka消息的可靠性,至少需要配置一下参数:

  • topic级别:replication-factor>=3;
  • producer级别:acks=-1;同时发送模式设置producer.type=sync;
  • broker级别:关闭不完全的leader选举,即unclean.leader.election.enable=false;

7. Kafka中的ISR列表(in-sync replica)

      ISR (in-sync replica)是与leader保持同步的replica集合,我们要保证不丢消息,要保证ISR中至少一个备份存活。就是说不仅需要机器正常,还跟上leader的消息进度,当达到一定程度的时候就会认为“非存活”状态。在0.9.0.0之前,Kafka提供replica lag.max.messages来控制follower最多落后leader的消息数量,超过该follower失效的,就被踢出ISR,常见的导致同步跟不上的原因主要是下面几个:

  • 新的副本在进行信息同步的追赶时期
  • 网络IO等原因,某些机器处理速度变慢所导致持续消费落后
  • 进程卡住(比如Full GC、高频次GC)

0.9.0.0 之后采用Kafka 落后于消费进度的时间长度来判断是否踢出ISR,即如果长时间落后于leader就会被踢出,有效地避免了突发流量偶然落后于leader被不合理的踢出ISR的情况,就有效避免了ISR反复移进移出带来的代价。

8. Kafka中如何保证数据不丢和数据不重复的

数据丢失的原因(对于producer端):

同步模式下ack = 0时,broker接收到还没有写入磁盘,当broker故障时有可能丢失数据;ack=-1时,在follower同步成功之前leader故障,数据也可能丢失。

异步模式下通过buffer控制数据的发送,有两个值来进行控制(时间阈值与消息的数量阈值),如果buffer满了数据还没发送出去,如果立即清理的话风险很大。

数据丢失的解决办法:

producer有丢数据的可能,但是可以通过配置保证消息的不丢失:

同步模式下,设置ack=1;异步模式下当缓冲区满时让生产者一直处于阻塞状态。

对于consumer的话,通过offset提交来保证数据的不丢失,kafka自己记录了offset值,下次消费的时候,接着上次的offset消费。

数据重复的原因:

由于是定期提交offset,线程挂掉或者partition断开连接发生rebalance,导致消费后的数据offset没有提交,下次会重复消费

数据重复的解决办法:

可以将Topic Partation的消费位移保存到外部介质(数据库或者redis)中,比如是一个Map,Map的key是Topic+Partition,value是Offset,定期更新Offset,当Consumer线程关闭时记录Offset,下一次启动Consumer,读取上一次的Offset信息,然后使用consumer.seek()方法指定到上次的offset位置

9. Kafka中数据一致性怎么做的?

       所谓一致性指的是不管是老的leader还是新的leader,consumer都能读到一样的数据。
       kafka引入了HW机制,只有High water mark以上的message才能被consumer读取,对于Leader新收到的消息,Consumer不能立刻消费,Leader会等待该消息被所有ISR中的replica同步后才更新HW,此时该消息才能被Consumer消费,这样就保证了如果Leader挂了,这个消息仍然可以从新选举的Leader中获取。

10. customer应该从brokes拉消息还是brokers将消息推到consumer

采用的pull模式,producer将消息推到broker,consumer从broker拉消息,push模式和pull模式各有优劣。push模式的目标是尽可能以最快速度传递消息,很难适应消费速率不同的消费者,这容易造成consumer来不及处理消息,而pull模式则可以根据consumer的消费能力以适当的速率消费消息,缺点是如果 broker没有可供消费的消息,consumer将在不断轮询。

11. kafka怎么体现消息顺序性?

写入时,每个partation中数据写入保证是有序的,消费时,每个partation只能被一个消费者消费,保证了消费是有序的。

12. kafka如何做到全局有序

      比如,有100条有序数据,生产者发送到kafka集群,kafka的partition有4个,可能的情况就是一个partition保存0-25,一个保存25-50......这样消息在kafka中存储是局部有序了,所以说,kafka是无法保证全局消息有序的,只能局部有序。

两种方案:

方案一,kafka topic只设置一个partition; 

方案二,producer基于某个字段作Hash,将消息发送到指定partition

解析:

方案一:kafka默认保证partition内部的消息是有序的,所以设置topic只使用一个partition,这样消息就是全局有序,缺点是一个partition只能被组内一个消费者消费,降低了性能;

方案二:producer可以在发送消息时可以指定需要保证顺序的几条消息发送到同一个partition,这样消费者消费时,消息也是有序的。

producer发送消息时具体到topic的哪一个partition,提供了三种方式:

  • 指定partition
  • 不指定partition,有指定key 则根据key的hash值与partition数进行运算后确定发送到哪个partition
  • 不指定partition,不指定key,则轮询各个partition发送
  • 也可以自定义分区器(没试过)

13. Kafka的分区策略

一个Group中的Consumer如何知道消费哪个分区的数据?
 
目前有三种分区策略:
  • Range策略

首先对同一个Topic里面的Partition按序号排序,消费者也按字母顺序进行排序,然后两者相除来决定每个消费者线程消费几个分区,如果除不尽,前面的几个消费者将会多消费分区。

  • RoundRobin(轮询分配)

首先对同一个Topic里面的Partition按序号排序,消费者也按字母顺序进行排序,按顺序依次把Topic的Partition分配给订阅该Topic的Consumer,缺点是还不够均匀

  • sticky策略(Kafka 0.11x以后的)

为了减少资源消耗和异常情况,本次分配时有两个目标:分区的分配要尽可能的均匀;分区的分配尽可能的与上次分配的保持相同(上次C1消费T1P1,这次尽量也是)

14. Coordinator-重点讨论Group Coordinator

Coordinator分为三种
  • GroupCoordinator:broker端的,每个kafka server都有一个实例,

两大作用:存储Group的元信息(包括Topic列表、Group的配置信息、Consumer信息比如主机名、Partation的消费Offset)和Consumer Rebalance

如何选举:首先, Kafka 内部有一个 topic专门来存储 group 消费的情况: __consumer_offsets topic ,默认有50个 partition,每个 partition 默认有三个副本,而具体一个 group 的消费情况要存储到哪一个 partition 上,是根据 abs(GroupId.hashCode()) % NumPartitions来计算的(其中,NumPartitions 是 __consumer_offsets 的 partition 数,默认是50个),即其 group.id进行hash得到对应的partition值,该partition leader所在Broker即为Group对应的GroupCoordinator。

  • WorkerCoordinator:broker端的,管理GroupCoordinator程序,主要管理workers的分配
  • ConsumerCoordinator:consumer端的,只负责与GroupCoordinator通信

15. rebalance

啥是rebalance:Rebalance是一种分区-consumer分配协议,根据给定的分配策略来为Group下所有Consumer重新分配要订阅的Partition

缺点:在Rebalance期间,Group中所有Consumer都不能消费消息

什么时候做
  • 消费者加入或者退出;
  • Topic的分区数发生变化;
  • 订阅Topic的数量发生变化(比如新建的Topic,消费者通过正则匹配到了)

Rebalance过程

  • 选取Coordinator即协调者。对于每1个消费组,要从集群中选择一个broker作为协调者;
  • 加入组。这一步中,所有成员都向协调者发送JoinGroup(加入消费组)请求,然后coordinator会从中选择一个consumer担任leader,并把组成员信息以及订阅信息发给leader;

  • 同步更新分配方案。leader制定分配方案:根据分配策略决定consumer负责topic的哪些分区,然后把分配方案封装进SyncGroup请求发送给coordinator,coordinator把每个consumer的消费方案返回给每个consumer,这样所有组成员就都知道自己消费哪些分区了

Rebalance Generation

每次group进行rebalance之后,generation号都会加1,表示group进入到了一个新的版本,主要是为了保护consumer group,隔离无效offset提交,比如上一届的consumer是无法提交Offset到新一届的consumer group中,我们有时候可以看到ILLEGAL_GENERATION的错误,就是kafka在抱怨这件事情。

16. kafka Topic的分区数可以增加或者减少么?

可以增加不可以减少,怕丢数据

17. 高并发下消息堆积的处理方式(Lag比较高)

从kafka的角度来说,暂时只想到了两点:
  • 适当地增大分区:提升生产和消费的并行度;
  • 增大线程数:如果有1个消费者线程消费多个分区的情况,建议增加消费者线程,尽量1个消费者线程对应1个分区,从而发挥现有分区数下的最大并行度。

18. consumerGroup中的组员和partition之间如何做负载均衡?

最好是一一对应,因为一个组内的数据与分片数对应,所以最好一个分片(partition)对应一个组中的消费成员(consumer),成员太多,必然会有空闲。

19. Kafka精确回溯到某个时间段的数据

Kafka 0.10以后数据都带有自己的时间戳,所以可以精确回溯

20. Kafka Leader选举过程

      Kafka在Zookeeper上针对每个Topic都维护了一个ISR(in-sync replica---已同步的副本)的集合,集合的增减Kafka都会更新该集合,如果某分区的Leader不可用,Kafka就从ISR集合中选择一个副本作为新的Leader。如果ISR中副本都不可用,有两种处理方法:

  • 等待一个 ISR 的副本重新恢复正常服务,并选择这个副本作为领 leader (它有极大可能拥有全部数据)。
  • 选择第一个重新恢复正常服务的副本(不一定是 ISR 中的)作为leader,默认是这种,不保证已包含所有已commit的消息,不符合强一致性。

21. Kafka的存储机制

       Kafka分不同Topic进行存储的,一Ttopic下有多个不同的partition,每个partition为一个目录,partition命名的规则是Topic的名称加上一个序号,每一个partition目录下的文件被平均切割成大小相等的Segment(默认500M),一个segment对应一对文件:一个log文件和一个index文件相对应,log文件里面存放的就是消息,而index文件记录了元数据信息,包括offset编号、日志长度等信息,第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,开始读取指定分区中某个offset对应的数据时,先根据offset和当前分区的所有segment的名称比较,确定出数据在哪个segment中,再查找该segment的索引文件,确定当前offset在数据文件中的开始位置,最后从该位置开始读取数据文件,获取完整数据。

22. kafka的幂等性

      Kafka在0.11.0.0版本支持增加了对幂等的支持。幂等是针对生产者角度的特性,幂等可以保证上生产者发送的消息,不会丢失,而且不会重复,Producer的幂等性指的是在发送一条消息时,服务端可以区分请求是否重复,过滤掉重复的请求,但是这里的幂等性是有条件的:
  • 只能保证Producer在单个会话内不丟不重,如果Producer意外挂掉再重启是无法保证的;
  • 幂等性不能跨多个Topic-Partition,只能保证单个partition内的幂等性,当涉及多个Topic-Partition时,这中间的状态并没有同步。

系统需要有能力鉴别一条数据到底是不是重复的数据,常用的手段是通过唯一id判断,系统一般需要缓存已经处理的唯一键记录,这样才能更有效率地判断一条数据是不是重复,对于Kafka而言,唯一键是在分区的维度上去做,重复数据的判断让partition的leader判断处理;另外,考虑到一个Partition有多个client写入的情况,client之间很难用同一个唯一键。Kafka Producer在实现时有以下两个重要机制:

  • PID:每个Producer在初始化时都会被分配一个唯一的PID,这个PID对用户是不可见的。这里的 PID 是全局唯一的,Producer 故障后重新启动后会被分配一个新的 PID,这也是幂等性无法做到跨会话的一个原因。
  • sequence numbers:对于每个PID,Producer发送数据的每个<Topic, Partition>给每条消息对应一个从0开始单调递增的Sequence Number,可以使用更少的空间和更快的查询效率,Server也就是通过这个sequence number来验证数据是否重复。

Server端在缓存中保存了这个seq number,对于接收的每条消息,如果其序号比Server缓存中序号大于1则接受它,否则将其丢弃。 此外,generation必须等于server存储的generation或更大,增加generation阻止来自“僵尸”生成者的任何消息。

23. kafka工具

KafkaManager 

24. kafka常用命令来自官网

  • 创建topic
1 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
  • 显示topic
1 bin/kafka-topics.sh --list --zookeeper localhost:2181
  • 开启生产者
1 bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic
  • 开启消费者
1 bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic my-replicated-topic
  • 显示每个分区lag和偏移量信息

1 kafka-consumer-offset-checker --zookeeper 1.1.1.1:2181/aaa --topic xxxx --group group_id

结果显示为:

1 Group           Topic                          Pid Offset          logSize         Lag             Owner
  • 查看单个分区的修改时间、偏移量(另外还有创建时间ctime、修改时间mtime)
1 get /目录/consumers/group_id/offsets/topic名/分区名

设置的话直接用

1 set /目录/consumers/group_id/offsets/topic名/分区名 值

25. kafka的一些特点

  • kafka没有提供额外的索引机制来存储offset,所以在kafka中几乎不允许对消息进行“随机读写”;
  • kafka和JMS(Java Message Service)实现(activeMQ)不同的是:即使消息被消费,消息仍然不会被立即删除。将会根据broker中的配置要求,保留一定的时间之后删除,释放磁盘空间,比如:
1 log.retention.hours=12 #数据最多保存12小时
2 log.retention.bytes=1073741824 #数据最多1G

26. kafka消息传递的语义

  • At most once - 消息传递过程中有可能丢失,丢失的消息也不会重新传递,其实就是保证消息不会重复发送或者重复消费
  • At least once - 消息在传递的过程中不可能会丢失,丢失的消息会重新传递,其实就是保证消息不会丢失,但是消息有可能重复发送或者重新被消费
  • Exactly once - 这个是大多数场景需要的语义,其实就是保证消息不会丢失,也不会重复被消费,消息只传递一次

27. storm整合kafka(参见博客

  • storm消费kafka       
       在创建KafkaSpout对象之前,必须先准备一个SpoutConfig对象。KafkaSpout是kafka消费者storm的spout数据源的一个整合类,目的是让kafka的消费数据直接作为storm的数据源进行数据处理。而SpoutConfig是spout的一个配置类,配置kafka的各种信息,包括节点、端口、主题等等。
1 TopologyBuilder builder = new TopologyBuilder();
2 BoltDeclarer generateDeclarer =
3     builder.setBolt("generate", new CommonGenerateBolt(), generateParallelHint);
4  
5 final SpoutConfig viewSpout = getSpoutConfig(viewTopic, yconfig, ComponentType.VIEW);
6 KafkaSpout viewKafkaSpout = new KafkaSpout(viewSpout);
7 builder.setSpout("view-spout", viewKafkaSpout, viewSpoutParallelHint);
  • storm写入kafka
 1 public class ToKafkaBolt extends BaseRichBolt{
 2     private static final Logger Log = LoggerFactory.getLogger(ToKafkaBolt.class);
 3     
 4     public static final String TOPIC = "topic";
 5     public static final String KAFKA_BROKER_PROPERTIES = "kafka.broker.properties";
 6  
 7     private Producer producer;
 8     private OutputCollector collector;
 9     private TupleToKafkaMapper Mapper;
10     private KafkaTopicSelector topicselector;
11     
12     public ToKafkaBolt withTupleToKafkaMapper(TupleToKafkaMapper mapper){
13         this.Mapper = mapper;
14         return this;
15     }
16     
17     public ToKafkaBolt withTopicSelector(KafkaTopicSelector topicSelector){
18         this.topicselector = topicSelector;
19         return this;
20     }
21     
22     @Override
23     public void prepare(Map stormConf, TopologyContext context,
24             OutputCollector collector) {
25         
26         if (Mapper == null) {
27             this.Mapper = new FieldNameBasedTupleToKafkaMapper();
28         }
29         
30         if (topicselector == null) {
31             this.topicselector = new DefaultTopicSelector((String)stormConf.get(TOPIC));
32         }
33         
34         Map configMap = (Map) stormConf.get(KAFKA_BROKER_PROPERTIES);
35         Properties properties = new Properties();
36         properties.putAll(configMap);
37         ProducerConfig config = new ProducerConfig(properties);
38         producer = new Producer(config);
39         this.collector = collector;
40     }
41  
42     @Override
43     public void execute(Tuple input) {
44 //     String iString = input.getString(0);
45         K key = null;
46         V message = null;
47         String topic = null;
48         try {
49             key = Mapper.getKeyFromTuple(input);
50             message = Mapper.getMessageFromTuple(input);
51             topic = topicselector.getTopic(input);
52             if (topic != null) {
53                 producer.send(new KeyedMessage(topic,message));
54             }else {
55                 Log.warn("skipping key = "+key+ ",topic selector returned null.");
56             }
57         } catch ( Exception e) {
58             // TODO: handle exception
59             Log.error("Could not send message with key = " + key
60                     + " and value = " + message + " to topic = " + topic, e);
61         }finally{
62             collector.ack(input);
63         }
64     }
65     @Override
66     public void declareOutputFields(OutputFieldsDeclarer declarer) {
67     }
68 }

 

 
posted @ 2019-04-20 22:31  akia开凯  阅读(418)  评论(0编辑  收藏  举报