From f5b3cd52a8758dc049e788dc378ca2d06b6cb4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E7=A5=A5?= <1366971433@qq.com> Date: Tue, 28 May 2019 13:11:44 +0800 Subject: [PATCH] =?UTF-8?q?kafka=E7=94=9F=E4=BA=A7=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/Kafka/kafka-basis/pom.xml | 38 ++++++++ .../heibaiying/producers/ProducerASyn.java | 43 +++++++++ .../com/heibaiying/producers/ProducerSyn.java | 42 +++++++++ .../producers/ProducerWithPartitioner.java | 37 ++++++++ .../heibaiying/producers/SimpleProducer.java | 34 ++++++++ .../producers/utils/CustomPartitioner.java | 29 +++++++ notes/Kafka生产者详解.md | 82 ++++++++++++++++++ pictures/kafka-simple-producer.png | Bin 0 -> 10251 bytes 8 files changed, 305 insertions(+) create mode 100644 code/Kafka/kafka-basis/pom.xml create mode 100644 code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerASyn.java create mode 100644 code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerSyn.java create mode 100644 code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerWithPartitioner.java create mode 100644 code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/SimpleProducer.java create mode 100644 code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/utils/CustomPartitioner.java create mode 100644 pictures/kafka-simple-producer.png diff --git a/code/Kafka/kafka-basis/pom.xml b/code/Kafka/kafka-basis/pom.xml new file mode 100644 index 0000000..d46efa3 --- /dev/null +++ b/code/Kafka/kafka-basis/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + com.heibaiying + kafka-basis + 1.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + + + + org.apache.kafka + kafka-clients + 2.2.0 + + + org.slf4j + slf4j-nop + 1.7.25 + + + + + \ No newline at end of file diff --git a/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerASyn.java b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerASyn.java new file mode 100644 index 0000000..0bebb09 --- /dev/null +++ b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerASyn.java @@ -0,0 +1,43 @@ +package com.heibaiying.producers; + +import org.apache.kafka.clients.producer.*; + +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +/* + * Kafka生产者示例——异步发送消息 + */ +public class ProducerASyn { + + public static void main(String[] args) { + + String topicName = "Hello-Kafka"; + + Properties props = new Properties(); + props.put("bootstrap.servers", "192.168.200.226:9092"); + props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + /*创建生产者*/ + Producer producer = new KafkaProducer<>(props); + + for (int i = 0; i < 10; i++) { + /*异步发送消息*/ + ProducerRecord 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()); + } + } + }); + } + + /*关闭生产者*/ + producer.close(); + } +} \ No newline at end of file diff --git a/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerSyn.java b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerSyn.java new file mode 100644 index 0000000..0fa05ad --- /dev/null +++ b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerSyn.java @@ -0,0 +1,42 @@ +package com.heibaiying.producers; + +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; + +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +/* + * Kafka生产者示例——同步发送消息 + */ +public class ProducerSyn { + + public static void main(String[] args) { + + String topicName = "Hello-Kafka"; + + Properties props = new Properties(); + props.put("bootstrap.servers", "192.168.200.226:9092"); + props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + /*创建生产者*/ + Producer producer = new KafkaProducer<>(props); + + for (int i = 0; i < 10; i++) { + /*同步发送消息*/ + try { + ProducerRecord 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(); + } + } + + /*关闭生产者*/ + producer.close(); + } +} \ No newline at end of file diff --git a/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerWithPartitioner.java b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerWithPartitioner.java new file mode 100644 index 0000000..b707323 --- /dev/null +++ b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/ProducerWithPartitioner.java @@ -0,0 +1,37 @@ +package com.heibaiying.producers; + +import org.apache.kafka.clients.producer.*; + +import java.util.Properties; + +/* + * Kafka生产者示例——异步发送消息 + */ +public class ProducerWithPartitioner { + + public static void main(String[] args) { + + String topicName = "Kafka-Partitioner-Test"; + + Properties props = new Properties(); + props.put("bootstrap.servers", "192.168.200.226: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.utils.CustomPartitioner"); + props.put("pass.line", 6); + + Producer producer = new KafkaProducer<>(props); + + for (int i = 0; i <= 10; i++) { + /*异步发送消息*/ + String score = "score:" + i; + ProducerRecord record = new ProducerRecord<>(topicName, i, score); + producer.send(record, (metadata, exception) -> + System.out.printf("%s, partition=%d, \n", score, metadata.partition())); + } + + /*关闭生产者*/ + producer.close(); + } +} \ No newline at end of file diff --git a/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/SimpleProducer.java b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/SimpleProducer.java new file mode 100644 index 0000000..e0429ec --- /dev/null +++ b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/SimpleProducer.java @@ -0,0 +1,34 @@ +package com.heibaiying.producers; + +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerRecord; + +import java.util.Properties; + +/* + * Kafka生产者示例 + */ + +public class SimpleProducer { + + public static void main(String[] args) { + + String topicName = "Hello-Kafka"; + + Properties props = new Properties(); + props.put("bootstrap.servers", "192.168.200.226:9092"); + props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); + /*创建生产者*/ + org.apache.kafka.clients.producer.Producer producer = new KafkaProducer<>(props); + + for (int i = 0; i < 10; i++) { + /* 发送消息*/ + ProducerRecord record = new ProducerRecord<>(topicName, "hello" + i, "world" + i); + producer.send(record); + } + + /*关闭生产者*/ + producer.close(); + } +} \ No newline at end of file diff --git a/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/utils/CustomPartitioner.java b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/utils/CustomPartitioner.java new file mode 100644 index 0000000..1842406 --- /dev/null +++ b/code/Kafka/kafka-basis/src/main/java/com/heibaiying/producers/utils/CustomPartitioner.java @@ -0,0 +1,29 @@ +package com.heibaiying.producers.utils; + +import org.apache.kafka.clients.producer.Partitioner; +import org.apache.kafka.common.Cluster; + +import java.util.Map; + + +public class CustomPartitioner implements Partitioner { + + private int passLine; + + @Override + public void configure(Map configs) { + passLine = (Integer) configs.get("pass.line"); + } + + @Override + public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) { + return (Integer) key >= passLine ? 1 : 0; + } + + @Override + public void close() { + System.out.println("分区器关闭"); + } + + +} diff --git a/notes/Kafka生产者详解.md b/notes/Kafka生产者详解.md index e69de29..f5cfd19 100644 --- a/notes/Kafka生产者详解.md +++ b/notes/Kafka生产者详解.md @@ -0,0 +1,82 @@ +#### 1. 启动Kakfa + +Kafka的运行依赖于zookeeper,需要预先启动,可以启动Kafka内置的zookeeper,也可以启动自己安装的。 + +```shell +# zookeeper启动命令 +bin/zkServer.sh start + +# 内置zookeeper启动命令 +bin/zookeeper-server-start.sh config/zookeeper.properties +``` + +启动单节点kafka用于测试: + +```shell +# bin/kafka-server-start.sh config/server.properties +``` + +#### 2. 创建topic + +```shell +# 创建用于测试主题 +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. 启动消费者 + + 启动一个控制台消费者用于观察写入情况,启动命令如下: + +```shell +# bin/kafka-console-consumer.sh --bootstrap-server hadoop001:9092 --topic Hello-Kafka --from-beginning +``` + + + +```shell +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 +``` + + + + + +```shell + bin/kafka-topics.sh --create \ + --bootstrap-server hadoop001:9092 \ + --replication-factor 1 --partitions 2 \ + --topic Kafka-Partitioner-Test +``` + + + +```shell +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, +分区器关闭 +``` + diff --git a/pictures/kafka-simple-producer.png b/pictures/kafka-simple-producer.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff45311c93af0c8e92a6c73fd84ed57c9c937fc GIT binary patch literal 10251 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