カフカシリーズ(3) - カフカのプロデューサーは、詳細

メッセージプロデューサを送信する処理

まず、メッセージを送信カフカプロデューサーのプロセスを教えて:

  • カフカはProducerRecordオブジェクトをパッケージ化メッセージを送信する、ProducerRecord対象オブジェクトは、テーマと内容が送信されるように含まれていますが、キーとパーティションを指定することができます。彼らは唯一のネットワークを介して送信することができるようにProducerRecordオブジェクトを送信する前に、プロデューサーは、バイト配列に最初のキー値および標的配列になります。
  • 次に、データがパーティに渡されます。あなたは以前に内部パーティションProducerRecordオブジェクトを指定している場合は、パーティショナは何もしません。ないパーティション、パーティションはその後、キーオブジェクトProducerRecordパーティションに従って選択されません場合、このレコードは、レコードにバッチに添加され、バッチ内のすべてのメッセージは、同じ被写体とパーティションに送られます上。別のスレッドが適切なブローカーに送信されたこれらのバッチ記録を担当してあります。
  • これらのメッセージサーバを受信すると応答を返します。メッセージが正常にカフカに記述されている場合、それがテーマとパーティションの情報が含まれていRecordMetaDataオブジェクトを返し、パーティションのオフセットを記録します。書き込みに失敗した場合は、エラーが返されます。生産者は、直接のスロー例外、再試行しませリトライの指定された数に達した後は成功していない場合は、メッセージを再送信しようとした後にエラーが発生します。

第二に、プロデューサーを作成します

2.1プロジェクトの依存関係

このプロジェクトは、ビルドにMavenを使用して、あなたはカフカの生産のAPIを呼び出したい、あなたはインポートする必要がありkafka-clients、次のように、依存性を:

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.2.0</version>
</dependency>

2.2プロデューサーを作成します

あなたはカフカの生産を作成すると、3つのプロパティを指定する必要があります。

  • bootstrap.servers:指定されたブローカーのアドレスリストは、ブローカーのすべてのアドレスのリストに含まれる必要がない、生産者は、与えられたでブローカーからの情報ブローカーを見つけるでしょう。しかし、提案された情報は、フォールトトレランスなどの少なくとも二つのブローカを提供します。
  • key.serializer:シリアライザ指定されたキー。
  • value.serializer:シリアライザ指定された値。

次のように作成したサンプルコード:

public class SimpleProducer {

    public static void main(String[] args) {

        String topicName = "Hello-Kafka";

        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop001:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        /*创建生产者*/
        Producer<String, String> producer = new KafkaProducer<>(props);

        for (int i = 0; i < 10; i++) {
            ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "hello" + i, 
                                                                         "world" + i);
            /* 发送消息*/
            producer.send(record);
        }
        /*关闭生产者*/
        producer.close();
    }
}

この記事のすべてのサンプルコードはGitHubのからダウンロードすることができます:カフカが住ん-基本とするために使用しました

2.3テスト

1. Kakfa

カフカの実行が飼育係に依存し、事前スタートをする必要がある、あなたはまた、独自のインストールを開始することができ、カフカが飼育係を構築して起動することができます。

# zookeeper启动命令
bin/zkServer.sh start

# 内置zookeeper启动命令
bin/zookeeper-server-start.sh config/zookeeper.properties

テストのためのカフカ単一ノードを起動します。

# bin/kafka-server-start.sh config/server.properties

2.トピックを作成します。

# 创建用于测试主题
bin/kafka-topics.sh --create \
                    --bootstrap-server hadoop001:9092 \
                     --replication-factor 1 --partitions 1 \
                     --topic Hello-Kafka

# 查看所有主题
 bin/kafka-topics.sh --list --bootstrap-server hadoop001:9092

3. [スタート]消費者

書き込み消費者を表示するためのコンソールを起動し、次のようにstartコマンドは次のとおりです。

# bin/kafka-console-consumer.sh --bootstrap-server hadoop001:9092 --topic Hello-Kafka --from-beginning

4.プロジェクトを実行します

この時点で、消費者が、次のようにコンソール出力がある見ることができるkafka-console-consumer唯一の情報の値を出力しますキー情報をプリントアウトされることはありません。

2.4可能性のある問題

ここで発生する可能性のある一つの問題は、次のとおりです。番組制作を開始した後、待機状態にありました。これは通常の必要性、その場合、あなたはカフカを開始するには、デフォルトの設定を使用する場合に発生しserver.propertiesたファイルは、listeners設定を変更します。

# hadoop001 为我启动kafka服务的主机名,你可以换成自己的主机名或者ip地址
listeners=PLAINTEXT://hadoop001:9092

メッセージを送信するために2つ、

上記の例では、呼び出すsendメソッドが、この場合には、メッセージを送信した後に何もしない、我々は送信されたメッセージの結果を知る方法はありません。メッセージを知りたい、あなたは非同期または同期伝送を使用して実装されて送信することができます。

2.1同期伝送

呼び出しsendメソッドは、呼び出してもよいget()方法を、send戻り値は、送信されたメッセージ・トピック、パーティション、オフセット及びその他の情報が含まれている将来の<RecordMetadata>オブジェクト、RecordMetadataあります。コードは次のように書き換えます:

for (int i = 0; i < 10; i++) {
    try {
        ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "k" + i, "world" + i);
        /*同步发送消息*/
        RecordMetadata metadata = producer.send(record).get();
        System.out.printf("topic=%s, partition=%d, offset=%s \n",
                metadata.topic(), metadata.partition(), metadata.offset());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

作成するので、オフセットと0のパーティションに割り当てられたすべてのレコードに関連したコール数:この時点では、結果の出力は次のようであるHello-Kafka使用して、テーマ--partitions1のパーティションの数を指定するには、それが唯一のパーティションです。

topic=Hello-Kafka, partition=0, offset=40 
topic=Hello-Kafka, partition=0, offset=41 
topic=Hello-Kafka, partition=0, offset=42 
topic=Hello-Kafka, partition=0, offset=43 
topic=Hello-Kafka, partition=0, offset=44 
topic=Hello-Kafka, partition=0, offset=45 
topic=Hello-Kafka, partition=0, offset=46 
topic=Hello-Kafka, partition=0, offset=47 
topic=Hello-Kafka, partition=0, offset=48 
topic=Hello-Kafka, partition=0, offset=49 

2.2非同期伝送

通常我们并不关心发送成功的情况,更多关注的是失败的情况,因此 Kafka 提供了异步发送和回调函数。 代码如下:

for (int i = 0; i < 10; i++) {
    ProducerRecord<String, String> record = new ProducerRecord<>(topicName, "k" + i, "world" + i);
    /*异步发送消息,并监听回调*/
    producer.send(record, new Callback() {
        @Override
        public void onCompletion(RecordMetadata metadata, Exception exception) {
            if (exception != null) {
                System.out.println("进行异常处理");
            } else {
                System.out.printf("topic=%s, partition=%d, offset=%s \n",
                        metadata.topic(), metadata.partition(), metadata.offset());
            }
        }
    });
}

三、自定义分区器

Kafka 有着默认的分区机制:

  • 如果键值为 null, 则使用轮询 (Round Robin) 算法将消息均衡地分布到各个分区上;
  • 如果键值不为 null,那么 Kafka 会使用内置的散列算法对键进行散列,然后分布到各个分区上。

某些情况下,你可能有着自己的分区需求,这时候可以采用自定义分区器实现。这里给出一个自定义分区器的示例:

3.1 自定义分区器

/**
 * 自定义分区器
 */
public class CustomPartitioner implements Partitioner {

    private int passLine;

    @Override
    public void configure(Map<String, ?> configs) {
        /*从生产者配置中获取分数线*/
        passLine = (Integer) configs.get("pass.line");
    }

    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, 
                         byte[] valueBytes, Cluster cluster) {
        /*key 值为分数,当分数大于分数线时候,分配到 1 分区,否则分配到 0 分区*/
        return (Integer) key >= passLine ? 1 : 0;
    }

    @Override
    public void close() {
        System.out.println("分区器关闭");
    }
}

需要在创建生产者时指定分区器,和分区器所需要的配置参数:

public class ProducerWithPartitioner {

    public static void main(String[] args) {

        String topicName = "Kafka-Partitioner-Test";

        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop001:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        /*传递自定义分区器*/
        props.put("partitioner.class", "com.heibaiying.producers.partitioners.CustomPartitioner");
        /*传递分区器所需的参数*/
        props.put("pass.line", 6);

        Producer<Integer, String> producer = new KafkaProducer<>(props);

        for (int i = 0; i <= 10; i++) {
            String score = "score:" + i;
            ProducerRecord<Integer, String> record = new ProducerRecord<>(topicName, i, score);
            /*异步发送消息*/
            producer.send(record, (metadata, exception) ->
                    System.out.printf("%s, partition=%d, \n", score, metadata.partition()));
        }

        producer.close();
    }
}

3.2 测试

需要创建一个至少有两个分区的主题:

 bin/kafka-topics.sh --create \
                    --bootstrap-server hadoop001:9092 \
                     --replication-factor 1 --partitions 2 \
                     --topic Kafka-Partitioner-Test

此时输入如下,可以看到分数大于等于 6 分的都被分到 1 分区,而小于 6 分的都被分到了 0 分区。

score:6, partition=1, 
score:7, partition=1, 
score:8, partition=1, 
score:9, partition=1, 
score:10, partition=1, 
score:0, partition=0, 
score:1, partition=0, 
score:2, partition=0, 
score:3, partition=0, 
score:4, partition=0, 
score:5, partition=0, 
分区器关闭

四、生产者其他属性

上面生产者的创建都仅指定了服务地址,键序列化器、值序列化器,实际上 Kafka 的生产者还有很多可配置属性,如下:

1. acks

acks 参数指定了必须要有多少个分区副本收到消息,生产者才会认为消息写入是成功的:

  • acks=0 : 消息发送出去就认为已经成功了,不会等待任何来自服务器的响应;
  • acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应;
  • acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。

2. buffer.memory

设置生产者内存缓冲区的大小。

3. compression.type

默认情况下,发送的消息不会被压缩。如果想要进行压缩,可以配置此参数,可选值有 snappy,gzip,lz4。

4. retries

发生错误后,消息重发的次数。如果达到设定值,生产者就会放弃重试并返回错误。

5. batch.size

当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。

6. linger.ms

该参数制定了生产者在发送批次之前等待更多消息加入批次的时间。

7. clent.id

客户端 id,服务器用来识别消息的来源。

8. max.in.flight.requests.per.connection

指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量,把它设置为 1 可以保证消息是按照发送的顺序写入服务器,即使发生了重试。

9. timeout.ms, request.timeout.ms & metadata.fetch.timeout.ms

  • timeout.ms 指定了 borker 等待同步副本返回消息的确认时间;
  • request.timeout.ms 指定了生产者在发送数据时等待服务器返回响应的时间;
  • metadata.fetch.timeout.ms 指定了生产者在获取元数据(比如分区首领是谁)时等待服务器返回响应的时间。

10. max.block.ms

指定了在调用 send() 方法或使用 partitionsFor() 方法获取元数据时生产者的阻塞时间。当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方法会阻塞。在阻塞时间达到 max.block.ms 时,生产者会抛出超时异常。

11. max.request.size

该参数用于控制生产者发送的请求大小。它可以指发送的单个消息的最大值,也可以指单个请求里所有消息总的大小。例如,假设这个值为 1000K ,那么可以发送的单个最大消息为 1000K ,或者生产者可以在单个请求里发送一个批次,该批次包含了 1000 个消息,每个消息大小为 1K。

12. receive.buffer.bytes & send.buffer.byte

这两个参数分别指定 TCP socket 接收和发送数据包缓冲区的大小,-1 代表使用操作系统的默认值。

参考资料

  1. Neha Narkhede, Gwen Shapira ,Todd Palino(著) , 薛命灯 (译) . Kafka 权威指南 . 人民邮电出版社 . 2017-12-26

更多大数据系列文章可以参见 GitHub 开源项目大数据入门指南

おすすめ

転載: www.cnblogs.com/heibaiying/p/11375220.html