本篇主要介绍kafka的分区和副本,因为这两者是有些关联的,所以就放在一起来讲了,后面顺便会给出一些对应的配置以及具体的实现代码,以供参考~

1.kafka分区机制

分区机制是kafka实现高吞吐的秘密武器,但这个武器用得不好的话也容易出问题,今天主要就来介绍分区的机制以及相关的部分配置。

首先,从数据组织形式来说,kafka有三层形式,kafka有多个主题,每个主题有多个分区,每个分区又有多条消息。

而每个分区可以分布到不同的机器上,这样一来,从服务端来说,分区可以实现高伸缩性,以及负载均衡,动态调节的能力。

当然多分区就意味着每条消息都难以按照顺序存储,那么是不是意味着这样的业务场景kafka就无能为力呢?不是的,最简单的做法可以使用单个分区,单个分区,所有消息自然都顺序写入到一个分区中,就跟顺序队列一样了。而复杂些的,还有其他办法,那就是使用按消息键,将需要顺序保存的消息存储的单独的分区,其他消息存储其他分区,这个在下面会介绍

我们可以通过replication-factor指定创建topic时候所创建的分区数。

bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test

比如这里就是创建了1个分区,的主题。值得注意的是,还有一种创建主题的方法,是使用zookeeper参数的,那种是比较旧的创建方法,这里是使用bootstrap参数的。

1.1 分区个数选择

既然分区效果这么好,是不是越多分区越好呢?显而易见并非如此。

分区越多,所需要消耗的资源就越多。甚至如果足够大的时候,还会触发到操作系统的一些参数限制。比如linux中的文件描述符限制,一般在创建线程,创建socket,打开文件的场景下,linux默认的文件描述符参数,只有1024,超过则会报错。

看到这里有读者就会不耐烦了,说这么多有啥用,能不能直接告诉我分区分多少个比较好?很遗憾,暂时没有。

因为每个业务场景都不同,只能结合具体业务来看。假如每秒钟需要从主题写入和读取1GB数据,而消费者1秒钟最多处理50MB的数据,那么这个时候就可以设置20-25个分区,当然还要结合具体的物理资源情况。

而如何无法估算出大概的处理速度和时间,那么就用基准测试来测试吧。创建不同分区的topic,逐步压测测出最终的结果。如果实在是懒得测,那比较无脑的确定分区数的方式就是broker机器数量的2~3倍。

1.2 分区写入策略

所谓分区写入策略,即是生产者将数据写入到kafka主题后,kafka如何将数据分配到不同分区中的策略。

常见的有三种策略,轮询策略,随机策略,和按键保存策略。其中轮询策略是默认的分区策略,而随机策略则是较老版本的分区策略,不过由于其分配的均衡性不如轮询策略,故而后来改成了轮询策略为默认策略。

轮询策略

所谓轮询策略,即按顺序轮流将每条数据分配到每个分区中。

举个例子,假设主题test有三个分区,分别是分区A,分区B和分区C。那么主题对接收到的第一条消息写入A分区,第二条消息写入B分区,第三条消息写入C分区,第四条消息则又写入A分区,依此类推。

轮询策略是默认的策略,故而也是使用最频繁的策略,它能最大限度保证所有消息都平均分配到每一个分区。除非有特殊的业务需求,否则使用这种方式即可。

随机策略

随机策略,也就是每次都随机地将消息分配到每个分区。其实大概就是先得出分区的数量,然后每次获取一个随机数,用该随机数确定消息发送到哪个分区。

在比较早的版本,默认的分区策略就是随机策略,但其实使用随机策略也是为了更好得将消息均衡写入每个分区。但后来发现对这一需求而言,轮询策略的表现更优,所以社区后来的默认策略就是轮询策略了。

按键保存策略

按键保存策略,就是当生产者发送数据的时候,可以指定一个key,计算这个key的hashCode值,按照hashCode的值对不同消息进行存储。

至于要如何实现,那也简单,只要让生产者发送的时候指定key就行。欸刚刚不是说默认的是轮询策略吗?其实啊,kafka默认是实现了两个策略,没指定key的时候就是轮询策略,有的话那激素按键保存策略了。

上面有说到一个场景,那就是要顺序发送消息到kafka。前面提到的方案是让所有数据存储到一个分区中,但其实更好的做法,就是使用这种按键保存策略。

让需要顺序存储的数据都指定相同的键,而不需要顺序存储的数据指定不同的键,这样一来,即实现了顺序存储的需求,又能够享受到kafka多分区的优势,岂不美哉。

1.3 实现自定义分区

说了这么多,那么到底要如何自定义分区呢?

kafka提供了两种让我们自己选择分区的方法,第一种是在发送producer的时候,在ProducerRecord中直接指定,但需要知道具体发送的分区index,所以并不推荐。

第二种则是需要实现Partitioner.class类,并重写类中的partition(String topic, Object key, byte[] keyBytes,Object value, byte[] valueBytes, Cluster cluster) 方法。后面在生成kafka producer客户端的时候直接指定新的分区类就可以了。

package kafkaconf;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom; import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.PartitionInfo; public class MyParatitioner implements Partitioner {
@Override
public void configure(Map<String, ?> configs) {
} @Override
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
//key不能空,如果key为空的会通过轮询的方式 选择分区
if(keyBytes == null || (!(key instanceof String))){
throw new RuntimeException("key is null");
}
//获取分区列表
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic); //以下是上述各种策略的实现,不能共存
//随机策略
return ThreadLocalRandom.current().nextInt(partitions.size()); //按消息键保存策略
return Math.abs(key.hashCode()) % partitions.size(); //自定义分区策略, 比如key为123的消息,选择放入最后一个分区
if(key.toString().equals("123")){
return partitions.size()-1;
}else{
//否则随机
ThreadLocalRandom.current().nextInt(partitions.size());
}
} @Override
public void close() {
}
}

然后需要在生成kafka producer客户端的时候指定该类就行:

    val properties = new Properties()
......
props.put("partitioner.class", "kafkaconf.MyParatitioner"); //主要这个配置指定分区类
......其他配置
val producer = new KafkaProducer[String, String](properties)

2.kafka副本机制

说完了分区,再来说说副本。先说说副本的基本内容,在kafka中,每个主题可以有多个分区,每个分区又可以有多个副本。这多个副本中,只有一个是leader,而其他的都是follower副本。仅有leader副本可以对外提供服务。

多个follower副本通常存放在和leader副本不同的broker中。通过这样的机制实现了高可用,当某台机器挂掉后,其他follower副本也能迅速”转正“,开始对外提供服务。

这里通过问题来整理这部分内容。

kafka的副本都有哪些作用?

在kafka中,实现副本的目的就是冗余备份,且仅仅是冗余备份,所有的读写请求都是由leader副本进行处理的。follower副本仅有一个功能,那就是从leader副本拉取消息,尽量让自己跟leader副本的内容一致。

说说follower副本为什么不对外提供服务?

这个问题本质上是对性能和一致性的取舍。试想一下,如果follower副本也对外提供服务那会怎么样呢?首先,性能是肯定会有所提升的。但同时,会出现一系列问题。类似数据库事务中的幻读,脏读。

比如你现在写入一条数据到kafka主题a,消费者b从主题a消费数据,却发现消费不到,因为消费者b去读取的那个分区副本中,最新消息还没写入。而这个时候,另一个消费者c却可以消费到最新那条数据,因为它消费了leader副本。

看吧,为了提高那么些性能而导致出现数据不一致问题,那显然是不值得的。

leader副本挂掉后,如何选举新副本?

如果你对zookeeper选举机制有所了解,就知道zookeeper每次leader节点挂掉时,都会通过内置id,来选举处理了最新事务的那个follower节点。

从结果上来说,kafka分区副本的选举也是类似的,都是选择最新的那个follower副本,但它是通过一个In-sync(ISR)副本集合实现。

kafka会将与leader副本保持同步的副本放到ISR副本集合中。当然,leader副本是一直存在于ISR副本集合中的,在某些特殊情况下,ISR副本中甚至只有leader一个副本。

当leader挂掉时,kakfa通过zookeeper感知到这一情况,在ISR副本中选取新的副本成为leader,对外提供服务。

但这样还有一个问题,前面提到过,有可能ISR副本集合中,只有leader,当leader副本挂掉后,ISR集合就为空,这时候怎么办呢?这时候如果设置unclean.leader.election.enable参数为true,那么kafka会在非同步,也就是不在ISR副本集合中的副本中,选取出副本成为leader,但这样意味这消息会丢失,这又是可用性和一致性的一个取舍了。

ISR副本集合保存的副本的条件是什么?

上面一直说ISR副本集合中的副本就是和leader副本是同步的,那这个同步的标准又是什么呢?

答案其实跟一个参数有关:replica.lag.time.max.ms。

前面说到follower副本的任务,就是从leader副本拉取消息,如果持续拉取速度慢于leader副本写入速度,慢于时间超过replica.lag.time.max.ms后,它就变成“非同步”副本,就会被踢出ISR副本集合中。但后面如何follower副本的速度慢慢提上来,那就又可能会重新加入ISR副本集合中了。

producer的acks参数

前面说了那么多理论的知识,那么就可以来看看如何在实际应用中使用这些知识。

跟副本关系最大的,那自然就是acks机制,acks决定了生产者如何在性能与数据可靠之间做取舍。

配置acks的代码其实很简单,只需要在新建producer的时候多加一个配置:

    val properties = new Properties()
......
props.put("acks", "0/1/-1"); //配置acks,有三个可选值
......其他配置
val producer = new KafkaProducer[String, String](properties)

acks这个配置可以指定三个值,分别是0,1和-1。我们分别来说三者代表什么:

  • acks为0:这意味着producer发送数据后,不会等待broker确认,直接发送下一条数据,性能最快
  • acks为1:为1意味着producer发送数据后,需要等待leader副本确认接收后,才会发送下一条数据,性能中等
  • acks为-1:这个代表的是all,意味着发送的消息写入所有的ISR集合中的副本(注意不是全部副本)后,才会发送下一条数据,性能最慢,但可靠性最强

还有一点值得一提,kafka有一个配置参数,min.insync.replicas,默认是1(也就是只有leader,实际生产应该调高),该属性规定了最小的ISR数。这意味着当acks为-1(即all)的时候,这个参数规定了必须写入的ISR集中的副本数,如果没达到,那么producer会产生异常。

以上~

详细解析kafka之kafka分区和副本的更多相关文章

  1. kafka之partition分区及副本replica升级

    修改kafka的partition分区 bin/kafka-topics.sh --zookeeper datacollect-2:2181 --alter --partitions 3 --topi ...

  2. (一)kafka修改topic分区的位置

    (一)kafka修改topic分区的位置 环境:kafka_2.10-0.8.2.1 + JDK1.7.0_80 1. 查看分区topic的分区分布 $ le-kafka-topics.sh --de ...

  3. Kafka技术内幕 读书笔记之(六) 存储层——服务端处理读写请求、分区与副本

    如下图中分区到 日 志的虚线表示 : 业务逻辑层的一个分区对应物理存储层的一个日志 . 消息集到数据文件的虚线表示 : 客户端发送的消息集最终会写入日志分段对应的数据文件,存储到Kafka的消息代理节 ...

  4. kafka分区及副本在broker的分配

    kafka分区及副本在broker的分配 部分内容參考自:http://blog.csdn.net/lizhitao/article/details/41778193 以下以一个Kafka集群中4个B ...

  5. kafka 分区和副本以及kafaka 执行流程,以及消息的高可用

    1.Kafka概览 Apache下的项目Kafka(卡夫卡)是一个分布式流处理平台,它的流行是因为卡夫卡系统的设计和操作简单,能充分利用磁盘的顺序读写特性.kafka每秒钟能有百万条消息的吞吐量,因此 ...

  6. 入门大数据---Kafka深入理解分区副本机制

    一.Kafka集群 Kafka 使用 Zookeeper 来维护集群成员 (brokers) 的信息.每个 broker 都有一个唯一标识 broker.id,用于标识自己在集群中的身份,可以在配置文 ...

  7. 二、kafka 中央控制器、主题、分区、副本

    集群和中央控制器 一个独立的Kafka服务器被称为broker.broker用来接收来自生产者的消息,为消息设置偏移量,并把消息保存到磁盘.换句话说,多个kafka实例组成kafka集群,每个实例(s ...

  8. Kafka动态增加Topic的副本

    一.kafka的副本机制 由于Producer和Consumer都只会与Leader角色的分区副本相连,所以kafka需要以集群的组织形式提供主题下的消息高可用.kafka支持主备复制,所以消息具备高 ...

  9. kafka 日常使用和数据副本模型的理解

    kafka 日常使用和数据副本模型的理解 在使用Kafka过程中,有时经常需要查看一些消费者的情况.Kafka健康状况.临时查看.同步一些数据,又由于Kafka只是用来做流式存储,又没有像Mysql或 ...

随机推荐

  1. 《C Prime Plus》第九节笔记

    第九节 函数 9.1 复习函数 函数原型 function prototype 函数调用 function call 函数定义 function definition 形参 实参 典型的ANSI C函 ...

  2. [LC] 1099. Two Sum Less Than K

    Given an array A of integers and integer K, return the maximum S such that there exists i < j wit ...

  3. 年度Java技术盘点,懂这些技术的程序员2019发展大好

    与一年前一样,Java仍然是最流行的编程语言.据TIOBE的数据显示,几十年来,Java比其他语言更常名列榜首,Java因为它拥有可移植性.可扩展性和庞大的用户社区,所以许多知名互联网公司使用Java ...

  4. C++ const成员变量和成员函数(常成员函数)

    在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定.const 可以用来修饰成员变量和成员函数. const成员变量 const 成员变量的用法和普通 const 变量的用法相似,只 ...

  5. [Windows] Windows API 串口通信

    Windows 中通过Windows API 进行串口通信主要有以下步骤: 打开串口 配置串口 读写串口 关闭串口 打开串口 关键API: CreateFile Windows 中进行设备的操作,第一 ...

  6. Python爬虫带用户名密码登录

    # -*- coding: utf-8 -*- """ Created on Wed Jun 6 13:18:58 2018 @author: Lenovo " ...

  7. springboot学习笔记:7.IDEA下5步完成热部署配置

    开发工具IDEA 2017.02   JDK1.8 1.pom.xml中增加: <dependency> <groupId>org.springframework.boot&l ...

  8. css3 - transform, transition 与 translate

    零.序言 css 3 的新特性,很多都停留在听说而非实际使用.transform, transition, translate 这三长得实在太像,刚开始的时候总是迷迷糊糊,分不清它们的功能.而最近新接 ...

  9. GUI-猜字游戏

    # 设计思想先定义输入,输入,然后设计逻辑 # GUI from tkinter # 逻辑层 # 设计GUI(用户界面) from tkinter import * import tkinter.si ...

  10. Ubuntu12.10下安装Python3

    设置Python3.2为默认版本 ubuntu12.10下默认安装了Python2.7和Python3.2.3,并且设置了Python2.7为默认的版本,因此如果讨厌麻烦的话,可以直接将Python3 ...