ネッティーフリーハンドのラインとシンプルなkafkaClientを使用します

ネッティーフリーハンドラインとZkClientを使用します

ネッティーフリーハンドラインとRedisClientを使用します

二日たちボーエン前ネッティーフリーハンドのラインとカフカのクライアントを使用する方法について説明します。クライアントは、いわゆるカフカカフカ生産者と消費者です。

カフカのAPIの設計に関するTucao

0.8生産者と消費者がScalaのを使用することである前に、我々はすべて知っているように、カフカクライアントがバージョンによって再構築され、後に様々な理由のために開発され、それはもう変更されません。バージョン0.9に重いJavaを使用する場合カフカクライアント構成。

クライアントのJavaのバージョンがまだ広く使用され、パフォーマンスの問題の多くを持っていますが、APIカフカクライアント上の私の研究によると、これらの日はありませんが、私はいつも一日、カフカのクライアントがAになってきたことを感じました混沌 - - それはあまりにもあるので、完全ので何それを再構築友人。?:

1. 多版本问题.
   每个api都有好几个版本, 但是每个api使用的版本都不一致. 
   举个例子, 在kafka-client 1.0.0中,broker的版本是2.3.0时:
   METADATA(拉取topic元数据)的api有1个version, 当前使用版本是1.
   PRODUCE(生产消息)的api有6个version, 当前使用版本是6
   FETCH(拉取消息)的api有5个version, 当前使用版本是5

2. 报文的数据结构巨复杂
   等下实现生产消息的报文的时候,你们会看到,这个报文嵌套了6层,即有6个子结构体.
复制代码

それは意図デザインや意味を理解することができなかったので、私の上。もちろん、私は、小さなTucaoカフカのAPIの不十分なレベルかもしれ~~

カフカのメッセージ・フォーマット

カフカは、独自の最適化を続けてきたので、そのメッセージの形式は常に変化しています。

戦闘における<<アパッチカフカ(フー西付き)>>本(カフカ1.0.0に基づいて)、著者紹介はるか、種々によるメッセージフォーマットV0、V1、V2。V0とV1の三種類の合計医療過誤は、長い間、徐々に段階的に廃止されました。今カフカ使用の新バージョンは、メッセージ・フォーマットのV2バージョンです。この論文は、kafka2.3.0に実装され、テストは合格にV2がメッセージをフォーマットし使用しました。

メッセージフォーマットはV2バージョンここに示されているので。

在开始介绍kafka的消息格式之前, 大家还要理解一个概念: 可变长度.
常规的长度字段要么就是使用4个字节,要么就是使用8个字节来表示,
总之这个字段使用的字节数一般都是固定的.
但是在kafka的v2版本的消息里就不一样了.
它参考了Zig-zag的编码方式, 可以使用不同长度的字段来表示不同的数值.

简单来说就是这样:

用 0 来表示 0
用 1 来表示 1
用 2 来表示 -1
用 3 来表示 2
用 4 来表示 -2
.....

这样的好处就是可以用比较少的字节数来表示绝对值比较小的数字, 
不用每个数字都占用4个或8个字节, 从而可以节省很大的空间

复制代码

((書籍>>)胡西と<< Apacheのカフカ戦闘からスクリーンショット)カフカメッセージ形式の後に、次のチャートを見ることができる「可変長」コンセプトのv2のバージョンを学ぼう:

画像
のは、これらのフィールド一つ一つを理解してみましょう

  1. 消息总长度. 顾名思义, 就是这条消息的总长度啦. 用的是Zig-zag编码表示
  2. 属性. 一个字节表示(8位), 其中第三位用来表示压缩方式.高5位保留,没有用到
  由于我这里的实现没有用到压缩,所以这个字段总是0
  3. 时间戳增量.也是用Zig-zag编码. 所谓增量, 是指针对该消息batch的第一条消息的时间戳的增量.
  消息batch接下来会介绍.
  4. 位移增量. 跟时间戳增量含义差不多
  5. key length. 每条kafka消息都可以有key, 这个就表示key的字节数
  6. key. 这个字段就是kafka消息里面的key.
  7. value size. 更key length含义差不多
  8  value. 就是kafka消息的内容
  9. header个数. kafka消息也可以带有header
  10. header. kafka的header
复制代码

第三及び第四のフィールドを参照してください無知な表情のビットである?それは問題で、あなたが理解する読むことを続けることはありません。

メッセージカフカを送信する際、送信aを持っていますが、一緒に複数のメッセージを持って、その後、一緒に送信されません。これは、カフカのメッセージバッチと呼ばれています。

そしてブローカーのカフカバッチに送信されたメッセージ、その後、それはまた、オープンが、消費者にそのままニュースバッチ、またはログファイルに保存されません。

我々は、送信メッセージを達成するために、メッセージのバッチを理解し、消費ので、メッセージが必要です。

下に示すように、メッセージのバッチの形式:

画像

ビット突然ベンを崩壊されていない、突然、非常に多くのフィールドが巻き起こっ。道を持っていなかった、私たちは一つ一つを見てみましょう。

最初の最後の「ニュース」メッセージのフォーマットのV2バージョンは、x、xは最後から二番目のフィールドであってもよく、上述した「メッセージ番号」。

フィールドの残り:

1. 起始位移
   最后面的"消息"中第一条消息的位移offset
2. 长度
   表示接下来的报文的长度, 即"消息batch的总长度" - 8Byte(起始位移字段) - 4Byte(长度字段)
3. 分区leader版本号
   我这里的实现写死为-1
4. 版本号
   就是magic. 我们这里是V2,所以是2
5. CRC
   是指接下来的所有字段的CRC码
6. 属性
   跟上面消息中的属性的含义一致
7. 最大位移增量
   就是最后一条消息的"位置增量"的值
8. 起始时间戳
   就是第一条消息的时间戳
9. 最大时间戳
   最后一条消息的时间戳
10. 后面三个pid epoch, seq三个字段都是跟事务等相关的,我们这里没有用到, 所以都写死为-1   
复制代码

ここで私は、コードの豆で定義された「ニュース」と「ニュースのバッチは、」KafkaMsgRecordV2とKafkaMsgRecordBatchです。あなたは上記のテキストを見て、絵は理解していない場合は、次のように表示するためのコードに従うことができ、またはより深く理解することができる。してくださいgithubのは、アドレスの末尾にテキストを参照してください。

もちろん、あなたがこの期間を理解していれば、の罰金。しかし、あまりにも早く幸せになることはありません。そのため、上記のはkafakは、6層のメッセージ、ネストされたデータ構造を送信し、ここでは2つだけ、言っていること。まだ4階を待っていること我々は理解する必要があります。もちろん、その層4は比較的単純である。理解する最も困難な部分を通過しました

requestHeader和responseHeader

APIカフカ各要求は、ヘッダと要求、及び応答ボディのAPI header.requestHeaderの各々とも応答responseHeaderとしなければならない示す通りです。

画像

画像

ヘッダー応答は比較的簡単ですが、それはcorrelationIdのある、このIDは、実際にサーバがそのまま返され、クライアントからサーバーに送信されます。同じの役割を持つXID飼育係を。

私たちは見てみましょうrequestHeader

APIキーとapiVersion

public enum ApiKeys {
    /**
     * 发送消息
     */
    PRODUCE(0, "Produce", (short) 5),

    /**
     * fetch 消息
     */
    FETCH(1, "Fetch", (short)6),
    /**
     * 拉取元数据
     */
    METADATA(3, "Metadata", (short) 1);

    public final short id;

    public final String name;

    public short apiVersion;

    ApiKeys(int id, String name, short apiVersion) {
        this.id = (short) id;
        this.name = name;
        this.apiVersion = apiVersion;
    }


}
复制代码

コードIDフィールドはAPIKEYは、apiVersionはapiVersionで対応するヘッダである。始めTucaoのような私たちは、各バージョンのAPIは同じではないので。この実現には、私はちょうど3つのAPIを実現しました。しかし、実際にはダースのAPIを提供カフカ。

correlationIdの

IDとXID zkClient効果の関連性は、主に、要求と応答に関連して、同じである。カフカの応答パケットは、このフィールドが含まれます。

clientIdLen和のclientId

我々は指定しない場合は、公式のクライアントでは、それは自動的にclientIdをを生成しますカフカ生産者と消費者、のclientIdを指定する必要があるかどうか。

最後に、clientIdLenここでは2バイトで表現される、こと。2バイトで表されるカフカ文字列の長さは、これは同じではない飼育係いると言及する価値があります。

プロデューサー

プロデューサー・ロジックは、sendメソッドKafkaClientに実装されています。

  public ProduceResponse send(KafkaProduceConfig config, String topic , String key, String val)
复制代码

具体的には以下のように、総要求パケットプロデューサネストされた層6、上述されています。

  1. ProduceRequest继承KafkaRequestHeader, 持有TopicProduceData对象
  2. TopicProduceData 持有PartitionData对象
  3. PartitionData持有Record对象
  4. Record持有KafkaMsgRecordBatch对象
  5. KafkaMsgRecordBatch持有KafkaMsgRecordV2对象
复制代码

あなたはそれを見ることができ、実際には、「ブローカー情報」徐々にパッケージ=>「トピック情報」=>「パーティション情報」=>「記録情報」=>「メッセージバッチ」=>「メッセージ」や​​他のレベルがあります。

フィールド示され、パケットは、学生がシーケンスの開始から直接、コードで興味がある可能性があり、ここでは与えられないであろう、通信プロトコルカフカの生産は、ロジックは、一般的に以下のことが理解されるであろう。


- ProduceRequest.serializable()
- KafkaRequestHeader.serializable()
    - TopicProduceData.serializable()
        - PartitionData.serializable()
            - Record.serializable()
               - KafkaMsgRecordBatch.serializable()
                   - KafkaMsgRecordV2.serializable()
复制代码

最終的ProduceRequestを変換するために、上述した一連のシリアライズは、ブローカに送信カフカByteBuf、にオブジェクト後、メッセージは正常に生成されます。

消費者

プロデューサー・ロジックは、ポーリング方式KafkaClientに実装されています。

public Map<Integer, List<ConsumerRecord>> poll(KafkaConsumerConfig consumerConfig, String topic, int partition, long fetchOffset)
复制代码

生産に関しては、消費者がパケットを要求し、それはまた、パッケージ工程から=>「トピック情報」=>「パーティション情報」の「構成ブローカー」で、比較的簡単です

次のとおりです。

1. FetchRequest 继承KafkaRequestHeader, 持有FetchTopicRequest对象
2. FetchTopicRequest持有FetchTopicPartitionRequest对象

复制代码

しかし、消費者のレスポンスボディは比較的はるかに複雑なボディ生産者の応答を超えています。

上述したように、生産者はブローカー「メッセージバッチ」を送信し、ブローカーは、特定のメッセージにそれを解決するつもりはない。無傷とにログインするためにそれを保存し、消費者が手つかずであります消費ので、このニュースを解決するための作業は、当然、消費者の肩の上に落下します。

詳細については、KafkaClient#parseResp()メソッドを参照してください

コードの実行

そしてZkClientとRedisClient前に、ここでのように、また簡単な経験とデバッグに、kafkaClientTestを達成しました。

いくつかのシナリオのためのテスト:

  1. 生産メッセージkafkaClientTest、カフカの使用消費kafka-console-consumer.shが来ます

生産ニュース:

    private final static String host = "localhost";
    private final static int port = 9092;
    private final static String topic = "testTopic1";
 @Test
    public void testProducer(){
        KafkaClient kafkaClient = new KafkaClient("producer-111", host, port);
        KafkaProduceConfig kafkaConfig = new KafkaProduceConfig();
        // 注意这里设置为0时, broker不会响应任何数据, 但是消息实际上是发送到broker了的
        short ack = -1;
        kafkaConfig.setAck(ack);
        kafkaConfig.setTimeout(30000);
        ProduceResponse response  = kafkaClient.send(kafkaConfig, topic,"testKey","helloWorld1113");
        assert ack == 0 || response != null;
        System.out.println(new Gson().toJson(response));
    }
复制代码

あなたは、コンソールが消費されたメッセージを見ることができます:

lhhMacBook-Air:bin$ sh kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic testTopic1
helloWorld1113
复制代码
  1. kafkaClientTest、消費者のメッセージkafkaClientTestでニュース(ニュースシーン1)の製造:
   private final static String host = "localhost";
    private final static int port = 9092;
    private final static String topic = "testTopic1";
   @Test
    public void testConsumer(){
        // 如果broker上不存在这个topic的话, 直接消费可能会报错, 可以fetch一下metadata, 或先生产消息
        // testMetaData();
        // testProducer();
        KafkaClient kafkaClient = new KafkaClient("consumer-111", host, port);
        KafkaConsumerConfig consumerConfig = new KafkaConsumerConfig();
        consumerConfig.setMaxBytes(Integer.MAX_VALUE);
        consumerConfig.setMaxWaitTime(30000);
        consumerConfig.setMinBytes(1);
        Map<Integer, List<ConsumerRecord>>  response = kafkaClient.poll(consumerConfig, topic, 0, 0L);
        assert response != null && response.size() > 0;
        Set<Map.Entry<Integer, List<ConsumerRecord>>> entrySet =response.entrySet();
        for(Map.Entry<Integer, List<ConsumerRecord>> entry : entrySet){
            Integer partition = entry.getKey();
            System.out.println("partition" + partition + "的数据:");
            for(ConsumerRecord consumerRecord : entry.getValue()){
                System.out.println(new Gson().toJson(consumerRecord));
            }
        }

    }
复制代码

コンソールメッセージは、単に消費者の成功を示す、(前にテストメッセージを含む)の生産をプリントアウト:

partition0的数据:
{"offset":0,"timeStamp":1573896186007,"key":"testKey","val":"helloWorld"}
{"offset":1,"timeStamp":1573896202787,"key":"testKey","val":"helloWorld"}
{"offset":2,"timeStamp":1573896309808,"key":"testKey","val":"helloWorld111"}
{"offset":3,"timeStamp":1573899639313,"key":"testKey","val":"helloWorld1113"}
{"offset":4,"timeStamp":1574011584095,"key":"testKey","val":"helloWorld1113"}
复制代码
  1. kafka-console-producer.sh生産ニュース、kafkaClientTestにおける消費者のニュースを使用します。

生産ニュース:

lhhMacBook-Air:bin$ sh kafka-console-producer.sh --broker-list localhost:9092 --topic testTopic222
>hi
>h
复制代码

消費者ニュース出力、消費者の成功

partition0的数据:
{"offset":0,"timeStamp":1574012251856,"val":"hi"}
{"offset":1,"timeStamp":1574012270368,"val":"h"}
复制代码

ソース

最後に、githubのソースアドレスを添付:

github.com/NorthWard/a...

興味のある学生は、一緒に学び、進歩するために参照することができます。

おすすめ

転載: juejin.im/post/5ddb5605e51d4523551669b3