生产者
注意点:
- 可以使用消息的同步发送和异步发送, send 方法返回的是一个 future 对象, 可以使用 get 进行阻塞等待返回. 或传入 callBack 方法进行异步回调.
- 可以在创建 record 的时候指定分区, 如果不指定, 则使用默认的负载均衡分配分区.
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.IntegerSerializer;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @author djh
* @date 2020/2/29 16:53
* @description 描述信息
*/
public class KafkaProductDemo {
public static final String TOPIC = "This_is_a_topic";
public static void main(String[] args) throws InterruptedException {
Properties properties = new Properties();
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop3-host:9092,hadoop4-host:9092,hadoop5-host:9092");
properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class.getName());
properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 创建生产者.
KafkaProducer<Integer, String> kafkaProducer = new KafkaProducer<>(properties);
for (int i = 1; i < 100000; i++) {
Thread.sleep(500);
ProducerRecord<Integer, String> record = new ProducerRecord<>(TOPIC, i, "这是第" + i + "条消息");
kafkaProducer.send(record, (metadata, exception) -> {
System.out.println(metadata.toString());
exception.printStackTrace();
});
}
kafkaProducer.close();
}
}
运行结果:
This_is_a_topic-0@0
This_is_a_topic-3@0
This_is_a_topic-1@0
This_is_a_topic-3@1
This_is_a_topic-4@0
This_is_a_topic-9@0
This_is_a_topic-3@2
This_is_a_topic-6@0
This_is_a_topic-8@0
This_is_a_topic-1@1
This_is_a_topic-9@1
This_is_a_topic-7@0
This_is_a_topic-3@3
This_is_a_topic-9@2
This_is_a_topic-8@1
This_is_a_topic-9@3
This_is_a_topic-1@2
我们可以看到,每个分区都有分配到消息, 我这默认设置成了十个分区.
消费者
注意点:
- 属于不同进程的消费者, 只要 group_id 相同则属于同一个消费者组, 对同一个分区存在争夺消费的情况.
- 如果不为某一消费者指定分区, 则使用默认的负债均衡, 消费某几个分区, 如果一个消费者组只有一个消费者, 那就会消费所有分区的数据.
- 可以使用 assign 指定某一个消费者指定消费哪一个分区.
- 消费者需要不断的对 kafka 进行轮询, 如果一定时间段内, 没有接受到来之消费者的轮询, 则认为该消费者死亡.
关于消费的顺序性:
- 如果整个 topic 只有一个消费者在消费, 无论这一个 topic 有多少个分区, 依旧可以保证消费的顺序性, 从头到尾都是顺序的.
- 如果动态的在一个消费者组中添加消费者, 那么此时便在不同消费者之间不会顺序, 因为一个消费者只能消费某一个对应的分区嘛, 但是在一个消费者内部, 对同一个分区的消费还是保有顺序性的.
import org.apache.kafka.clients.consumer.ConsumerConfig;
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.serialization.IntegerDeserializer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
/**
* @author djh
* @date 2020/2/29 17:13
* @description 描述信息
*/
public class KafkaConsumerDemo {
public static final String CONSUMER_GROUP = "consumer_two";
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop3-host:9092,hadoop4-host:9092,hadoop5-host:9092");
properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class.getName());
properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 指定消费者组,组名称相同的不同消费者进程仍然属于同一个消费者组.
properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP);
KafkaConsumer<Integer, String> kafkaConsumer = new KafkaConsumer<>(properties);
kafkaConsumer.subscribe(Collections.singletonList(KafkaProductDemo.TOPIC));
//kafkaConsumer.assign(Arrays.asList(new TopicPartition(KafkaProductDemo.TOPIC, 0), new TopicPartition(KafkaProductDemo.TOPIC, 2)));
//kafkaConsumer.seek(new TopicPartition(KafkaProductDemo.TOPIC, 0), 0);
// 一直处于消费状态.
while (true) {
ConsumerRecords<Integer, String> records = kafkaConsumer.poll(Duration.ofSeconds(5));
for (ConsumerRecord<Integer, String> record : records) {
System.out.println("消费第" + record.key() + "条消息, \t 内容为:" + record.value() + "\t 偏移量为:" + record.offset() +
"\t 分区为:" + record.partition());
}
}
}
}
运行结果:
当有三个消费者时:
消费者 1 的日志:
消费第4102条消息, 内容为:这是第4102条消息 偏移量为:429 分区为:4
消费第4115条消息, 内容为:这是第4115条消息 偏移量为:430 分区为:4
消费第4117条消息, 内容为:这是第4117条消息 偏移量为:399 分区为:5
消费第4121条消息, 内容为:这是第4121条消息 偏移量为:467 分区为:6
消费第4122条消息, 内容为:这是第4122条消息 偏移量为:431 分区为:4
消费第4123条消息, 内容为:这是第4123条消息 偏移量为:400 分区为:5
消费第4126条消息, 内容为:这是第4126条消息 偏移量为:432 分区为:4
消费者 2 的日志:
消费第4232条消息, 内容为:这是第4232条消息 偏移量为:440 分区为:3
消费第4235条消息, 内容为:这是第4235条消息 偏移量为:432 分区为:2
消费第4236条消息, 内容为:这是第4236条消息 偏移量为:390 分区为:1
消费第4238条消息, 内容为:这是第4238条消息 偏移量为:409 分区为:0
消费第4240条消息, 内容为:这是第4240条消息 偏移量为:391 分区为:1
消费第4242条消息, 内容为:这是第4242条消息 偏移量为:441 分区为:3
消费第4244条消息, 内容为:这是第4244条消息 偏移量为:433 分区为:2
消费第4245条消息, 内容为:这是第4245条消息 偏移量为:410 分区为:0
消费者 3 的日志:
消费第4043条消息, 内容为:这是第4043条消息 偏移量为:379 分区为:9
消费第4046条消息, 内容为:这是第4046条消息 偏移量为:380 分区为:9
消费第4061条消息, 内容为:这是第4061条消息 偏移量为:400 分区为:8
消费第4065条消息, 内容为:这是第4065条消息 偏移量为:381 分区为:9
消费第4066条消息, 内容为:这是第4066条消息 偏移量为:397 分区为:7
消费第4067条消息, 内容为:这是第4067条消息 偏移量为:398 分区为:7
消费第4068条消息, 内容为:这是第4068条消息 偏移量为:399 分区为:7
消费第4075条消息, 内容为:这是第4075条消息 偏移量为:401 分区为:8
当只有一个消费者时的日志:
消费第4630条消息, 内容为:这是第4630条消息 偏移量为:493 分区为:4
消费第4631条消息, 内容为:这是第4631条消息 偏移量为:470 分区为:2
消费第4632条消息, 内容为:这是第4632条消息 偏移量为:448 分区为:8
消费第4633条消息, 内容为:这是第4633条消息 偏移量为:480 分区为:3
消费第4634条消息, 内容为:这是第4634条消息 偏移量为:431 分区为:9
消费第4635条消息, 内容为:这是第4635条消息 偏移量为:427 分区为:1
消费第4636条消息, 内容为:这是第4636条消息 偏移量为:454 分区为:7
消费第4637条消息, 内容为:这是第4637条消息 偏移量为:461 分区为:0
消费第4638条消息, 内容为:这是第4638条消息 偏移量为:432 分区为:9
消费第4639条消息, 内容为:这是第4639条消息 偏移量为:471 分区为:2
消费第4640条消息, 内容为:这是第4640条消息 偏移量为:455 分区为:7
消费第4641条消息, 内容为:这是第4641条消息 偏移量为:428 分区为:1
消费第4642条消息, 内容为:这是第4642条消息 偏移量为:433 分区为:9
消费第4643条消息, 内容为:这是第4643条消息 偏移量为:447 分区为:5
通过上面我们看到, 结果很明显, 要想使 kafka 的消息得到顺序消费, 要么只要有一个分区, 要么多个分区只要有一个消费者即可. 动态的添加或删除消费者, 如果没指定消费那个分区, 则会进行自动的负债均衡.