Kafka(二)

第 4 章 Kafka API

4.1 Producer API

4.1.1 消息發送流程

Kafka 的 Producer 發送消息採用的是異步發送的方式。在消息發送的過程中,涉及到了
兩個線程main 線程和 Sender 線程,以及一個線程共享變量RecordAccumulator
main 線程將消息發送給 RecordAccumulator,Sender 線程不斷從 RecordAccumulator 中拉取消息發送到 Kafka broker。
在這裏插入圖片描述
相關參數:
batch.size:只有數據積累到 batch.size 之後,sender 纔會發送數據。
linger.ms:如果數據遲遲未達到 batch.size,sender 等待 linger.time 之後就會發送數據。

4.1.2 異步發送 API

1)導入依賴

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

2)編寫代碼
需要用到的類:

  1. KafkaProducer:需要創建一個生產者對象,用來發送數據
  2. ProducerConfig:獲取所需的一系列配置參數
  3. ProducerRecord:每條數據都要封裝成一個 ProducerRecord 對象
    不帶回調函數的 API
package com.atguigu.kafka;

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

import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class CustomProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");//kafka集羣,broker-list
        props.put("acks", "all");
        props.put("retries", 1);//重試次數
        props.put("batch.size", 16384);//批次大小
        props.put("linger.ms", 1);//等待時間
        props.put("buffer.memory", 33554432);//RecordAccumulator緩衝區大小
        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 < 100; i++) {
            producer.send(new ProducerRecord<String, String>("first", Integer.toString(i), Integer.toString(i)));
        }
        producer.close();
    }
}

2.帶回調函數的API
回調函數會在producer收到ack時調用,爲異步調用,該方法有兩個參數,分別是RecordMetadata和Exception,RecordMetadata爲原數據信息,如果Exception爲null,說明消息發送成功,如果Exception不爲null,說明消息發送失敗。
注意:消息發送失敗會自動重試,不需要我們在回調函數中手動重試。

package com.atguigu.kafka;

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

import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class CustomProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");//kafka集羣,broker-list
        props.put("acks", "all");
        props.put("retries", 1);//重試次數
        props.put("batch.size", 16384);//批次大小
        props.put("linger.ms", 1);//等待時間
        props.put("buffer.memory", 33554432);//RecordAccumulator緩衝區大小
        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);
        //不指定分區和key,數據會輪訓的發給所有分區
        for (int i = 0; i < 10; i++) {
            producer.send(new ProducerRecord<String, String>("first",  "test-"+Integer.toString(i)), new Callback() {

                //回調函數,該方法會在Producer收到ack時調用,爲異步調用
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.out.println("success->" metadata.partition()+ metadata.offset());
                    } else {
                        exception.printStackTrace();
                    }
                }
            });
        }
        //指定分區,數據會全部發給指定分區0
		/*for (int i = 0; i < 10; i++) {
            producer.send(new ProducerRecord<String, String>("first", 0,"test", "test-"+Integer.toString(i)), new Callback() {

                //回調函數,該方法會在Producer收到ack時調用,爲異步調用
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.out.println("success->" metadata.partition()+ metadata.offset());
                    } else {
                        exception.printStackTrace();
                    }
                }
            });
        }*/
		//不指定partition,指定key,通過key的hash值和partition取模%來選擇partition
		/*for (int i = 0; i < 10; i++) {
            producer.send(new ProducerRecord<String, String>("first","test"+Integer.toString(i), "test-"+Integer.toString(i)), new Callback() {

                //回調函數,該方法會在Producer收到ack時調用,爲異步調用
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.out.println("success->" metadata.partition()+ metadata.offset());
                    } else {
                        exception.printStackTrace();
                    }
                }
            });
        }*/
        producer.close();
    }
}

4.1.3 同步發送API
同步發送的意思就是,一條消息發送之後,會阻塞當前線程,直至返回ack。
由於send方法返回的是一個Future對象,根據Futrue對象的特點,我們也可以實現同步發送的效果,只需在調用Future對象的get方發即可

package com.atguigu.kafka;

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

import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class CustomProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");//kafka集羣,broker-list
        props.put("acks", "all");
        props.put("retries", 1);//重試次數
        props.put("batch.size", 16384);//批次大小
        props.put("linger.ms", 1);//等待時間
        props.put("buffer.memory", 33554432);//RecordAccumulator緩衝區大小
        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);
       //2.調用send方法
        for (int i = 0; i < 1000; i++) {
            RecordMetadata meta = producer.send(new ProducerRecord<String, String>("first", i + "", "message-" + i)).get();
            System.out.println("offset = " + meta.offset());
        }
        producer.close();
    }
}

4.2 Consumer API

Consumer消費數據時的可靠性是很容易保證的,因爲數據在Kafka中是持久化的,故不用擔心數據丟失問題。
由於consumer在消費過程中可能會出現斷電宕機等故障,consumer恢復後,需要從故障前的位置的繼續消費,所以consumer需要實時記錄自己消費到了哪個offset,以便故障恢復後繼續消費。
所以offset的維護是Consumer消費數據是必須考慮的問題。

4.2.1 自動提交offset

爲了使我們能夠專注於自己的業務邏輯,Kafka提供了自動提交offset的功能。
自動提交offset的相關參數:
enable.auto.commit:是否開啓自動提交offset功能
auto.commit.interval.ms:自動提交offset的時間間隔
以下爲自動提交offset的代碼:

package com.atguigu.kafka;

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

import java.util.Arrays;
import java.util.Properties;

public class CustomConsumer {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");
        props.put("group.id", "test");
        //如果將該屬性變爲false,假如此時生產者發送10條數據,
        //那麼該消費者每次啓動時,都會重新消費該10條數據,
        //因爲這10條的offset沒有提交,但是啓動後只能拉取一次這10條,因爲消費者啓動的時候會從kafka集羣先獲取該消費者消費的offset,然後消費完在本地維護一個緩存儲存消費的offset,然後提交,避免每次都要從kafka獲取
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        //該屬性只有在兩種情況下會生效,1、此消費者組中的消費者第一次啓動沒有offset 2、此消費者組中的消費者維護的offset已經過期失效了
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("first"));
        while (true) {
            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());
        }
    }
}

4.2.2 手動提交offset

1)導入依賴

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

2)編寫代碼
需要用到的類:
KafkaConsumer:需要創建一個消費者對象,用來消費數據
ConsumerConfig:獲取所需的一系列配置參數
ConsuemrRecord:每條數據都要封裝成一個ConsumerRecord對象

同步提交offset

package com.atguigu.kafka;

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

import java.util.Arrays;
import java.util.Properties;

public class CustomConsumer {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");
        props.put("group.id", "test");//消費者組,只要group.id相同,就屬於同一個消費者組
        props.put("enable.auto.commit", "false");//自動提交offset
       
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("first"));
        while (true) {
            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());
            }
            consumer.commitSync();
        }
    }
}

異步提交offset
雖然同步提交 offset 更可靠一些,但是由於其會阻塞當前線程,直到提交成功。因此吞
吐量會收到很大的影響。因此更多的情況下,會選用異步提交 offset 的方式。
以下爲異步提交 offset 的示例:

package com.atguigu.kafka.consumer;

import org.apache.kafka.clients.consumer.*; import org.apache.kafka.common.TopicPartition;

import java.util.Arrays; import  java.util.Map; import java.util.Properties;

public class CustomConsumer {

	public static void main(String[] args) { 
		Properties props = new Properties();
		//Kafka 集羣
		props.put("bootstrap.servers", "hadoop102:9092");
		// 關 閉 自 動 提 交 offset props.put("enable.auto.commit", "false");
		
		props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
		props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
		
		KafkaConsumer<String,	String>	consumer	=	new KafkaConsumer<>(props);
		consumer.subscribe(Arrays.asList("first"));//消費者訂閱主題
		
		while (true) {
		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());
		}
		
		//異步提交
		consumer.commitAsync(new OffsetCommitCallback() {
			@Override
			public	void	onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
			if (exception != null) { 
				System.err.println("Commit	failed	for"	+offsets);
			}
		}	
	}
}

3)代碼分析:
手動提交offset的方法有兩種:分別是commitSync(同步提交)和commitAsync(異步提交)。兩者的相同點是,都會將本次poll的一批數據最高的偏移量提交;不同點是,commitSync會失敗重試,一直到提交成功(如果由於不可恢復原因導致,也會提交失敗);而commitAsync則沒有失敗重試機制,故有可能提交失敗。異步如果重試可能導致前面的offset覆蓋後面的offset,所以沒有重試,例如:消費者offset=100,提交失敗,這時第二次消費offset=200提交成功,offset=100又開始重試,則會覆蓋之前的200
4)數據重複消費問題
在這裏插入圖片描述

4.2.3 自定義存儲 offset

Kafka 0.9 版本之前,offset 存儲在 zookeeper,0.9 版本及之後,默認將 offset 存儲在 Kafka
的一個內置的 topic 中。除此之外,Kafka 還可以選擇自定義存儲 offset。 offset 的維護是相當繁瑣的,因爲需要考慮到消費者的 Rebalace。
當有新的消費者加入消費者組、已有的消費者推出消費者組或者所訂閱的主題的分區發
生變化,就會觸發到分區的重新分配,重新分配的過程叫做 Rebalance。
消費者發生 Rebalance 之後,每個消費者消費的分區就會發生變化。因此消費者要首先
獲取到自己被重新分配到的分區,並且定位到每個分區最近提交的 offset 位置繼續消費。要實現自定義存儲 offset,需要藉助 ConsumerRebalanceListener,以下爲示例代碼,其
中提交和獲取offset 的方法,需要根據所選的 offset 存儲系統自行實現。

package com.atguigu.kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;

import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;


/**
 * @author liubo
 */
public class CustomOffsetConsumer {

    public static void main(String[] args) {

        Properties props = new Properties();
        props.put("bootstrap.servers", "hadoop102:9092");
        props.put("group.id", "test");//消費者組,只要group.id相同,就屬於同一個消費者組
        props.put("enable.auto.commit", "false");//自動提交offset
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("first"), new ConsumerRebalanceListener() {

            //提交當前負責的分區的offset
            @Override
            public void onPartitionsRevoked(Collection<TopicPartition> partitions) {


            }

            //定位新分配的分區的offset
            @Override
            public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
                for (TopicPartition partition : partitions) {
                    Long offset = getPartitionOffset(partition);
                    consumer.seek(partition,offset);
                }
            }
        });


        while (true) {
            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());
                TopicPartition topicPartition = new TopicPartition(record.topic(), record.partition());
                commitOffset(topicPartition,record.offset()+1);
            }
        }
    }

    private static void commitOffset(TopicPartition topicPartition, long l) {

    }

    private static Long getPartitionOffset(TopicPartition partition) {
        return null;
    }

}

4.3 自定義Interceptor

4.3.1 攔截器原理

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):
該方法會在消息從RecordAccumulator成功發送到Kafka Broker之後,或者在發送過程中失敗時調用。並且通常都是在producer回調邏輯觸發之前。onAcknowledgement運行在producer的IO線程中,因此不要在該方法中放入很重的邏輯,否則會拖慢producer的消息發送效率。
(4)close:
關閉interceptor,主要用於執行一些資源清理工作
如前所述,interceptor可能被運行在多個線程中,因此在具體實現時用戶需要自行確保線程安全。另外倘若指定了多個interceptor,則producer將按照指定順序調用它們,並僅僅是捕獲每個interceptor可能拋出的異常記錄到錯誤日誌中而非在向上傳遞。這在使用過程中要特別留意。

4.3.2 攔截器案例

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

在這裏插入圖片描述
2)案例實操
(1)增加時間戳攔截器

package com.atguigu.kafka.interceptor;

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

import java.util.Map;

/**
 * @author liubo
 */
public class TimeInterceptor implements ProducerInterceptor<String, String> {
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        return new ProducerRecord<String, String>(record.topic(), record.partition(), record.timestamp(), record.key(), System.currentTimeMillis() + record.value(), record.headers());
    }

    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {

    }

    @Override
    public void close() {

    }

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

    }
}

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

package com.atguigu.kafka.interceptor;

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

import java.util.Map;

/**
 * @author liubo
 */
public class CounterInterceptor implements ProducerInterceptor<String, String> {

    private long successNum = 0L;
    private long errorNum = 0L;

    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        return record;
    }

    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
        if (exception == null) {
            successNum++;
        } else {
            errorNum++;
        }
    }

    @Override
    public void close() {
        System.out.println("successNum=" + successNum);
        System.out.println("errorNum=" + errorNum);

    }

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

    }
}

(3)producer主程序

package com.atguigu.kafka.producer;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author liubo
 */
public class CustomProducer {

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

        Properties props = new Properties();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        props.put(ProducerConfig.ACKS_CONFIG, "all");
        props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        ArrayList<String> interceptors = new ArrayList<>();
        interceptors.add("com.atguigu.kafka.interceptor.CounterInterceptor");
        interceptors.add("com.atguigu.kafka.interceptor.TimeInterceptor");
        props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);


        //1.創建1個生產者對象
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);

        //2.調用send方法
        for (int i = 0; i < 1000000; i++) {
            Thread.sleep(10);
            producer.send(new ProducerRecord<String, String>("number", i + "", "message-" + i));
        }

        //3.關閉生產者
        producer.close();
    }
}

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

[atguigu@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --from-beginning --topic first

1501904047034,message0
1501904047225,message1
1501904047230,message2
1501904047234,message3
1501904047236,message4
1501904047240,message5
1501904047243,message6
1501904047246,message7
1501904047249,message8
1501904047252,message9

第 5 章 Kafka 監 控

5.1 Kafka Eagle

1.修改kafka 啓動命令
修改 kafka-server-start.sh 命令中

if [ "x$KAFKA_HEAP_OPTS" = "x" ]; 
then export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi

if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
 export KAFKA_HEAP_OPTS="-server -Xms2G -Xmx2G -XX:PermSize=128m 
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -
XX:ConcGCThreads=5 -XX:InitiatingHeapOccupancyPercent=70"
 export JMX_PORT="9999"
 #export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi

注意:修改之後在啓動 Kafka 之前要分發之其他節點
2.上傳壓縮包kafka-eagle-bin-1.3.7.tar.gz 到集羣/opt/software 目錄
3.解壓到本地

[atguigu@hadoop102 software]$ tar -zxvf kafka-eagle-bin-
1.3.7.tar.gz

4.進入剛纔解壓的目錄

[atguigu@hadoop102 kafka-eagle-bin-1.3.7]$ ll
總用量 82932
-rw-rw-r--. 1 atguigu atguigu 84920710 813 23:00 kafka-eagleweb-1.3.7-bin.tar.gz

5.將 kafka-eagle-web-1.3.7-bin.tar.gz 解壓至/opt/module

[atguigu@hadoop102 kafka-eagle-bin-1.3.7]$ tar -zxvf kafka-eagleweb-1.3.7-bin.tar.gz -C /opt/module/

6.修改名稱

[atguigu@hadoop102 module]$ mv kafka-eagle-web-1.3.7/ eagle

7.給啓動文件執行權限

[atguigu@hadoop102 eagle]$ cd bin/
[atguigu@hadoop102 bin]$ ll
總用量 12
-rw-r--r--. 1 atguigu atguigu 1848 822 2017 ke.bat
-rw-r--r--. 1 atguigu atguigu 7190 730 20:12 ke.sh
[atguigu@hadoop102 bin]$ chmod 777 ke.sh

8.修改配置文件

######################################
# multi zookeeper&kafka cluster list
######################################
kafka.eagle.zk.cluster.alias=cluster1
cluster1.zk.list=hadoop102:2181,hadoop103:2181,hadoop104:2181
######################################
# kafka offset storage
######################################
cluster1.kafka.eagle.offset.storage=kafka
######################################
# enable kafka metrics
######################################
kafka.eagle.metrics.charts=true
kafka.eagle.sql.fix.error=false
######################################
# kafka jdbc driver address
######################################
kafka.eagle.driver=com.mysql.jdbc.Driver
kafka.eagle.url=jdbc:mysql://hadoop102:3306/ke?useUnicode=true&ch
aracterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
kafka.eagle.username=root
kafka.eagle.password=000000

9.添加環境變量

export KE_HOME=/opt/module/eagle
export PATH=$PATH:$KE_HOME/bin

注意:source /etc/profile
10.啓動

[atguigu@hadoop102 eagle]$ bin/ke.sh start
... ...
... ...
*****************************************************************
**
* Kafka Eagle Service has started success.
* Welcome, Now you can visit 'http://192.168.9.102:8048/ke'
* Account:admin ,Password:123456
*****************************************************************
**
* <Usage> ke.sh [start|status|stop|restart|stats] </Usage>
* <Usage> https://www.kafka-eagle.org/ </Usage>
*****************************************************************
**
[atguigu@hadoop102 eagle]$

注意:啓動之前需要先啓動 ZK 以及 KAFKA
11.登錄頁面查看監控數據

http://192.168.9.102:8048/ke

5.2 Kafka Manager

  1. 上傳壓縮包kafka-manager-1.3.3.15.zip到集羣
  2. 解壓到/opt/module
    3 .修改配置文件conf/application.conf
kafka-manager.zkhosts="kafka-manager-zookeeper:2181"

修改爲:

kafka-manager.zkhosts="hadoop102:2181,hadoop103:2181,hadoop104:2181"

4.啓動kafka-manager

bin/kafka-manager

5.登錄hadoop102:9000頁面查看詳細信息

5.3 Kafka Monitor

  1. 上傳jar包KafkaOffsetMonitor-assembly-0.4.6.jar到集羣
  2. 在/opt/module/下創建kafka-offset-console文件夾
  3. 將上傳的jar包放入剛創建的目錄下
  4. 在/opt/module/kafka-offset-console目錄下創建啓動腳本start.sh,內容如下:
#!/bin/bash
java -cp KafkaOffsetMonitor-assembly-0.4.6-SNAPSHOT.jar \
com.quantifind.kafka.offsetapp.OffsetGetterWeb \
--offsetStorage kafka \
--kafkaBrokers hadoop102:9092,hadoop103:9092,hadoop104:9092 \
--kafkaSecurityProtocol PLAINTEXT \
--zk hadoop102:2181,hadoop103:2181,hadoop104:2181 \
--port 8086 \
--refresh 10.seconds \
--retain 2.days \
--dbName offsetapp_kafka &
  1. 在/opt/module/kafka-offset-console目錄下創建mobile-logs文件夾
    mkdir /opt/module/kafka-offset-console/mobile-logs
  2. 啓動KafkaMonitor
    ./start.sh
  3. 登錄頁面hadoop102:8086端口查看詳情

第 6 章 Flume 對接 Kafka

1)配置 flume(flume-kafka.conf)

# define
# define
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# source
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F -c +0 /opt/module/data/flume.log
a1.sources.r1.shell = /bin/bash -c
# sink
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.bootstrap.servers = 
hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sinks.k1.kafka.topic = first
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1
# channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# bind
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1

2) 啓動 kafkaIDEA 消費者
3) 進入 flume 根目錄下,啓動 flume

$ bin/flume-ng agent -c conf/ -n a1 -f jobs/flume-kafka.conf

4) 向 /opt/module/data/flume.log 裏追加數據,查看 kafka 消費者消費情況

$ echo hello >> /opt/module/data/flume.log

第 7 章 Kafka 面試題

7.1 面試問題
1.Kafka 中的 ISR(InSyncRepli)、OSR(OutSyncRepli)、AR(AllRepli)代表什麼?
2.Kafka 中的 HW、LEO 等分別代表什麼?
3.Kafka 中是怎麼體現消息順序性的?
4.Kafka 中的分區器、序列化器、攔截器是否瞭解?它們之間的處理順序是什麼?
5.Kafka 生產者客戶端的整體結構是什麼樣子的?使用了幾個線程來處理?分別是什麼?
6.“消費組中的消費者個數如果超過 topic 的分區,那麼就會有消費者消費不到數據”這句
話是否正確?
7.消費者提交消費位移時提交的是當前消費到的最新消息的 offset 還是 offset+1?
8.有哪些情形會造成重複消費?
9.那些情景會造成消息漏消費?
10.當你使用 kafka-topics.sh 創建(刪除)了一個 topic 之後,Kafka 背後會執行什麼邏輯?
1)會在 zookeeper 中的/brokers/topics 節點下創建一個新的 topic 節點,如:
/brokers/topics/first
2)觸發 Controller 的監聽程序
3)kafka Controller 負責 topic 的創建工作,並更新 metadata cache
11.topic 的分區數可不可以增加?如果可以怎麼增加?如果不可以,那又是爲什麼?
12.topic 的分區數可不可以減少?如果可以怎麼減少?如果不可以,那又是爲什麼?
13.Kafka 有內部的 topic 嗎?如果有是什麼?有什麼所用?
14.Kafka 分區分配的概念?
15.簡述 Kafka 的日誌目錄結構?
16.如果我指定了一個 offset,Kafka Controller 怎麼查找到對應的消息?
17.聊一聊 Kafka Controller 的作用?
18.Kafka 中有那些地方需要選舉?這些地方的選舉策略又有哪些?
19.失效副本是指什麼?有那些應對措施?
20.Kafka 的哪些設計讓它有如此高的性能?

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