和同事交流不會kafka怎麼行,API奉上,不是大神也能編

對於kafka真的是又愛又恨,作爲架構和大數據兩個方面的通用者, 在這個數據量稱雄的時代,越來越起到至關重要的作用,在和同事進行交流的時候,kafka在開發的過程中如何使用能起到最大的效果成爲話題之一,那沒有用過kafka的你,又該怎麼整,沒關係,我的粉絲怎麼可以有這種尷尬,這裏我從環境準備開始搭建一套kafka開發的api,舊版本和新版本的代碼聯合使用,看完不說你成爲大神,起碼你在跟同事交流的時候不至於窘迫,但是,一定要自己去實踐一下啊

個人公衆號:Java架構師聯盟,每日更新技術好文

環境準備

1)在eclipse中創建一個java工程

2)在工程的根目錄創建一個lib文件夾

3)解壓kafka安裝包,將安裝包libs目錄下的jar包拷貝到工程的lib目錄下,並build path。

4)啓動zk和kafka集羣,在kafka集羣中打開一個消費者

[root@hadoop102 kafka]$ bin/kafka-console-consumer.sh --zookeeper hadoop102:2181 --topic first

Kafka生產者Java API

創建生產者(過時的API)

package com.root.kafka;

import java.util.Properties;

import kafka.javaapi.producer.Producer;

import kafka.producer.KeyedMessage;

import kafka.producer.ProducerConfig;

public class OldProducer {

    @SuppressWarnings("deprecation")

    public static void main(String[] args) {


        Properties properties = new Properties();

        properties.put("metadata.broker.list", "hadoop102:9092");

        properties.put("request.required.acks", "1");

        properties.put("serializer.class", "kafka.serializer.StringEncoder");


        Producer producer = new Producer(new ProducerConfig(properties));


        KeyedMessage message = new KeyedMessage("first", "hello world");

        producer.send(message );

    }

}

4.2.2 創建生產者(新API**)

package com.root.kafka;

import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.Producer;

import org.apache.kafka.clients.producer.ProducerRecord;

public class NewProducer {

    public static void main(String[] args) {


        Properties props = new Properties();

        // Kafka服務端的主機名和端口號

        props.put("bootstrap.servers", "hadoop103:9092");

        // 等待所有副本節點的應答

        props.put("acks", "all");

        // 消息發送最大嘗試次數

        props.put("retries", 0);

        // 一批消息處理大小

        props.put("batch.size", 16384);

        // 請求延時

        props.put("linger.ms", 1);

        // 發送緩存區內存大小

        props.put("buffer.memory", 33554432);

        // key序列化

        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        // value序列化

        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer producer = new KafkaProducer<>(props);

        for (int i = 0; i < 50; i++) {

            producer.send(new ProducerRecord("first", Integer.toString(i), "hello world-" + i));

        }

        producer.close();

    }

}

創建生產者帶回調函數(新API)

package com.root.kafka;

import java.util.Properties;

import org.apache.kafka.clients.producer.Callback;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.ProducerRecord;

import org.apache.kafka.clients.producer.RecordMetadata;

public class CallBackProducer {

    public static void main(String[] args) {

Properties props = new Properties();

        // Kafka服務端的主機名和端口號

        props.put("bootstrap.servers", "hadoop103:9092");

        // 等待所有副本節點的應答

        props.put("acks", "all");

        // 消息發送最大嘗試次數

        props.put("retries", 0);

        // 一批消息處理大小

        props.put("batch.size", 16384);

        // 增加服務端請求延時

        props.put("linger.ms", 1);

// 發送緩存區內存大小

        props.put("buffer.memory", 33554432);

        // key序列化

        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        // value序列化

        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        KafkaProducer kafkaProducer = new KafkaProducer<>(props);

        for (int i = 0; i < 50; i++) {

            kafkaProducer.send(new ProducerRecord("first", "hello" + i), new Callback() {

                @Override

                public void onCompletion(RecordMetadata metadata, Exception exception) {

                    if (metadata != null) {

                        System.err.println(metadata.partition() + "---" + metadata.offset());

                    }

                }

            });

        }

        kafkaProducer.close();

    }

}

4.2.4 自定義分區生產者

0)需求:將所有數據存儲到topic的第0號分區上

1)定義一個類實現Partitioner接口,重寫裏面的方法(過時API)

package com.root.kafka;

import java.util.Map;

import kafka.producer.Partitioner;

public class CustomPartitioner implements Partitioner {

    public CustomPartitioner() {

        super();

    }

    @Override

    public int partition(Object key, int numPartitions) {

        // 控制分區

        return 0;

    }

}

2)自定義分區(新API)

package com.root.kafka;

import java.util.Map;

import org.apache.kafka.clients.producer.Partitioner;

import org.apache.kafka.common.Cluster;

public class CustomPartitioner implements Partitioner {

    @Override

    public void configure(Map configs) {


    }

    @Override

    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {

       // 控制分區

        return 0;

    }

    @Override

    public void close() {


    }

}

3)在代碼中調用

package com.root.kafka;

import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.Producer;

import org.apache.kafka.clients.producer.ProducerRecord;

public class PartitionerProducer {

    public static void main(String[] args) {


        Properties props = new Properties();

        // Kafka服務端的主機名和端口號

        props.put("bootstrap.servers", "hadoop103:9092");

        // 等待所有副本節點的應答

        props.put("acks", "all");

        // 消息發送最大嘗試次數

        props.put("retries", 0);

        // 一批消息處理大小

        props.put("batch.size", 16384);

        // 增加服務端請求延時

        props.put("linger.ms", 1);

        // 發送緩存區內存大小

        props.put("buffer.memory", 33554432);

        // key序列化

        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        // value序列化

        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        // 自定義分區

        props.put("partitioner.class", "com.root.kafka.CustomPartitioner");

        Producer producer = new KafkaProducer<>(props);

        producer.send(new ProducerRecord("first", "1", "root"));

        producer.close();

    }

}

4)測試

(1)在hadoop102上監控/opt/module/kafka/logs/目錄下first主題3個分區的log日誌動態變化情況

[root@hadoop102 first-0]$ tail -f 00000000000000000000.log

[root@hadoop102 first-1]$ tail -f 00000000000000000000.log

[root@hadoop102 first-2]$ tail -f 00000000000000000000.log

(2)發現數據都存儲到指定的分區了。

Kafka消費者Java API

0)在控制檯創建發送者

[root@hadoop104 kafka]$ bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic first

>hello world

1)創建消費者(過時API)

package com.root.kafka.consume;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.Properties;

import kafka.consumer.Consumer;

import kafka.consumer.ConsumerConfig;

import kafka.consumer.ConsumerIterator;

import kafka.consumer.KafkaStream;

import kafka.javaapi.consumer.ConsumerConnector;

public class CustomConsumer {

    @SuppressWarnings("deprecation")

    public static void main(String[] args) {

        Properties properties = new Properties();


        properties.put("zookeeper.connect", "hadoop102:2181");

        properties.put("group.id", "g1");

        properties.put("zookeeper.session.timeout.ms", "500");

        properties.put("zookeeper.sync.time.ms", "250");

        properties.put("auto.commit.interval.ms", "1000");


        // 創建消費者連接器

        ConsumerConnector consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(properties));


        HashMap topicCount = new HashMap<>();

        topicCount.put("first", 1);


        Map>> consumerMap = consumer.createMessageStreams(topicCount);


        KafkaStream stream = consumerMap.get("first").get(0);


        ConsumerIterator it = stream.iterator();


        while (it.hasNext()) {

            System.out.println(new String(it.next().message()));

        }

    }

}

2)官方提供案例(自動維護消費情況)(新API)

package com.root.kafka.consume;

import java.util.Arrays;

import java.util.Properties;

import org.apache.kafka.clients.consumer.ConsumerRecord;

import org.apache.kafka.clients.consumer.ConsumerRecords;

import org.apache.kafka.clients.consumer.KafkaConsumer;

public class CustomNewConsumer {

public static void main(String[] args) {

Properties props = new Properties();

// 定義kakfa 服務的地址,不需要將所有broker指定上

props.put("bootstrap.servers", "hadoop102:9092");

// 制定consumer group

props.put("group.id", "test");

// 是否自動確認offset

props.put("enable.auto.commit", "true");

// 自動確認offset的時間間隔

props.put("auto.commit.interval.ms", "1000");

// key的序列化類

props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

// value的序列化類

props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

// 定義consumer

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

// 消費者訂閱的topic, 可同時訂閱多個

consumer.subscribe(Arrays.asList("first", "second","third"));

while (true) {

// 讀取數據,讀取超時時間爲100ms

ConsumerRecords<String, String> records = consumer.poll(100);

for (ConsumerRecord<String, String> record : records)

System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());

}

}

}`

Kafka producer攔截器(interceptor)

攔截器原理

Producer攔截器(interceptor)是在Kafka 0.10版本被引入的,主要用於實現clients端的定製化控制邏輯。

對於producer而言,interceptor使得用戶在消息發送前以及producer回調邏輯前有機會對消息做一些定製化需求,比如修改消息等。同時,producer允許用戶指定多個interceptor按序作用於同一條消息從而形成一個攔截鏈(interceptor chain)。Intercetpor的實現接口是org.apache.kafka.clients.producer.ProducerInterceptor,其定義的方法包括:

(1)configure(configs)

獲取配置信息和初始化數據時調用。

(2)onSend(ProducerRecord):

該方法封裝進KafkaProducer.send方法中,即它運行在用戶主線程中。Producer確保在消息被序列化以及計算分區前調用該方法。用戶可以在該方法中對消息做任何操作,但最好保證不要修改消息所屬的topic和分區,否則會影響目標分區的計算

(3)onAcknowledgement(RecordMetadata, Exception):

該方法會在消息被應答或消息發送失敗時調用,並且通常都是在producer回調邏輯觸發之前。onAcknowledgement運行在producer的IO線程中,因此不要在該方法中放入很重的邏輯,否則會拖慢producer的消息發送效率

(4)close:

關閉interceptor,主要用於執行一些資源清理工作

如前所述,interceptor可能被運行在多個線程中,因此在具體實現時用戶需要自行確保線程安全。另外倘若指定了多個interceptor,則producer將按照指定順序調用它們,並僅僅是捕獲每個interceptor可能拋出的異常記錄到錯誤日誌中而非在向上傳遞。這在使用過程中要特別留意。

攔截器案例

1)需求:

實現一個簡單的雙interceptor組成的攔截鏈。第一個interceptor會在消息發送前將時間戳信息加到消息value的最前部;第二個interceptor會在消息發送後更新成功發送消息數或失敗發送消息數。

2)案例實操

(1)增加時間戳攔截器

package com.root.kafka.interceptor;

import java.util.Map;

import org.apache.kafka.clients.producer.ProducerInterceptor;

import org.apache.kafka.clients.producer.ProducerRecord;

import org.apache.kafka.clients.producer.RecordMetadata;

public class TimeInterceptor implements ProducerInterceptor<String, String> {

@Override

public void configure(Map<String, ?> configs) {

}

@Override

public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {

// 創建一個新的record,把時間戳寫入消息體的最前部

return new ProducerRecord(record.topic(), record.partition(), record.timestamp(), record.key(),

System.currentTimeMillis() + "," + record.value().toString());

}

@Override

public void onAcknowledgement(RecordMetadata metadata, Exception exception) {

}

@Override

public void close() {

}

}

(2)統計發送消息成功和發送失敗消息數,並在producer關閉時打印這兩個計數器

package com.root.kafka.interceptor;

import java.util.Map;

import org.apache.kafka.clients.producer.ProducerInterceptor;

import org.apache.kafka.clients.producer.ProducerRecord;

import org.apache.kafka.clients.producer.RecordMetadata;

public class CounterInterceptor implements ProducerInterceptor<String, String>{

    private int errorCounter = 0;

    private int successCounter = 0;

@Override

public void configure(Map<String, ?> configs) {

}

@Override

public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {

return record;

}

@Override

public void onAcknowledgement(RecordMetadata metadata, Exception exception) {

// 統計成功和失敗的次數

        if (exception == null) {

            successCounter++;

        } else {

            errorCounter++;

        }

}

@Override

public void close() {

        // 保存結果

        System.out.println("Successful sent: " + successCounter);

        System.out.println("Failed sent: " + errorCounter);

}

}

(3)producer主程序

package com.root.kafka.interceptor;

import java.util.ArrayList;

import java.util.List;

import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.Producer;

import org.apache.kafka.clients.producer.ProducerConfig;

import org.apache.kafka.clients.producer.ProducerRecord;

public class InterceptorProducer {

public static void main(String[] args) throws Exception {

// 1 設置配置信息

Properties props = new Properties();

props.put("bootstrap.servers", "hadoop102:9092");

props.put("acks", "all");

props.put("retries", 0);

props.put("batch.size", 16384);

props.put("linger.ms", 1);

props.put("buffer.memory", 33554432);

props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");

props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

// 2 構建攔截鏈

List<String> interceptors = new ArrayList<>();

interceptors.add("com.root.kafka.interceptor.TimeInterceptor"); interceptors.add("com.root.kafka.interceptor.CounterInterceptor");

props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);

String topic = "first";

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

// 3 發送消息

for (int i = 0; i < 10; i++) {

    ProducerRecord<String, String> record = new ProducerRecord<>(topic, "message" + i);

    producer.send(record);

}

// 4 一定要關閉producer,這樣纔會調用interceptor的close方法

producer.close();

}

}

3)測試

(1)在kafka上啓動消費者,然後運行客戶端java程序。

[root@hadoop102 kafka]$ bin/kafka-console-consumer.sh --zookeeper hadoop102:2181 --from-beginning --topic first

1501904047034,message0

1501904047225,message1

1501904047230,message2

1501904047234,message3

1501904047236,message4

1501904047240,message5

1501904047243,message6

1501904047246,message7

1501904047249,message8

1501904047252,message9

(2)觀察java平臺控制檯輸出數據如下:

Successful sent: 10

Failed sent: 0

kafka Streams

概述

Kafka Streams

Kafka Streams。Apache Kafka開源項目的一個組成部分。是一個功能強大,易於使用的庫。用於在Kafka上構建高可分佈式、拓展性,容錯的應用程序。

Kafka Streams特點

1)功能強大

高擴展性,彈性,容錯

2)輕量級

無需專門的集羣

一個庫,而不是框架

3)完全集成

100%的Kafka 0.10.0版本兼容

易於集成到現有的應用程序

4)實時性

毫秒級延遲

並非微批處理

窗口允許亂序數據

允許遲到數據

爲什麼要有Kafka Stream

當前已經有非常多的流式處理系統,最知名且應用最多的開源流式處理系統有Spark Streaming和Apache Storm。Apache Storm發展多年,應用廣泛,提供記錄級別的處理能力,當前也支持SQL on Stream。而Spark Streaming基於Apache Spark,可以非常方便與圖計算,SQL處理等集成,功能強大,對於熟悉其它Spark應用開發的用戶而言使用門檻低。另外,目前主流的Hadoop發行版,如Cloudera和Hortonworks,都集成了Apache Storm和Apache Spark,使得部署更容易。

既然Apache Spark與Apache Storm擁有如此多的優勢,那爲何還需要Kafka Stream呢?主要有如下原因。

第一,Spark和Storm都是流式處理框架,而Kafka Stream提供的是一個基於Kafka的流式處理類庫。框架要求開發者按照特定的方式去開發邏輯部分,供框架調用。開發者很難了解框架的具體運行方式,從而使得調試成本高,並且使用受限。而Kafka Stream作爲流式處理類庫,直接提供具體的類給開發者調用,整個應用的運行方式主要由開發者控制,方便使用和調試。

第二,雖然Cloudera與Hortonworks方便了Storm和Spark的部署,但是這些框架的部署仍然相對複雜。而Kafka Stream作爲類庫,可以非常方便的嵌入應用程序中,它對應用的打包和部署基本沒有任何要求。

第三,就流式處理系統而言,基本都支持Kafka作爲數據源。例如Storm具有專門的kafka-spout,而Spark也提供專門的spark-streaming-kafka模塊。事實上,Kafka基本上是主流的流式處理系統的標準數據源。換言之,大部分流式系統中都已部署了Kafka,此時使用Kafka Stream的成本非常低。

第四,使用Storm或Spark Streaming時,需要爲框架本身的進程預留資源,如Storm的supervisor和Spark on YARN的node manager。即使對於應用實例而言,框架本身也會佔用部分資源,如Spark Streaming需要爲shuffle和storage預留內存。但是Kafka作爲類庫不佔用系統資源。

第五,由於Kafka本身提供數據持久化,因此Kafka Stream提供滾動部署和滾動升級以及重新計算的能力。

第六,由於Kafka Consumer Rebalance機制,Kafka Stream可以在線動態調整並行度。

Kafka Stream數據清洗案例

0)需求:

實時處理單詞帶有”>>>”前綴的內容。例如輸入”root>>>ximenqing”,最終處理成“ximenqing”

1)需求分析:

2)案例實操

(1)創建一個工程,並添加jar包

(2)創建主類

package com.root.kafka.stream;

import java.util.Properties;

import org.apache.kafka.streams.KafkaStreams;

import org.apache.kafka.streams.StreamsConfig;

import org.apache.kafka.streams.processor.Processor;

import org.apache.kafka.streams.processor.ProcessorSupplier;

import org.apache.kafka.streams.processor.TopologyBuilder;

public class Application {

public static void main(String[] args) {

// 定義輸入的topic

        String from = "first";

        // 定義輸出的topic

        String to = "second";

        // 設置參數

        Properties settings = new Properties();

        settings.put(StreamsConfig.APPLICATION_ID_CONFIG, "logFilter");

        settings.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");

        StreamsConfig config = new StreamsConfig(settings);

        // 構建拓撲

        TopologyBuilder builder = new TopologyBuilder();

        builder.addSource("SOURCE", from)

              .addProcessor("PROCESS", new ProcessorSupplier<byte[], byte[]>() {

@Override

public Processor<byte[], byte[]> get() {

// 具體分析處理

return new LogProcessor();

}

}, "SOURCE")

                .addSink("SINK", to, "PROCESS");

        // 創建kafka stream

        KafkaStreams streams = new KafkaStreams(builder, config);

        streams.start();

}

}

(3)具體業務處理

package com.root.kafka.stream;

import org.apache.kafka.streams.processor.Processor;

import org.apache.kafka.streams.processor.ProcessorContext;

public class LogProcessor implements Processor<byte[], byte[]> {


    private ProcessorContext context;


    @Override

    public void init(ProcessorContext context) {

        this.context = context;

    }

    @Override

    public void process(byte[] key, byte[] value) {

        String input = new String(value);


        // 如果包含“>>>”則只保留該標記後面的內容

        if (input.contains(">>>")) {

            input = input.split(">>>")[1].trim();

            // 輸出到下一個topic

            context.forward("logProcessor".getBytes(), input.getBytes());

        }else{

            context.forward("logProcessor".getBytes(), input.getBytes());

        }

    }

    @Override

    public void punctuate(long timestamp) {


    }

    @Override

    public void close() {


    }

}

(4)運行程序

(5)在hadoop104上啓動生產者

[root@hadoop104 kafka]$ bin/kafka-console-producer.sh --broker-list hadoop102:9092 --topic first

>hello>>>world

>h>>>root

>hahaha

(6)在hadoop103上啓動消費者

[root@hadoop103 kafka]$ bin/kafka-console-consumer.sh --zookeeper hadoop102:2181 --from-beginning --topic second

world

root

hahaha

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章