Kafka实战笔记
单机版搭建
kafka的运行需要提前配好Java 环境,笔者的是 java version “1.8.0_201”
第一步 下载程序
下载源码 此处用的是2.11版本
解压
[root@cluster01:opt] # tar -xzf kafka_2.11-2.1.0.tgz
[root@cluster01:opt] # cd kafka_2.11-2.1.0
第二步 启动服务
开启zookeeper, kafka的运行需要zookeeper
[root@cluster01:kafka_2.11-2.1.0] # /opt/Apache/zookeeper-3.4.10/bin/zkServer.sh start
或者可以使用kafka自带的zookeeper
[root@cluster01:kafka_2.11-2.1.0] # bin/zookeeper-server-start.sh config/zookeeper.properties
运行kafka程序
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-server-start.sh config/server.properties
第三步 创建Topic
创建名为 test3 且分区数为1,重复因子为1的 topic3
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test3
Created topic "test3".
查看创建的 topic
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-topics.sh --list --zookeeper localhost:2181
KAFKA-STORM
__consumer_offsets
connect-test
first
test
test2
第四步 发送消息
只用kafka自带的生产者脚本发送消息
[root@cluster02:kafka_2.11-2.1.0] # bin/kafka-console-producer.sh --broker-list cluster02:9092 --topic test
>hello
>hu
第五步 消费消息
使用kafka自带的消费者脚本消费消息
[root@cluster02:kafka_2.11-2.1.0] # bin/kafka-console-consumer.sh --bootstrap-server cluster02:9092 --topic test --from-beginning
hello
hu
note : 应当使用 --bootstrap-server 参数,之前的版本可能使用 --zookeeper , --from-beginning 参数指明从最初的消息开始读取(消费)
至此,单机版 kafka 已经尝鲜完毕,总结就是下载程序安装,开启zookeeper,kafka,创建topic,生产消息,消费消息
集群版搭建
这里的集群环境是三台CentOS 7,VMware虚拟机
第一步 下载程序
下载源码 此处用的是2.11版本
在三台CentOS 7服务器中分别下载好kafka程序包,如不想使用自带的zookeeper可以自己下载安装,笔者的三台服务器中之前已经安装好了
解压
[root@cluster01:opt] # tar -xzf kafka_2.11-2.1.0.tgz
[root@cluster01:opt] # cd kafka_2.11-2.1.0
第二步 修改主机名【可跳过】
登录主机之后使用 hostname 命令修改主机名,这样好区分三台主机,当然,不修改也无伤大雅
# 第一台
[root@localhost:~] # hostname cluster01
# 第二台
[root@localhost:~] # hostname cluster02
# 第三台
[root@localhost:~] # hostname cluster03
配置 hosts
为了不每次指定的服务器的时候都使用 IP 指定,可以在这三台服务器和外部主机的 hosts 文件中指定名称
笔者的外部主机使用的是 Ubuntu 18.10
首先使用 ifconfig/ ip address 命令查看CentOS7服务器的 IP 地址
然后修改各自的 hosts 文件
[root@sairoPC:sairo] # vim /etc/hosts
......
192.168.67.129 cluster01
192.168.67.130 cluster02
192.168.67.131 cluster03
......
另外CentOS7自带的tty终端界面感觉有些丑陋,Ubuntu的用户可以使用 ssh 远程连接命令,或者使用 Terminus 远程连接工具,Windows 的用户可以尝试 XShell , XFtp
因为我们的虚拟机的IP是根据DHCP协议动态分配的,所以有的时候IP肯能会自动改变,这个是可以:
- 修改hosts文件
- 固定服务器的IP分配策略,使其静态分配,具体方法可参考笔者的另一篇博客
第三步 修改配置文件
kafka集群搭建必须要修改以下三项
broker.id=1 # 必须唯一标识broker
listeners=listeners = listener_name://host_name:port
log.dirs=/tmp/kafka-logs-1 # 日志目录
笔者的修改如下
# The id of the broker. This must be set to a unique integer for each broker.
broker.id=1 # 修改,各个服务器中的数值必须不同以标识broker
########################## enable delete topic ###########################
delete.topic.enable=true # 添加此项
......
# The address the socket server listens on. It will get the value returned from
# java.net.InetAddress.getCanonicalHostName() if not configured.
# FORMAT:
# listeners = listener_name://host_name:port
# EXAMPLE:
# listeners = PLAINTEXT://your.host.name:9092
listeners=PLAINTEXT://cluster01:9092 # 添加此项,此处的cluster01对应于具体的主机名
......
# A comma separated list of directories under which to store log files
log.dirs=/opt/Apache/kafka_2.11-2.1.0/logs # 修改此项,注意这个logs 目录事先不存在,需自行创建,也可以随意指定
......
# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
zookeeper.connect=cluster01:2181,cluster02:2181,cluster03:2181 # 指定zookeeper,cluster*对应hosts文件中ip对应 # 的名称,读者按需修改
至此,kafka的集群的搭建已经完成,需要提醒的是kafka集群的运行需要zookeeper协调, 至于zookeeper集群的搭建读者可以谷歌搜索相关资料。或者直接使用kafka自带的zookeeper插件,不用再搭建zookeeper集群。笔者有时间也会再写一篇zookeeper集群环境搭建的文章。
第四步 启动kafka
进入到kafka目录
[root@cluster02:~] # cd /opt/Apache/kafka_2.11-2.1.0/
[root@cluster02:kafka_2.11-2.1.0] # ls
bin config libs LICENSE logs NOTICE site-docs
以下的操作在三台服务器中是相同的,节省篇幅只列出一台操作过程,三台服务器的操作不分先后
先启动zookeeper, 这里使用的是非内置的zookeeper,
[root@cluster03:kafka_2.11-2.1.0] # /opt/Apache/zookeeper-3.4.10/bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/Apache/zookeeper-3.4.10/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@cluster03:kafka_2.11-2.1.0] # /opt/Apache/zookeeper-3.4.10/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/Apache/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
[root@cluster03:kafka_2.11-2.1.0] #
在命令行执行如下命令以启动kafka
[root@cluster02:kafka_2.11-2.1.0] # bin/kafka-server-start.sh config/server.properties
[2019-02-26 21:58:44,997] INFO Registered kafka:type=kafka.Log4jController MBean (kafka.utils.Log4jControllerRegistration$)
[2019-02-26 21:58:45,390] INFO starting (kafka.server.KafkaServer)
[2019-02-26 21:58:45,391] INFO Connecting to zookeeper on cluster01:2181,cluster02:2181,cluster03:2181 (kafka.server.KafkaServer)
......
请注意,启动kafka的脚本是kafka-server-start.sh,参数是配置文件 server.properties(这个文件名不是固定的) ,所以每一个配置文件对应于一个kafka的broker, 所以读者如果想在一台服务器上搭建多个kafka节点,可以创建多个配置文件,修改其中的配置参数,然后用kafka-server-start.sh脚本启动,至于如何修改请参考第三步和官方教程
第五步 创建一个topic
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-topics.sh --create --zookeeper cluster01:2181 --replication-factor 3 --partitions 1 --topic test4
Created topic "test4".
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-topics.sh --list --zookeeper cluster01:2181
KAFKA-STORM
__consumer_offsets
connect-test
first
test
test2
test3
test4
[root@cluster01:kafka_2.11-2.1.0] #
第六步 启动生产者
[root@cluster01:kafka_2.11-2.1.0] # bin/kafka-console-producer.sh --broker-list cluster01:9092 --topic test4
>1
>2
>
>3
>
第七步 启动消费者
在另一台机器上
[root@cluster02:kafka_2.11-2.1.0] # bin/kafka-console-consumer.sh --bootstrap-server cluster02:9092 --from-beginning --topic test4
1
2
3
看,集群的效果出来了
以上的演示是kafka从生产者生产消息然后消费消息,但实际上消息的来源是多方面的,可以从数据库获取,本地文件获取。kafka本身也提供一个工具 connect-standalone.sh,有兴趣的读者可以看看,其官网有提供示例。
编程案例
接下来我们用代码实现生产者生产消息,消费者消费消息
KafkaProducer API
KafkaProducer API的核心部分是KafkaProducer类。KafkaProducer类提供了一个选项,用于在其构造函数中使用以下方法连接Kafka broker。
- KafkaProducer类提供send方法以异步方式topic发送消息。send() 的签名如下
producer.send(new ProducerRecord<byte[],byte[]>(topic, partition, key1, value1) , callback);
- ProducerRecord - 生产者管理等待发送的记录缓冲区。
- Callback - 用户提供的回调,在服务器确认记录时执行(null表示没有回调)。
- KafkaProducer类提供了一个flush方法,以确保所有先前发送的消息都已实际完成。flush方法的签名如下:
public void flush()
- KafkaProducer类提供partitionFor方法,该方法有助于获取给定主题的分区元数据。这可以用于自定义分区。这种方法的签名如下:
public Map metrics()
它返回生产者维护的内部metrics的映射。
public void close() - KafkaProducer类提供关闭方法块,直到完成所有先前发送的请求。
Producer API
Producer API的核心部分是Producer类。Producer类提供了一个通过以下方法在其构造函数中连接Kafka broker 的选项。
The Producer Class
生产者类提供send方法,使用以下方法将消息发送到单个或多个主题。
public void send(KeyedMessaget<k,v> message)
//sends the data to a single topic,par-titioned by key using either sync or async producer.
public void send(List<KeyedMessage<k,v>>messages)
//sends data to multiple topics.
Properties prop = new Properties();
prop.put(producer.type,”async”)
ProducerConfig config = new ProducerConfig(prop);
生产者有两种类型 —“同步”和“异步”
同样的API配置也适用于Sync生产者。它们之间的区别是同步型生产者直接发送消息,但是在后台发送。当您需要更高的吞吐量时,首选异步生成器。在0.8之前的版本中,异步生产者没有*send()*的回调来注册错误处理程序。这仅在当前版本的0.9中可用。
public void close()
Producer类提供close方法来关闭与所有Kafka broker 的生产者池连接。
Configuration Settings
下表列出了Producer API的主要配置设置,以便更好地了解
S.No | Configuration Settings and Description |
---|---|
1 | client.id identifies producer application |
2 | producer.type either sync or async |
3 | acks The acks config controls the criteria under producer requests are con-sidered complete. |
4 | retries If producer request fails, then automatically retry with specific value. |
5 | bootstrap.servers bootstrapping list of brokers. |
6 | linger.ms if you want to reduce the number of requests you can set linger.ms to something greater than some value. |
7 | key.serializer Key for the serializer interface. |
8 | value.serializer value for the serializer interface. |
9 | batch.size Buffer size. |
10 | buffer.memory controls the total amount of memory available to the producer for buff-ering. |
ProducerRecord API
ProducerRecord是一个键/值对,发送到Kafka cluster。ProducerRecord用于构造包含分区,键和值对的记录。
public ProducerRecord (string topic, int partition, k key, v value)
- Topic − 用户定义的主题名称将附加到记录。
- Partition − 分区计数
- Key − 将包含在记录中的key
- Value − 记录内容
public ProducerRecord (string topic, k key, v value)
ProducerRecord类构造函数用于创建具有键/值对且没有分区的记录。
- Topic − 创建 topic 以分配记录。
- Key − 记录的 key
- Value − 记录内容
public ProducerRecord (string topic, v value)
- Topic − 创建主题
- Value − 记录内容
ProducerRecord类方法列表
S.No | Class Methods and Description |
---|---|
1 | public string topic() Topic will append to the record. |
2 | public K key() Key that will be included in the record. If no such key, null will be returned here. |
3 | public V value() Record contents. |
4 | partition() Partition count for the record |
截至目前,我们已经创建了一个生产者来向Kafka集群发送消息。现在让我们创建一个消费者来消费Kafka集群中的消息。KafkaConsumer API用于使用来自Kafka群集的消息。KafkaConsumer类构造函数定义如下:
public KafkaConsumer(java.util.Map<java.lang.String,java.lang.Object> configs)
- configs - 返回消费者配置图。
KafkaConsumer
KafkaConsumer类具有以下重要方法,如下表所示:
S.No | Method and Description |
---|---|
1 | public java.util.Set assignment() Get the set of partitions currently assigned by the con-sumer. |
2 | public string subscription() Subscribe to the given list of topics to get dynamically as-signed partitions. |
3 | public void subscribe(java.util.List<java.lang.String> topics, ConsumerRebalanceListener listener) Subscribe to the given list of topics to get dynamically as-signed partitions. |
4 | public void unsubscribe() Unsubscribe the topics from the given list of partitions. |
5 | public void subscribe(java.util.List<java.lang.String> topics) Subscribe to the given list of topics to get dynamically as-signed partitions. If the given list of topics is empty, it is treated the same as unsubscribe(). |
6 | public void subscribe(java.util.regex.Pattern pattern, ConsumerRebalanceLis-tener listener) The argument pattern refers to the subscribing pattern in the format of regular expression and the listener argument gets notifications from the subscribing pattern. |
7 | public void assign(java.util.List partitions) Manually assign a list of partitions to the customer. |
8 | poll() Fetch data for the topics or partitions specified using one of the subscribe/assign APIs. This will return error, if the topics are not subscribed before the polling for data. |
9 | public void commitSync() Commit offsets returned on the last poll() for all the sub-scribed list of topics and partitions. The same operation is applied to commitAsyn(). |
10 | public void seek(TopicPartition partition, long offset) Fetch the current offset value that consumer will use on the next poll() method. |
11 | public void resume() Resume the paused partitions. |
12 | public void wakeup() Wakeup the consumer. |
ConsumerRecord API
ConsumerRecord API用于接收来自Kafka群集的记录。此API包含主题名称,分区号,从中接收记录以及指向Kafka分区中记录的偏移量。ConsumerRecord类用于创建具有特定主题名称,分区计数和<键,值>对的使用者记录。它有以下签名:
public ConsumerRecord(string topic,int partition, long offset,K key, V value)
- Topic - 从Kafka群集收到的使用者记录的主题名称。
- Partition - 主题的分区。
- Key - 记录的密钥,如果没有key,则返回null。
- Value - 记录内容。
ConsumerRecords API
ConsumerRecords API充当ConsumerRecord的容器。此API用于保留特定主题的每个分区的ConsumerRecord列表。其构造函数定义如下。
public ConsumerRecords(java.util.Map<TopicPartition,java.util.List<ConsumerRecord>K,V>>> records)
- TopicPartition - 返回特定主题的分区映射。
- Records = ConsumerRecord的返回列表。
ConsumerRecords类定义了以下方法:
S.No | Methods and Description |
---|---|
1 | public int count() The number of records for all the topics. |
2 | public Set partitions() The set of partitions with data in this record set (if no data was returned then the set is empty). |
3 | public Iterator iterator() Iterator enables you to cycle through a collection, obtaining or re-moving elements. |
4 | public List records() Get list of records for the given partition. |
Configuration Settings
S.No | Settings and Description |
---|---|
1 | bootstrap.servers Bootstrapping list of brokers. |
2 | group.id Assigns an individual consumer to a group. |
3 | enable.auto.commit Enable auto commit for offsets if the value is true, otherwise not committed. |
4 | auto.commit.interval.ms Return how often updated consumed offsets are written to ZooKeeper. |
5 | session.timeout.ms Indicates how many milliseconds Kafka will wait for the ZooKeeper to respond to a request (read or write) before giving up and continuing to consume messages. |
代码
以下代码已经放到 github https://github.com/lymboy/kafka1.git
在开始编码之前,先在虚拟机中启动 zookeeper 和创建一个 topic
笔者使用的 IDEA, 新建是 maven 项目,
<dependencies>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.12</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
生产者:
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;
/**
* @author sairo
* @date 2019-2-21
*/
public class MyKafkaProducer {
public static void main(String[] args) {
Properties props = new Properties();
//设置服务器地址
props.put("bootstrap.servers", "cluster01:9092");
//设置对所有请求应答
props.put("acks", "all");
//如果请求失败,kafka可以自动重试
props.put("retries", 0);
//设置缓冲区大小
props.put("batch.size", 16384);
//减少小于0的请求数
props.put("linger.ms", 1);
//buffer.memory控制生产者可用于缓冲的总内存量。
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");
Producer<String, String> producer = new KafkaProducer<>(props);
for(int i = 0; i < 10; i++)
producer.send(new ProducerRecord<>("test4",
Integer.toString(i), Integer.toString(i)));
System.out.println("Message sent successfully");
producer.close();
}
}
启动运行后,使用消费者脚本查看
消费者
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;
/**
* @author sairo
* @date 2019-02-21
*/
public class MyKafkaConsumer {
public static void main(String[] args) {
//Kafka consumer configuration settings
String topicName = "test4";
Properties props = new Properties();
props.put("bootstrap.servers", "cluster01:9092");
props.put("group.id", "test-consumer-group");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
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);
//Kafka Consumer subscribes list of topics here.
consumer.subscribe(Arrays.asList(topicName));
//print the topic name
System.out.println("Subscribed to topic " + topicName);
int i = 0;
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records)
// print the offset,key and value for the consumer records.
System.out.printf("offset = %d, key = %s, value = %s\n",
record.offset(), record.key(), record.value());
}
}
}
运行后,生产者使用名为 test4 的 topic 发送一些简单的消息
项目总览