Kafka的概念和入门

Kafka是一个消息系统。由LinkedIn于2011年设计开发。

Kafka是一种分布式的,基于发布/订阅的消息系统。主要设计目标如下:

  1. 以时间复杂度O(1)的方式提供消息持久化能力,即使对TB级以上数据页能保证常数时间复杂度的访问性能。
  2. 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条以上的消息传输。
  3. 支持Kafka Server间的消息分区,及分布式消费,同时保证每个Partition内的消息顺序传输。
  4. 同时支持离线数据处理和实时数据处理。
  5. 支持在线水平扩展。

消费者是采用pull模式从Broker订阅消息。

模式 优点 缺点
pull模式 消费者可以根据自己的消费能力决定拉取的策略 没有消息的时候会空轮询(kafka为了避免,有个参数可以阻塞直到新消息到达)
push模式 及时性高 消费能力远低于生产能力时,就会导致客户端消息堆积,甚至服务崩溃。服务端需要维护每次传输状态,以防消息传递失败好进行重试。

Kafka的基本概念

  1. Broker:Kafka集群包含一个或多个服务器,这种服务器被称为broker
  2. Topic:每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。
  3. Partition:Partition是物理上的概念,每个Topic包含一个或多个Partition
  4. Producer:负责发布消息到Kafka broker
  5. Consumer:消息消费者,向Kafka broker读取消息的客户端
  6. Consumer Group:每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)

单机部署结构

集群部署结构

Topic和Partition

一个Topic可以包含一个或者多个Partition。因为一台机器的存储和性能是有限的,多个Partition是为了支持水平扩展和并行处理。

Partition和Replica

分为多个Partition可以将消息压力分到多个机器上,但是如果其中一个partition数据丢了,那总体数据就少了一块。所以又引入了Replica的概念。

每个partition都可以通过副本因子添加多个副本。这样就算有一台机器故障了,其他机器上也有备份的数据

集群环境下的3分区,3副本:

二、安装部署

  1. 下载kafka http://kafka.apache.org/downloads

我用的2.7.0版本,下载后解压

注意选择Binary downloads而不是Source download

2. 进入conf/server.properties文件,打开如下配置

listeners=PLAINTEXT://localhost:9092

  1. 启动zookeeper

自行安装,我用的3.7版本的zookeeper

4. 启动kafka

bin/kafka-server-start.sh config/server.properties

Kafka命令行

查看topic

bin/kafka-topics.sh --zookeeper localhost:2181 --list

创建topic

bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic test1 --partitions 4 --replication-factor 1

查看topic信息

bin/kafka-topics.sh --zookeeper localhost:2181 --describe --topic test1

消费命令

bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic test1

生产命令

bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test1

简单性能测试:

bin/kafka-producer-perf-test.sh --topic test1 --num-records 100000 --record-size 1000 --throughput 2000 --producer-props bootstrap.servers=localhost:9092

bin/kafka-consumer-perf-test.sh --bootstrap-server localhost:9092 --topic test1 -- fetch-size 1048576 --messages 100000 --threads 1

Java客户端

生产者:

  1. public class SimpleKafkaProducer {
  2. public static void main(String[] args) {
  3. Properties properties=new Properties();
  4. properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
  5. properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
  6. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  7. KafkaProducer producer=new KafkaProducer(properties);
  8. ProducerRecord record=new ProducerRecord("test1","这是一条消息");
  9. producer.send(record);
  10. producer.close();
  11. }
  12. }

消费者

  1. public class SimpleKafkaConsumer {
  2. public static void main(String[] args) {
  3. Properties properties=new Properties();
  4. properties.setProperty("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
  5. properties.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
  6. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  7. //消费者组
  8. properties.setProperty("group.id","group1");
  9. KafkaConsumer consumer=new KafkaConsumer(properties);
  10. //订阅topic
  11. consumer.subscribe(Arrays.asList("test1"));
  12. while (true){
  13. //拉取数据
  14. ConsumerRecords poll=consumer.poll(100);
  15. ((ConsumerRecords) poll).forEach(
  16. data->{
  17. System.out.println(((ConsumerRecord)data).value());
  18. }
  19. );
  20. }
  21. }
  22. }

三、高级特性

生产者特性

生产者-确认模式

  • acks=0 :只发送不管有没有写入到broker
  • acks=1:只写入到leader就认为成功
  • acks=-1/all:要求ISR列表里所有follower都同步过去,才算成功

将acks设置为-1就一定能保证消息不丢吗?

答:不是的。如果partition只有一个副本,也就是光有leader没有follower,那么宕机了消息一样会丢失。所以至少也要设置2个及以上的副本才行。

另外,要提高数据可靠性,设置acks=-1的同时,也要设置min.insync.replicas(最小副本数,默认1)

生产者-同步发送

  1. public void syncSend() throws ExecutionException, InterruptedException {
  2. Properties properties=new Properties();
  3. properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
  4. properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
  5. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  6. KafkaProducer producer=new KafkaProducer(properties);
  7. ProducerRecord record=new ProducerRecord("test1","这是一条消息");
  8. Future future = producer.send(record);
  9. //同步发送消息方法1
  10. Object o = future.get();
  11. //同步发送消息方法2
  12. producer.send(record);
  13. producer.flush();
  14. producer.close();
  15. }

生产者-异步发送

  1. public void asyncSend(){
  2. Properties properties=new Properties();
  3. properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
  4. properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
  5. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  6. //生产者在发送批次之前等待更多消息加入批次的时间
  7. properties.setProperty("linger.ms","1");
  8. properties.setProperty("batch.size","20240");
  9. KafkaProducer producer=new KafkaProducer(properties);
  10. ProducerRecord record=new ProducerRecord("test1","这是一条消息");
  11. //异步发送方法1
  12. producer.send(record);
  13. //异步发送方法2
  14. producer.send(record,((metadata, exception) -> {
  15. if(exception==null){
  16. System.out.println("record="+record.value());
  17. }
  18. }));
  19. }

生产者-顺序保证

同步请求发送+broker只能一个请求一个请求的接

  1. public void sequenceGuarantee(){
  2. Properties properties=new Properties();
  3. properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
  4. properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
  5. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  6. //生产者在收到服务器响应之前可以发送多少个消息,保证一个一个的发
  7. properties.setProperty("max.in.flight.requests.per.connection","1");
  8. KafkaProducer producer=new KafkaProducer(properties);
  9. ProducerRecord record=new ProducerRecord("test1","这是一条消息");
  10. //同步发送
  11. producer.send(record);
  12. producer.flush();
  13. producer.close();
  14. }

生产者-消息可靠性传递

事务+幂等

这里的事务就是,发送100条消息,如果其中报错了,那么所有的消息都不能被消费者读取。

  1. public static void transaction(){
  2. Properties properties=new Properties();
  3. properties.setProperty("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
  4. properties.setProperty("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
  5. //重试次数
  6. properties.setProperty("retries","3");
  7. properties.setProperty("bootstrap.servers","192.168.157.200:9092");
  8. //生产者发送消息幂等,此时会默认把acks设置为all
  9. properties.setProperty("enable.idempotence","true");
  10. //事务id
  11. properties.setProperty("transactional.id","tx0001");
  12. ProducerRecord record=new ProducerRecord("test1","这是一条消息");
  13. KafkaProducer producer=new KafkaProducer(properties);
  14. try {
  15. producer.initTransactions();
  16. producer.beginTransaction();
  17. for (int i = 0; i < 100; i++) {
  18. producer.send(record,(recordMetadata, e) -> {
  19. if(e!=null){
  20. producer.abortTransaction();
  21. throw new KafkaException("send error"+e.getMessage());
  22. }
  23. });
  24. }
  25. producer.commitTransaction();
  26. } catch (ProducerFencedException e) {
  27. producer.abortTransaction();
  28. e.printStackTrace();
  29. }
  30. producer.close();
  31. }

消费者特性

消费者-消费者组

每个消费者组都记录了一个patition的offset,一个partition只能被一个消费者组消费。

如图,一个Topic有4个partition,分别在两个broker上。

对于消费者组A来说,他有两个消费者,所以他里面一个消费者消费2个partition。而对于消费者组B,他有4个消费者,所以一个消费者消费1个partition.

消费者-offset同步提交

  1. void commitSyncReceive() throws InterruptedException {
  2. Properties props = new Properties();
  3. props.put("bootstrap.servers", "49.234.77.60:9092");
  4. props.put("group.id", "group_id");
  5. //关闭自动提交
  6. props.put("enable.auto.commit", "false");
  7. props.put("auto.commit.interval.ms", "1000");
  8. props.put("session.timeout.ms", "30000");
  9. props.put("max.poll.records", 1000);
  10. props.put("auto.offset.reset", "earliest");
  11. props.put("key.deserializer", StringDeserializer.class.getName());
  12. props.put("value.deserializer", StringDeserializer.class.getName());
  13. KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
  14. consumer.subscribe(Arrays.asList(TOPIC));
  15. while (true){
  16. ConsumerRecords<String, String> msgList=consumer.poll(1000);
  17. for (ConsumerRecord<String,String> record:msgList){
  18. System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
  19. }
  20. //同步提交,当前线程会阻塞直到 offset 提交成功
  21. consumer.commitSync();
  22. }
  23. }

消费者-异步提交

  1. void commitAsyncReceive() throws InterruptedException {
  2. Properties props = new Properties();
  3. props.put("bootstrap.servers", "49.234.77.60:9092");
  4. props.put("group.id", "group_id");
  5. props.put("enable.auto.commit", "false");
  6. props.put("auto.commit.interval.ms", "1000");
  7. props.put("session.timeout.ms", "30000");
  8. props.put("max.poll.records", 1000);
  9. props.put("auto.offset.reset", "earliest");
  10. props.put("key.deserializer", StringDeserializer.class.getName());
  11. props.put("value.deserializer", StringDeserializer.class.getName());
  12. KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
  13. consumer.subscribe(Arrays.asList(TOPIC));
  14. while (true){
  15. ConsumerRecords<String, String> msgList=consumer.poll(1000);
  16. for (ConsumerRecord<String,String> record:msgList){
  17. System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
  18. }
  19. //异步提交
  20. consumer.commitAsync(new OffsetCommitCallback() {
  21. @Override
  22. public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
  23. if(e!=null){
  24. System.err.println("commit failed for "+map);
  25. }
  26. }
  27. });
  28. }
  29. }

消费者-自定义保存offset

  1. void commitCustomSaveOffest() throws InterruptedException {
  2. Properties props = new Properties();
  3. props.put("bootstrap.servers", "49.234.77.60:9092");
  4. props.put("group.id", "group_id");
  5. props.put("enable.auto.commit", "false");
  6. props.put("auto.commit.interval.ms", "1000");
  7. props.put("session.timeout.ms", "30000");
  8. props.put("max.poll.records", 1000);
  9. props.put("auto.offset.reset", "earliest");
  10. props.put("key.deserializer", StringDeserializer.class.getName());
  11. props.put("value.deserializer", StringDeserializer.class.getName());
  12. KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props);
  13. consumer.subscribe(Arrays.asList(TOPIC), new ConsumerRebalanceListener() {
  14. //调用时机是Consumer停止拉取数据后,Rebalance开始之前,我们可以手动提交offset
  15. @Override
  16. public void onPartitionsRevoked(Collection<TopicPartition> collection) {
  17. }
  18. //调用时机是Rebalance之后,Consumer开始拉取数据之前,我们可以在此方法调整offset
  19. @Override
  20. public void onPartitionsAssigned(Collection<TopicPartition> collection) {
  21. }
  22. });
  23. while (true){
  24. ConsumerRecords<String, String> msgList=consumer.poll(1000);
  25. for (ConsumerRecord<String,String> record:msgList){
  26. System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
  27. }
  28. consumer.commitAsync(new OffsetCommitCallback() {
  29. @Override
  30. public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
  31. if(e!=null){
  32. System.err.println("commit failed for "+map);
  33. }
  34. }
  35. });
  36. }
  37. }

四、SpringBoot整合Kafka

  1. 引入依赖
  1. <dependency>
  2. <groupId>org.springframework.kafka</groupId>
  3. <artifactId>spring-kafka</artifactId>
  4. </dependency>
  1. 配置

  1. #kafka
  2. spring.kafka.bootstrap-servers=192.168.157.200:9092
  3. # 发生错误后,消息重发的次数
  4. spring.kafka.producer.retries=0
  5. spring.kafka.producer.batch-size=16384
  6. # 设置生产者内存缓冲区的大小。
  7. spring.kafka.producer.buffer-memory=33554432
  8. spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
  9. spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
  10. spring.kafka.producer.acks=1
  11. #消费者
  12. #自动提交的时间间隔
  13. spring.kafka.consumer.auto-commit-interval=1S
  14. # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
  15. # latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
  16. # earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
  17. spring.kafka.consumer.auto-offset-reset=earliest
  18. spring.kafka.consumer.enable-auto-commit=false
  19. spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
  20. spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
  21. # 在侦听器容器中运行的线程数。
  22. spring.kafka.listener.concurrency=5
  23. #listner负责ack,每调用一次,就立即commit
  24. spring.kafka.listener.ack-mode=manual_immediate
  25. spring.kafka.listener.missing-topics-fatal=false
  1. producer
  1. @Component
  2. public class MyKafkaProducer {
  3. @Autowired
  4. private KafkaTemplate<String,Object> kafkaTemplate;
  5. public void send(String topic,Object object){
  6. ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, object);
  7. future.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
  8. @Override
  9. public void onFailure(Throwable ex) {
  10. System.out.println("发送消息失败"+ex.getMessage());
  11. }
  12. @Override
  13. public void onSuccess(SendResult<String, Object> result) {
  14. System.out.println("发送消息成功"+result);
  15. }
  16. });
  17. }
  1. consumer
  1. @Component
  2. public class MyKafkaConsumer {
  3. @KafkaListener(topics = "test1",groupId = "group_test")
  4. public void consumer(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic){
  5. Optional message = Optional.ofNullable(record.value());
  6. if (message.isPresent()) {
  7. Object msg = message.get();
  8. System.out.println("group_test 消费了: Topic:" + topic + ",Message:" + msg);
  9. ack.acknowledge();
  10. }
  11. }
  12. @KafkaListener(topics = "test1",groupId = "group_test2")
  13. public void consumer2(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic){
  14. Optional message = Optional.ofNullable(record.value());
  15. if (message.isPresent()) {
  16. Object msg = message.get();
  17. System.out.println("group_test2 消费了: Topic:" + topic + ",Message:" + msg);
  18. ack.acknowledge();
  19. }
  20. }
  21. }
  1. 测试

超详细kafka教程来啦的更多相关文章

  1. VMware虚拟机下安装CentOS7.0超详细图文教程

    1.本文说明: 官方的第一个文本档案.也就是0_README.txt,大概意思是这样(渣翻译,但是大概意思还是有的). CentOS-7.0-1406-x86_64-DVD.iso:这个镜像(DVD ...

  2. MySql5.6 Window超详细安装教程

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录 一.安装包准备二.开始安装三.验证安装四.客户端工具 一.安装包准备 1.下载MySql ...

  3. MySql5.6Window超详细安装教程(msi 格式的安装)

    转自:红黑联盟  http://www.2cto.com/database/201506/409821.html 一.安装包准备 1.下载MySql5.6 http://www.mysql.com/ ...

  4. 在Ubuntu下进行XMR Monero(门罗币)挖矿的超详细图文教程

    大家都知道,最近挖矿什么的非常流行,于是我也在网上看了一些大神写的教程,以及跟一些大神请教过如何挖矿,但是网上的教程都感觉写得不够详细,于是今天我这里整理一个教程,希望能够帮到想要挖矿的朋友. 首先, ...

  5. 超详细实战教程丨多场景解析如何迁移Rancher Server

    本文转自Rancher Labs 作者介绍 王海龙,Rancher中国社区技术经理,负责Rancher中国技术社区的维护和运营.拥有6年的云计算领域经验,经历了OpenStack到Kubernetes ...

  6. 【建议收藏】Redis超详细入门教程大杂烩

    写在前边 Redis入门的整合篇.本篇也算是把2021年redis留下来的坑填上去,重新整合了一翻,点击这里,回顾我的2020与2021~一名大二后台练习生 NoSQL NoSQL(NoSQL = N ...

  7. 最新MATLAB R2021b超详细安装教程(附完整安装文件)

    摘要:本文详细介绍Matlab R2021b的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用. ...

  8. 最新MATLAB R2020b超详细安装教程(附完整安装文件)

    摘要:本文详细介绍Matlab R2020b的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用. ...

  9. MATLAB R2019b超详细安装教程(附完整安装文件)

    摘要:本文详细介绍Matlab的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用.本文目录如首页 ...

随机推荐

  1. python中进程详解

    1:pdb调试:基于命令行的调试工具,非常类似gnu和gdb调试,以下是常用的调试命令: 可以python -m pdb xxx.py(你的py文件名)进入命令行调试模式 命令 简写命令 作用 bea ...

  2. linux 20个常用命令

    一.文件和目录 1. cd命令 (它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径) cd /home    进入 '/ home' 目录 cd ..       ...

  3. selenium元素定位之 动态id, class元素定位

    1.直接进入正题 如下图, 有些元素每次进入都会刷新, 造成元素无法重复定位 怎么办? "xpath部分属性值"定位方法可以帮到我们 1.包含属性定位 driver.find_el ...

  4. 第3篇-CallStub新栈帧的创建

    在前一篇文章 第2篇-JVM虚拟机这样来调用Java主类的main()方法  中我们介绍了在call_helper()函数中通过函数指针的方式调用了一个函数,如下: StubRoutines::cal ...

  5. Golang语言系列-17-Gin框架

    Gin框架 Gin框架简介 package main import ( "github.com/gin-gonic/gin" "io" "net/ht ...

  6. CVE-2021-21972 vSphere Client RCE复现,附POC & EXP

    漏洞简介 vSphere 是 VMware 推出的虚拟化平台套件,包含 ESXi.vCenter Server 等一系列的软件.其中 vCenter Server 为 ESXi 的控制中心,可从单一控 ...

  7. 说说XXE漏洞那些事

    想不起来写点啥了,又是摸鱼的一天,看了一些红队大佬们整理的资料,非常精彩,于是一个咸鱼翻身先选了一些简单的小点来写一写个人的感想(后续会继续更新其他内容) 不能说写的是技术分享,因为师傅们的文章珠玉在 ...

  8. 内网探测之SPN服务扫描及相关利用

    在写下一个大块之前,补充一些小知识点,也没啥新东西 0x01简介 如果常规扫描服务,结果不理想,非常GG,可以考虑使用SPN进行服务扫描,这是为了借助Kerberos的正常查询行为(向域控发起LDAP ...

  9. SQL 练习40

    按照出生日期来计算学生的年龄信息 IF OBJECT_ID('GetStudentAge','FN') IS NOT NULL DROP FUNCTION GetStudentAge GO CREAT ...

  10. Java小题,通过JNI调用本地C++共享库中的对应方法实现杨辉三角的绘制

    1.在Eclipse中配置Javah,配置如下 位置是你javah.exe在你电脑磁盘上的路径 位置:C:\Program Files\Java\jdk1.8.0_112\bin\javah.exe ...