Preface:

简单地整理了一下Kafka的基础操作指令和简单的Java实现。未来会整理一篇Kafka的概念篇以及做一篇我开发的供数小程序walk through。

 

安装流程参考:

1.Kafka安装+配置 Ubuntu16.04 环境kafka部署以及项目demo

2.Ubuntu 16下单机安装配置zookeeper和kafka 

*Kafka各种报错       https://www.icode9.com/content-4-440422.html

Cluster ID不一致报错    

打开kafka配置文件(server.properties), 找到该选项参数配置(log.dirs=/home/ssd/kafka-本地存储kafka分区、log、index等数据文件的目录)的目录位置,cd /home/ssd/kafka下,删除(rm -rf ./*)掉该目录下的所有文件.然后尝试重新启动就可以了。

问题出现原因分析:之前用其他版本的kafak在该目录下创建了一些主题信息(kafka内部会在用户指定目录下存储许多与保证服务正常工作的相关文件),后面升级到新的kafka版本,然后复用的是该log目录位置,但是没有对该log目录位置下的数据进行情理,导致新版本的kafka服务起来之后报错。将目录清理之后,重启服务正常工作。

 

指令:

启动卡夫卡:  bin/

./ /home/yuchenwu/Kafka/kafka_2.12-2.5.0/config/zookeeper.properties zookeeper

不在console显示日志,转存日志启动: 

bin/nohup ./ /home/yuchenwu/Kafka/kafka_2.12-2.5.0/config/server.properties > ../logs/kafkaqidongrizhi.log &

 

1.创建topic

topic kafka-topics.sh --zookeeper<zookeeper connect> --create --topic <string> --replication-factor <integer> --partitions <integer>
bin/ kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

其中localhost:2181为zookeeper的名字和端口,test为topic的名字自行修改

2.增加分区

从消费者角度来看,为基于键的主题添加分区是很困难的。因为如果改变了分区的数量,键到分区之间的映射也会发生变化。
所以,对于基于键的主题来说,建议在一开始就设置好分区数量,避免以后对其进行调整。 

kafka-toplcs.sh --zookeeper master:2181 --alter -- topic <topicName> --partitions 16

**将 my-topic 主题的分区数量增加到 16。
3.减少分区

我们无法减少主题的分区数量。因为如果删除了分区,分区里的数据也一并被删除,导致数据不一致。 
我们也无法将这些数据分配给其他分区,因为这样做很难,而且会出现消息乱序。所以,如果一定要减少分区数量,只能删 除整个主题,然后重新创建它。
4.删除主题

为了能够删除主题, broker 的 delete.topic. enable 参数必须被设置为 true。如果该参数被设为 false,删除主题的请求会被忽略。

kafka-topics.sh --zookeeper master:2181 --delete -- topic my-topic

5.列出主题

-kafka-topics.sh --zookeeper master:2181 --list

6.列出主题详细信息-

kafka-topics.sh --zookeeper master:2181 --describe <topicName>

7.消费者群组-列出新版本的消费者群组

kafka-consumer-groups.sh new-consumer --bootstrap-server master:9092 --list

8.分区管理 ---TBC
9.消费和生产

在使用 Kafka时,有时候为了验证应用程序,需要手动读取消息或手动生成消息。
这个时 候可以借助 kafka-console-consumer.sh 和 这两个工具,它们包装 了 Java 客户端,让用户不需要编写整个应用程序就可以与 Kafka 主题发生交互。
10.控制台生产者-往主题test上发布消息: 默认情况下,该工具将命令行输入的每一行视为一个消息,消息的键和值以 Tab 字符分隔(如果没有出现 Tab 字符,那么键就是 null)。 

 --broker-list localhost:9092 --topic <name>
Test Message 1
Test Message 2
^D

11.控制台消费者

kafka-console-consumer.sh --zookeeper master:2181 --topic test --from-beginning

./kafka-console-consumer.sh --bootstrap-server master:9092 --topic gongshu --from-beginning

同样,注意broker名字和端口
12.重置

offset ./kafka-consumer-groups.sh --bootstrap-server master:9092 --group <consumerGroup> --reset-offsets --all-topics --to-offset 0 --execute

–to-earliest:把位移调整到分区当前最小位移
–to-latest:把位移调整到分区当前最新位移
–to-current:把位移调整到分区当前位移
–to-offset <offset>: 把位移调整到指定位移处
–shift-by N: 把位移调整到当前位移 + N处,注意N可以是负数,表示向前移动
–to-datetime <datetime>:把位移调整到大于给定时间的最早位移处,datetime格式是yyyy-MM-ddTHH:mm:ss.xxx,比如2017-08-04T00:00:00.000
–by-duration <duration>:把位移调整到距离当前时间指定间隔的位移处,duration格式是PnDTnHnMnS,比如PT0H5M0S
–from-file <file>:从CSV文件中读取调整策略

3.往主题test上发布消息:

 --broker-list localhost:9092 --topic test
Test Message 1
Test Message 2
^D

注意自行修改broker端口localhost:9092

4.从主题test上读取消息: 

kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning


Java实现Kafka 

Maven依赖

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

    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>kafka_2.11</artifactId>
      <version>0.9.0.0</version>
    </dependency>

一些例子

1.创建生产者:

private Properties kafkaProps = new Properties(); 
kafkaProps.put("bootstrap.servers","broker1:9092,broker2:9092");
kafkaProps.put ("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
kafkaProps.put( "value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
producer= new KafkaProducer<String,String>(kafkaProps);

创建一个新的properties——键值为string,所以使用内置的stringSerializer——创建新的生产者对象,把properties传给它

2.发送消息:
一、发送并忘记fire-and-forget

ProducerRecord<String, String> record = new ProducerRecord<>( "CustomerCountry","Precision Products", "France");
try {
producer.send(record);
} 
catch (Exception e){
e.printStackTrace(); 
}

创建producerRecord对象——此构造函数需要topic名字,键,值。它们都是字符串,必须与生产者和序列化器匹配——用send()方法发送。如果不指定key,则会设定成null——
send会返回一个包含RecordMatadata的Future对象,不过我们忽略返回值。如果不关心发送结果,那么可以使用这种发送方式。
比如,记录 Twitter 消息日志,或记录不太重要的应用程序日志。

二、同步发送-我们使用 send() 方怯发送消息, 它会返回…个 Future 对象,调用 get() 方法进行等待, 就可以知道悄息是否发送成功。

ProducerRecord<String, String> record = new ProducerRecord<>( "CustomerCountry","Precision Products", "France");
try {
producer.send(record).get();
} 
catch (Exception e){
e.printStackTrace(); 
}

get()方怯会抛出异常。如果没有发生错 误,我们会得到一个 RecordMetadata 对象,可以用它获取消息的偏移量。
如果在发送数据之前或者在发送过程中发生了任何错误,比如 broker 返回 了一个不允许重发消息的异常或者已经超过了重发的次数,那么就会抛出异常。
我们只是简单地把 异常信息打印出来。

三、异步发送消息-调用 send() 方怯,并指定一个回调函数, 服务器在返回响应时调用该函数。

private class DemoProducerCallback implements Callback {
@Override 
public void onCompletion(RecordMetadata recordMetadata , Exception e){
if(e != null) { 
e.printStackTrace() ;
} 
}
ProducerRecord<String, String> record = new ProducerRecord<>("CustomerCountry","Biomedical Materials", "USA");
producer.send(record,new DemoProducerCallback());

如果 Kafka 返回一个错误, onCompletfon 方邑会抛出一个非空(non null)异常。

2.创建消费者

Properties props = new Properties();
props.put( "bootstrap.servers", "broker1:9092,broker2: 9092");
props.put( "group.id","CountryCounter");
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<String, String>(props);

是新增了group.id 属性,它指定了消费者所属群组的 名字。其实并非必须的,只是创建一个不属于任何群组的消费者不太常见

3.订阅主题

consumer.subscribe(Collections.singletonlist("customerCountries"));

为了简单起见,我们创建了一个只包含单个元素的列表,主题的名字叫作"customerCountries"。
我们也可以在调用 subscribe() 方法时传入一个正则表达式。正则表达式可以匹配多个主题,
如果有人创建了新的主题,并且主题的名字与正则表达式匹配,那么会立即触发一次 再均衡,消费者就可以读取新添加的主题。
consumer.subscribe( "test.*"); 订阅test相关所有主题

4.消费者轮询-消息轮询是消费者 API 的核心,通过一个简单的轮询向服务器请求数据。
一旦消费者订阅 了主题,轮询就会处理所有的细节,包括群组协调、分区再均衡、发送心跳和获取数据,开发者只需要使用一组简单的 API 来处理从分区返回的数据

try { 
while(true){ 
ConsumerRecords<String , String> records = consumer.poll(100)
for(ConsumerRecord<String,String> record : records){ 
log.debug("topic = %s, partition = %s, offset = %d, customer = %s, country = %s\n" , record.topic(), record.partition(),record.offset(),record.key(),record.value());
int updatedCount = 1;
if(custCountryMap.countainsValue(record.value())){ 
updatedCount = custCountryMap.get(record.value()) + 1; 
custCountryMap.put(record.value(), updatedCount);
JSONObject json = new JSONObject(custCountryMap); 
System.out.println(json.toString(4));
} 
}
}
}
finally {
consumer.close();
}

poll() 方能返回一个记录列表。每条记录都包含了记录所属主题的信息、记录所在分 区的信息、 记录在分区里的偏移量,以及记录的键值对。
我们一般会遍历这个列表,逐 条处理这些记录。poll()方法有一个超时参数,它指定了方蓓在多久之后可以返回, 不管有没有可用的数据都要返回。
超时时间的设置取决于应用程序对响应速度的要求, 比如要在多长时间内把控制权归还给执行轮询的线程。

5.提交偏移量
1.自动提交。 enable.auto.commit 被设为 true,不过并没有为开发者留有余地来避免重复处理消息, = 5
2.提交当前偏移量 auto.commit.offset 设为 false ,让应用程序决定何时提交偏移量。使用 commitSync() ,会一直提交,直到提交成功或者发生无法恢复的错误

while (true){ 
ConsumerRecords<String, String>records = consumer.poll(100);
for(ConsumerRecord<String, String> record :records){ 
System.out.printf("topic= %s , partition= %s, offset = %d, customer = % s , country = %s\n", record.topic(), record.partition(), record.offset(),
record.key(), record.value()); 
} 
try {
consumer.commitSync();
}catch (CoMMitFailedException e) { 
log.error("commit failed", e)
}
}

3.异步提交 -我们只管发送提交请求,无需等待 broker 的响应。

while (true){ 
ConsumerRecords<String, String>records = consumer.poll(100);
for(ConsumerRecord<String, String> record :records){ 
System.out.printf("topic= %s , partition= %s, offset = %d, customer = % s , country = %s\n", record.topic(), record.partition(), record.offset(),
record.key(), record.value()); 
} 
consumer.commitAsync();
}

4.同步和异步组合提交
如果一切正常,我们使用 commitAsync() 方陆来提交。这样速度更快,而且即使这次提 交失败,下一次提交很可能会成功。
如果直接关闭消费者,就没有所谓的"下一次提交"了。使用 commitSync()方也会一 直重式,直到提交成功或发生无陆恢复的错误。

 


References Other Than Listed Above

  • Kafa权威指南 Kafka The Definitive Guide