很多资料将offset为5的位置看作HW,而把offset为8的位置看作LEO,是错误的
AR = ISR + OSR
副本处于不同的broker中,当leader副本出现故障时,从follower副本重新选举新的leader副本对外提供服务。kafka通过多副本机制实现故障转移,当kafka集群中某个broker失效仍能保证服务可用。
生产者和消费者只与leader副本进行交互,而follower副本只负责与leader副本进行消息的同步,很多时候follower副本中的消息相对leader副本而言有一定的滞后。
kafka消费端也具备一定的容灾能力。Consumer使用拉(pull)模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线可以根据之前保存的消费位置重新拉取需要的消息进行消费。
生产者就是负责向kafka发送消息的应用程序。一个正常的生产逻辑的步骤为:
构建消息对象ProducerRecord,ProducerRecord类的定义属性如下:
消息以主题为单位进行分类,而这个key可以让消息再进行二次分类,同一个key会被划分到同一个分区。
kafkaProducer是线程安全的,可以在多个线程中共享单个KafkaProducer实例。
创建生产者实例和构建消息之后,就可以发送消息了,发送消息的有3中模式:
同步发送消息只需要通过
Future<RecordMetadata> = producer.send(record).get()
get() 会阻塞等待Kafka的响应,知道消息发送成功,或者消息发生异常,如果发生异常需要捕获异常并交由外层逻辑处理.
参考java Future类的get方法.
https://www.cnblogs.com/cz123/p/7693064.html
异步发送消息:
一般是在send()方法里面指定一个callback的回调函数,使用callback,kafka有回应就会回调,要么发送成功,要么抛出异常.
producer.send(record, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if(exception != null) {
exception.printStrackTrace();
}else {
System.out.println(metadata.topic() + "-" + metadata.offset());
}
}
});
对于同一个分区而言,消息1于消息2之前发送,那么KafkaProducer可以保证对应的callback1在callback2之前调用,也就是回调函数也可以保证分区有序.
KafkaProducer一般会发生两种类型的异常: 可重试异常和不可重试异常. 常见的异常如分区leader副本不可用,这个异常通常发生在leader下线而新的leader正在选举中,重试之后可以重新恢复, 不可重试异常例如: 发送消息太大,kafka不会对该消息进行任何重试,直接抛出异常.
生产者使用的序列化器和消费者使用的反序列化器是需要一一对应的.
生产者需要用序列化器把对象转化成字节数组才能通过网络发送给kafka,而在对侧消费者需要用反序列化器把从Kafka中收到的字节数据转换成相应的对象.kafka提供了StringSerializer,ByteArray,ByteBuffer,Double,Integer等,除此之外可以选择使用Avro,JSON,ProtoBuf,或者也可以使用自定义类型(如对象)
分区器的作用就是为消息分配分区
消息在通过send()方法发往broker的过程中, 有可能经过拦截器,序列化器和分区器的一系列作用之后才能真正发往broker. 拦截器不必要,序列化器是必需的.如果消息指定了partition字段,那么就不需要分区器,如果没有指定partition字段,那么久依赖分区器,根据key这个字段来计算partition的值.
kafka有默认的分区器,如果key不为null,分区器会对key进行哈希,如果key为null, 那么消息将会以轮训的方式发往主题内的各个可用分区.
可以在消息发送前做一些准备工作,按照某个规则过滤不符合要求的消息,修改消息的内容.KafkaProducer不仅可以指定一个拦截器,还可以指定多个拦截器以形成拦截链.
如果生产者客户端需要向很多分区发送消息,可以将buffer.memory参数适当调大以增加整体的吞吐量.
消息在网络上都是以字节的形式传输,在发送之前需要创建一块内存区域来保存对应的消息.kafka生产者客户端中,通过nio.ByteBuffer来实现消息内存的创建和释放
我们创建一条消息会使用如下方式:
ProducerRecord<String, String> record = new ProducerRecord<>(topic, "Hello, Kafka");
kafkaProducer要将此消息追加到指定主题的某个分区所对应的leader副本之前,首先需要知道主题的分区数量,然后经过计算得出目标分区**,需要知道目标分区的leader副本所在的broker节点的地址,端口信息等,这些信息都属于元数据信息.**
元数据是指kafka集群的元数据,这些元数据记录了集群中有哪些主题,这些主题有哪些分区,每个分区的leader副本分配在那个节点,follower副本分配在那些节点上,那些副本在AR,ISR等集合中.
acks 用来指定分区中必须要有多少个副本收到这条消息,之后生产者才会认为这条消息成功写入.涉及到可靠性和吞吐量之间的权衡.
max.request.size 限制生产者客户端能发送消息的最大值。
retries 和 retry.backoff.ms 重试次数和重试时间,对于一些临时异常,如网络抖动,leader副本选举,以免生产者过早的放弃重试。
compression.type 指定压缩方式,默认为null,可以配置gzip,snappy,lz4
receive.buffer.bytes 设置socket接受消息缓存区的大小,默认32k,设置为-1,使用操作系统默认值,如果producer和kafka处于不同的机房,可以适当调大这个参数值。