搭建什么的都不说了,先来熟悉一下基本操作
创建一个消息 replica为3, partition为3
1)启动zk(保证你自己的zookeeper已经启动了,这里我用的是kafka自带的zookeeper)
bin/zookeeper-server-start.sh config/zookeeper.properties 1>/dev/null 2>&1 &(后台运行)
2)启动kafka
bin/kafka-server-start.sh config/server.properties
4) 创建新的节点带三个副本:
bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 3 --partitions 3 --topic cc1
bin/kafka-topics.sh --list --bootstrap-server localhost:9092
bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic cc1
5) 开始生产消息
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic cc1
6) 消费消息(单节点的话就退出生产,然后再执行消费者代码,如果是已经搭好集群了,就另启一个客户端来进行消费)
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic cc1
7)查看cc1这个消息
bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic cc1
接下来用Java代码实现生产者:
这个是流程图,这里说一下分区器的分区机制: kafka发送消息到broker之前要确定该消息的具体分区信息,kafka默认提供分区器。该分区机制如果在producer record中指定了key,那么将根据key的值进行hash计算,如果没有则采用轮询方式进行,除此之外,还可以在构造producer record的时候指定分区信息,kafka将优先使用指定的分区信息,避免进行计算提升了性能,但是客户端在指定分区信息的时候要考虑分区负载均衡的问题(这就叫数据偏移,所有数据在一个分区上,压力特别大)
先建好一个cc2,用来生产
生产者代码:
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 KafkaProducerDemo {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "master:9092");
//这里master是我IP的映射名,这个可以在hosts文件里自己去修改
props.put("acks", "all");//这里acks有三个参数可选
//0 : producer不会等待broker发送ack
// 1 :当leader 接收到消息后发送ack
// -1 :当所有的follower都同步消息成功后发送ack
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>("cc2", Integer.toString(i), Integer.toString(i)));
System.out.println(i);
}
producer.close();
System.out.println("发送100条消息成功...");
}
}
pom.xml(这里根据自己的版本来添加)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.1</version>
<!-- <scope>test</scope> -->
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.12</artifactId>
<version>2.3.0</version>
</dependency>
接下来是对案例的美化(这里使用的是同步方案)
优点:保证每条消息准确无误的写入了broker,对于立即需要发送结果的情况非常适合,在producer故障或者宕机的适合也可以保证结果的正确性
缺点:由于同步需要每条消息都需要及时发送到broker,没有缓冲批量操作,性能比较低
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
public class KafkaProducerSyncDemo2 {
public static final Properties props=new Properties();
static{
props.put("bootstrap.servers", "master:9092");
//0 : producer不会等待broker发送ack
// 1 :当leader 接收到消息后发送ack
// -1/all :当所有的follower都同步消息成功后发送ack
props.put("acks", "-1");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
}
final KafkaProducer<String,String> kafkaProducer=new KafkaProducer<String,String>(props);
private String topicName;
public KafkaProducerSyncDemo2(String topicName){
this.topicName=topicName;
}
public RecordMetadata send(String key,String value){
RecordMetadata recordMetadata=null;
try {
recordMetadata =kafkaProducer.send(new ProducerRecord<String,String>(topicName, key,value)).get();
//get()方法讲阻塞 知道返回结果RecordMetadata
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return recordMetadata;
}
public void close(){
if(kafkaProducer!=null){
kafkaProducer.close();
}
}
public static void main(String[] args) {
KafkaProducerSyncDemo2 synKafkaProducer =new KafkaProducerSyncDemo2("cc2");
for (int i=0;i<100;i++){
//broker接受的结果
RecordMetadata metadata=synKafkaProducer.send(String.valueOf(i), "This is"+i+"message");
System.out.println(metadata);
System.out.println("topicname:"+metadata.topic()+"partition:"+metadata.partition()+"offset:"+metadata.offset());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
synKafkaProducer.close();
}
}
这里可以把kafka停掉,来测试一下,因为我们这里写的是发送一百条,然后每次睡眠1秒,就是为了可以测试,同步效果,如果正确的话,他会卡在发送某一条消息
另一种,异步方案
优点:可以通过缓冲池对消息进行缓冲,然后进行消息的批量发送,大量减少了和broker的交互频率,性能高,可以通过回调机制获取发送结果
缺点:在producer直接断电或者重启等故障,将有可能丢失消息发送结果,在对消息准确性很高的场景不适合
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 KafkaProducerAsyncDemo3 {
public static final Properties props=new Properties();
static{
props.put("bootstrap.servers", "master:9092");
//0 : producer不会等待broker发送ack
// 1 :当leader 接收到消息后发送ack
// -1/all :当所有的follower都同步消息成功后发送ack
props.put("acks", "1");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
}
final KafkaProducer<String,String> kafkaProducer=new KafkaProducer<String,String>(props);
private String topicName;
public KafkaProducerAsyncDemo3(String topicName){
this.topicName=topicName;
}
public void send(String key,String value){
//异步方案 采用回调机制来获取服务器响应
kafkaProducer.send(new ProducerRecord<String,String>(topicName,key,value),new Callback(){
//当完成发送后 监听服务器的响应
public void onCompletion(RecordMetadata metadata,Exception exception){
if(exception==null){
System.out.println("broker接受到消息了,"+metadata);
System.out.println("topicname:"+metadata.topic()+"partition:"+metadata.partition()+"offset:"+metadata.offset());
}else{
//进行异常处理
System.out.println("broker出异常了..."+exception);
}
}
});
}
public void close(){
if(kafkaProducer!=null){
kafkaProducer.close();
}
}
public static void main(String[] args) {
KafkaProducerAsyncDemo3 synKafkaProducer =new KafkaProducerAsyncDemo3("cc2);
for (int i=0;i<100;i++){
//broker接受的结果
synKafkaProducer.send(String.valueOf(i), "This is"+i+"message");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(2000);//这里阻塞2秒钟 等待回调函数打印结果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synKafkaProducer.close();
}
}