Kafka producer在设计上要比consumer简单,不涉及复杂的组管理操作,每个producer都是独立进行工作的,与其他producer实例之间没有关联。Producer的主要功能就是向某个topic的某个分区发送消息,所以首先要确认向topic的哪个分区写入消息——即分区器(partitioner)的功能。Kafka producer提供了一个默认的分区器。对于每条待发送的消息而言,如果该消息指定了key,则partitioner会根据key的哈希值来选择目标分区,将具有相同key的所有消息都路由到相同的分区中;若该消息未指定key,则partitioner使用轮询的方式确认目标分区。另外producer的API赋予了用户自行指定目标分区的权力,即用户可以在消息发送时跳过partitioner直接指定到要发送到的分区。

另外producer也允许用户实现自定义的分区策略而非使用默认的partitioner。

确认分区后,producer要做的第二件事是寻找该分区对应的leader,也就是该分区leader副本所在的Kafka broker。每个topic分区都由若干个副本组成,其中一个副本充当leader角色,只有leader能响应clients发送的请求,剩下的副本中有一部分副本会与leader副本保持同步,即所谓的ISR(In-Sync Replicas, 副本同步队列)。因此,在发送消息时,producer可以有多种选择来实现消息发送,例如不等待任何副本的响应便返回成功,或者只是等待leader副本响应写入操作后再返回成功。

补充副本概念:

副本(Replication)

同一个partition可能会有多个replication(对应server.properties 配置中的 default.replication.factor=N)。没有replication的情况下,一旦broker 宕机,其上所有 patition 的数据都不可被消费,同时producer也不能再将数据存于其上的patition。引入replication之后,同一个partition可能会有多个replication,而这时需要在这些replication之间选出一个leader,producer和consumer只与这个leader交互,其它replication作为follower从leader 中复制数据。

Producer写入流程

1)producer先从zookeeper的 “/brokers/…/state”节点找到该partition的leader

2)producer将消息发送给该leader

3)leader将消息写入本地log

4)followers从leader pull消息,写入本地log后向leader发送ACK

5)leader收到所有ISR中的replication的ACK后,增加HW(high watermark,最后commit 的offset)并向producer发送ACK

Producer发送消息

在Java版代码中,producer首先使用一个线程(用户主线程,也就是用户启动producer的线程)将待发送的消息封装进一个ProducerRecord类实例,然后将其序列化之后发送给partitioner,再由后者确定了目标分区后一同发送到位于producer程序中的一块内存缓冲池中。而producer的另一个工作线程(I/O发送线程,也称Sender线程)则负责实时地从该缓冲区中提取准备就绪的消息封装进一个批次(batch),统一发送给对应的broker。

Kafka producer发送消息的主方法是send方法,producer在底层完全实现了异步化发送,并且通过Java提供的Future同时实现了同步发送和异步发送+回调(Callback)(默认异步)两种发送方式。最后producer程序结束时需要关闭producer。

acks参数(用来平衡吞吐量与可靠性):

producers可以一步的并行向kafka发送消息,但是通常producer在发送完消息之后会得到一个响应,返回的是offset值(metadata)或者发送过程中遇到的错误(exception)。这其中有个非常重要的参数“request.required.acks”,这个参数决定了producer要求leader partition收到确认的副本个数:

如果acks设置为0,表示producer不会等待broker的响应,所以,producer无法知道消息是否发生成功,这样有可能导致数据丢失,但同时,acks值为0会得到最大的系统吞吐量。一般这种情况下允许消息丢失,如统计服务器日志等;

若acks设置为1,表示producer会在leader partition收到消息时得到broker的一个确认,这样会有更好的可靠性,因为客户端会等待知道broker确认收到消息。此时,当发送消息时,leader broker仅将该消息写入本地日志,然后便发送响应结果给producer,而无需等待ISR中其他副本写入该消息;

若设置为-1或者all,producer会在所有备份的partition收到消息时得到broker的确认,这个设置可以得到最高的可靠性保证。此时,当发送消息时,leader broker不仅会将消息写入本地日志,同时还会等待ISR中所有其他副本都成功写入它们各自的本地日志后,才发送响应结果给producer。

buffer.memory参数:

指定producer端用于缓存消息的缓冲区的大小,单位是字节,默认32M,采用异步发送消息的架构,Java版Producer启动时会首先创建一块内存缓冲区用于保存待发送消息,然后由另一个专属线程负责从缓冲区中读取消息执行真正的发送,这部分内存空间的大小就是由buffer.memory参数指定。该参数指定的内存大小几乎可以认为是producer程序使用的内存大小,若producer程序要给很多分区发送消息,那么就需要仔细设置该参数防止过小的内存缓冲区降低了producer程序整体的吞吐量。

batch.size参数:

batch.size是producer调优吞吐量与延时性能最重要的参数之一,producer会将发往同一分区的多条消息封装进一个batch中,当batch满了或者batch没满(linger.ms参数=0,默认消息立即发送)producer会发送该batch。

Batch小,吞吐量低,延时低,Batch大,吞吐量高,延时高

Batch.size默认为16KB。

request.timeout.ms参数:

当producer发送请求给broker后,broker需要在规定时间范围内将处理结果返回给producer。超时时间默认30s。

自定义分区机制

  1. 在producer程序中创建一个类,实现org.apache.kafka.clients.producer.Partitioner接口,主要分区逻辑在Partitioner.partition中实现。入参(topic, key, keyBytes, value, valueBytes, Cluster);
  2. 在用于构造KafkaProducer的Properties对象中设置partitioner.class参数。

序列化

网络上发送数据都是以字节的形式,kafka也不例外,Apache Kafka支持用户给broker发送各种类型消息,如字符串、整数、数组或任意对象类型,序列化器serializer负责在producer发送前将消息转换成字节数组;解序列化器deserializer则用于将consumer接收到的字节数组转换成相应的对象。

Kafka生产者producer简要总结的更多相关文章

  1. 【转】 详解Kafka生产者Producer配置

    粘贴一下这个配置,与我自己的程序做对比,看看能不能完善我的异步带代码:   -----------------------------------------    详解Kafka生产者Produce ...

  2. kafka 0.8.2 消息生产者 producer

    package com.hashleaf.kafka; import java.util.Properties; import kafka.javaapi.producer.Producer; imp ...

  3. kafka 0.10.2 消息生产者(producer)

    package cn.xiaojf.kafka.producer; import org.apache.kafka.clients.producer.*; import org.apache.kafk ...

  4. kafka集群搭建和使用Java写kafka生产者消费者

    1 kafka集群搭建 1.zookeeper集群  搭建在110, 111,112 2.kafka使用3个节点110, 111,112 修改配置文件config/server.properties ...

  5. Kafka的Producer和Consumer源码学习

    先解释下两个概念: high watermark (HW) 它表示已经被commited的最后一个message offset(所谓commited, 应该是ISR中所有replica都已写入),HW ...

  6. Kafka学习-Producer和Customer

    在上一篇kafka入门的基础之上,本篇主要介绍Kafka的生产者和消费者. Kafka 生产者 kafka Producer发布消息记录到Kakfa集群.生产者是线程安全的,可以在多个线程之间共享生产 ...

  7. Kafka生产者-向Kafka中写入数据

    (1)生产者概览 (1)不同的应用场景对消息有不同的需求,即是否允许消息丢失.重复.延迟以及吞吐量的要求.不同场景对Kafka生产者的API使用和配置会有直接的影响. 例子1:信用卡事务处理系统,不允 ...

  8. Python 使用python-kafka类库开发kafka生产者&消费者&客户端

    使用python-kafka类库开发kafka生产者&消费者&客户端   By: 授客 QQ:1033553122       1.测试环境 python 3.4 zookeeper- ...

  9. Kafka遇到30042ms has passed since batch creation plus linger time at org.apache.kafka.clients.producer.internals.FutureRecordMetadata.valueOrError(FutureRecordMetadata.java:94)

    问题描述: 运行生产者线程的时候显示如下错误信息: Expiring 1 record(s) for XXX-0: 30042 ms has passed since batch creation p ...

随机推荐

  1. PTA (Advanced Level) 1023 Have Fun with Numbers

    Have Fun with Numbers Notice that the number 123456789 is a 9-digit number consisting exactly the nu ...

  2. [转]MongoDB 概念解析

    本文转自:http://www.runoob.com/mongodb/mongodb-databases-documents-collections.html 不管我们学习什么数据库都应该学习其中的基 ...

  3. ado.net中的 sqlconnection sqlcommand datareader dataset SqlDataAdapter之间的关系

    Connection: 和数据库交互,必须连接它.连接帮助指明数据库服务器.数据库名字.用户名.密码,和连接数据库所需要的其它参数.Connection对象会被Command对象使用,这样就能够知道是 ...

  4. c#项目代码风格要求

    代码风格没有正确与否,重要的是整齐划一,这是我拟的一份<.Net 项目代码风格要求>,供大家参考. 1. C# 代码风格要求 1.1注释 类型.属性.事件.方法.方法参数,根据需要添加注释 ...

  5. OC与JS交互之WKWebView

    上一篇文章我们使用了JavaScriptCore框架重写了之前的示例,iOS8苹果偏爱HTML5,重构了UIWebVIew,给我们带来了WKWebView,使其性能.稳定性.功能大幅度提升,也更好的支 ...

  6. 网络安全之——DNS欺骗实验

        ---------------发个帖证明一下存在感,希望各位大牛们,别喷我!!谢谢--------------         DNS(域名系统)的作用是把网络地址(域名,以一个字符串的形式) ...

  7. JDBC入门(4)--- 批处理

    1.Statement批处理 当你有10条SQL语句要执行时,一次向服务器发送一条SQL语句,这样做的效率上极差,处理的方案是使用批处理,即一次向服务发送多条SQL语句,然后由服务器一次性处理. 批处 ...

  8. sql: Oracle simple example table

    --Oracle 9i 实例数据脚本地址:$oracle_home/rdbms/admin/utlsampl.sql CREATE TABLE DEPT (DEPTNO NUMBER(2) CONST ...

  9. CodeChef SADPAIRS:Chef and Sad Pairs

    vjudge 首先显然要建立圆方树 对于每一种点建立虚树,考虑这一种点贡献,对于虚树上已经有的点就直接算 否则对虚树上的一条边 \((u, v)\),\(u\) 为父亲,假设上面连通块大小为 \(x\ ...

  10. System.out.println与System.err.println的区别

    public class Test2 { static { System.out.println("1"); } { System.out.println("2" ...