1. 引言

  最近使用Kafka做消息队列时,完成了基本的消息发送与接收,已上线运行。一方面防止出现Bug时自己不能及时定位问题,一方面网上的配置可能还可以更加优化,决定去了解下Kafka。

2. 配置

  kafka基本配合zookeeper使用,网上有很多关于liunx上搭建zookeeper+kafka集群的实例,此处不再阐述。贴一个我以前搭建过的实例。https://blog.csdn.net/hudyang/article/details/80419214

3. 理解Kafka

3.1 kafka是什么

  个人理解:kafka是一个消息中间件,用于各个系统中消息的传递。例如:A系统是平台主入口,每天产生大量日志,A系统不可能自己去处理这些冗杂的事务,于是发送给Kafka。B系统很空闲,让他来接收Kafka数据,于是从kafka取数据,然后记录日志;在举个例子,在微服务中,A系统是用户模块,B系统是权限模块。创建用户时也要创建用户权限。于是在A系统在创建用户时,向kafka中发送一条指定消息,B系统收到这条消息后,于是开始创建用户权限。是不是很好理解-----

  总结一句话:kafka是一个分布流媒体平台

  流媒体平台有以下三个关键功能:

  • 发布和订阅记录流,类似于消息队列或企业消息传递系统。
  • 以容错的持久方式存储记录流。
  • 记录发生时处理流。

3.2 相关概念

  主题是Kafka最核心的抽象模块,用于连接生产者和消费者。生产者发送消息到指定的主题,消费者订阅该主题的消息,于是完成基本的消息传递流程。下面为kafka建立主题的code:

./kafka-topics.sh --create --zookeeper 10.113.56.68: --replication-factor  --partitions  --topic testKafkas
// --zookeeper zookeeper集群地址
// --replication-factor 副本
// --parttitions 分区
// --topic 主题名称

  分区的概念:分区是主题的横向拓展的实现。传统的消息队列相当于一个列表,生产者将消息放入列表中,然后消费者从列表中取出消息。吞吐量受到限制。如果使用多线程去取消息,就会造成异步操作,有可能出现数据消费混乱。Kafka中使用分区概念(相当于将一个大的队列分为成多个队列)每个分区由指定的消费者去消费,这样能极大的提高kafka的吞吐量,并且效率提高。

  

  每个分区都是一个有序的,不可变的记录序列,不断附加到结构化的提交日志中。分区中的记录每个都被分配一个称为偏移的顺序ID号(offSet),它唯一地标识分区中的每个记录。如上图,一个topic的消息均匀分布在partition0,partition1,partition2中。消费者写3条消息到topic上,分别记录在partition0的offset12节点,partition1的offset9节点,partition2的offset12节点。消费者消费时,会从partition的offset0节点开始,消费完成后,offset+1,下次消费时,从上次的offset开始进行消费。

  副本的概念:每个分区拥有多个副本,其中一个副本将被指定为主副本(leader replicas),其余的为跟随副本。所有的消息都会写入到主副本,所有的消息都从主要副本读取,其他的副本只需要保持于主副本同步即可。

  消费组概念:消费组比较难理解。消费组相当于一个指定消费者的聚合。

  如图,KafkaCluster相当于一个topic,topic有4个分区 P0,P1,P2,P3;P0,P3在服务器1上,P1,P2在服务器2上(此处可以忽略他们在哪个服务器);有2个消费组ConsumerGroupA,ConsumerGroupB,里面有C1-C6消费者,每个消费者都订阅了topic的消息。上文提过,一个分区的消息只能被同一消费组下面的一个消费者消费。于是,对于ConsumerGroupA来说,C1接收P0,P3的消息,C2接收P1,P2的消息(为什么这么分配,下文会进行)。对于ConsumerGroupB来说,C3-C6分别接收P0-P3的消息。此处还有个小点,即消费组中的消费者数量不要大于分区数量。以ConsumerGroupB为例,消费者和分区刚好一一对应,如果此时增加了一个消费者C7,那么C7接受不到任何分区的消息,因为一个分区只能对应同个消费组下的一个消费者。

3.3 补充

  描述了Kafka基本概念后,此处介绍另一个概念Rebalance(即分区分配给消费组中指定消费者的过程)

  

  如上,消费组中原来有2个consumer,consumerA对应主题A的0分区,主题B的1分区,主题C的0分区;consumerB对应主题A的1分区,主题B的0分区,主题C的1分区。此时,消费组新增一个consumerC,那么对应的分区就要相应的重新分配,这个过程就叫Rebalance。

  什么时候发生Rebalance?

  • 消费组中消费者新增,离开,崩溃都会触发Rebalance;
  • 订阅的主题增加或减少;
  • 订阅的主题分区数增加或减少;

  Rebalance引发的问题

  • Rebalance过程中,消费者不能进行消费;
  • 如果一个消费者消费一段消息后,没有提交确认就发生了Rebalance,那么此段消息还会分发给分配后的消费者,造成消息重复消费;

  Rebalance流程(简介)

  • 加入组请求(JoinGroup)=>作用在于选择Group Leader(消费组中的领导,用于制定分区分配方案)。
  • 同步组请求(SyncGroup)=>作用在于确定分区分配方案给Coordinator(对消费组进行管理的对象),把方案响应给所有Consumer。

  自己理解:consumer申请加入组,向Coordinator发送JoinGroup请求,Coordinator从重多consumer选择一个Group Leader(领导)。领导用于指定分区方案,即那个分区非配给哪个消费者。制定完成后,把分区方案给Coordinator,Coordinator然后告诉所有消费者:你负责的分区是哪个。

4. 实际案例

  自己用Kafka做了一个简易消息系统,完成基本的消息发送。

// 引入kafka相关jar包
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
import org.apache.kafka.clients.consumer.CommitFailedException;
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.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord; import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer; /**
* @Author xiabing5
* @Create 2019/8/1 19:34
* @Desc kafka工具类
**/
public class KafkaUtil { private static Producer<String, String> producer; private static KafkaConsumer<String, String> consumer; private static KafkaConsumer<String, String> consumer1;
static {
Properties producerProps = new Properties();
//必需的3个参数
producerProps.put("bootstrap.servers", "10.113.56.68:9093,10.113.56.68:9094,10.113.56.68:9092");
producerProps.put("key.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
producerProps.put("value.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
producer = new KafkaProducer<String, String>(producerProps); Properties consumerProps = new Properties();
consumerProps.put("bootstrap.servers", "10.113.56.68:9093,10.113.56.68:9094,10.113.56.68:9092");
consumerProps.put("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
consumerProps.put("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
consumerProps.put("group.id", "VoucherGroup");
//关闭自动提交offset
consumerProps.put("enable.auto.commit", "false");
//此处开启2个消费者进行消费
consumer = new KafkaConsumer<String, String>(consumerProps);
consumer1 = new KafkaConsumer<String, String>(consumerProps);
} /**
* @Author xiabing5
* @Create 2019/8/1 19:40
* @Desc 同步发送消息 topic 主题 value 消息内容
**/
public static void sendSync(String topic, String value) throws ExecutionException, InterruptedException {
producer.send(new ProducerRecord<String, String>(topic, value)).get();
} public static void consume(Consumer<String> c) {
// 订阅主题,可订阅多个主题
consumer.subscribe(Arrays.asList("testKafkas"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(10);
for (ConsumerRecord<String, String> record : records) {
System.out.println("A"+record);
c.accept(record.value());
}
try {
//同步手动提交offset
consumer.commitSync();
} catch (CommitFailedException e) {
System.out.println("Kafka消费者提交offset失败");
}
}
} public static void consume1(Consumer<String> c) {
// 订阅主题,可订阅多个主题
consumer1.subscribe(Arrays.asList("testKafkas"));
while (true) {
ConsumerRecords<String, String> records = consumer1.poll(10);
for (ConsumerRecord<String, String> record : records) {
System.out.println("A"+record);
c.accept(record.value());
}
try {
//同步手动提交offset
consumer1.commitSync();
} catch (CommitFailedException e) {
System.out.println("Kafka消费者提交offset失败");
}
}
} }
// 发送消息接口
public interface MessageService { // 发送消息
boolean sendMessage(String topic,String content); }
@Service
public class MessageServiceImpl implements MessageService { // 创建线程池
@PostConstruct
public void init() {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MessageConsumerThread-%d")
.setDaemon(true)
.build();
//ExecutorService executorService = Executors.newSingleThreadExecutor(threadFactory);
ExecutorService executorService = Executors.newFixedThreadPool(2,threadFactory);
MqMessageConsumerThread mqMessageConsumerThread= new MqMessageConsumerThread();
MqMessageConsumerThread1 mqMessageConsumerThread1= new MqMessageConsumerThread1();
executorService.execute(mqMessageConsumerThread);
executorService.execute(mqMessageConsumerThread1);
} // 自定义接收线程
private class MqMessageConsumerThread implements Runnable {
@Override
public void run() {
KafkaUtil.consume(consumerRecord -> {
System.out.println("A"+consumerRecord);
});
}
}
private class MqMessageConsumerThread1 implements Runnable {
@Override
public void run() {
KafkaUtil.consume1(consumerRecord -> {
System.out.println("B"+consumerRecord);
});
}
}
@Override
public boolean sendMessage(String topic, String content) {
try {
KafkaUtil.sendSync(topic,content);
// 执行业务逻辑
System.out.println("发送kafka消息成功");
}
catch (Exception e) {
// 发送失败
System.out.println("发送kafka消息失败");
// 失败重发
try {
KafkaUtil.sendSync(topic,content);
}catch (Exception e1) {
System.out.println("失败");
}
}
return true;
}
}
/**
* @Author xiabing5
* @Create 2019/9/19 20:17
* @Desc 容器初始化(模拟消息发送)
**/
@Component
public class InitApplication implements CommandLineRunner{ @Autowired
MessageService messageService; @Override
public void run(String... strings) throws Exception {
for (int i = 0; i < 10; i++) {
messageService.sendMessage("testKafkas",i+"");
}
}
}

  启动容器,会出现如下界面

5. 总结

  学习一项新技术,最好知道它实现的基本内涵,希望自己这个小白在每一次记录中成长;

  本文纯原创,写的不好或理解不到位的地方请多多指教;

Kafka理解的更多相关文章

  1. 关于Flume以及Kafka理解

  2. 5 kafka整合storm

    本博文的主要内容有 .kafka整合storm   .storm-kafka工程  .storm + kafka的具体应用场景有哪些? 要想kafka整合storm,则必须要把这个storm-kafk ...

  3. tech| kafka入门书籍导读

    J梳理了一下自己在入门 kafka 时读过的一些书, 希望能帮助到对 kafka 感兴趣的小伙伴. 涉及到的书籍: kafka 权威指南 Kafka: The Definitive Guide (ka ...

  4. 【转】快速理解Kafka分布式消息队列框架

     from:http://blog.csdn.net/colorant/article/details/12081909 快速理解Kafka分布式消息队列框架 标签: kafkamessage que ...

  5. 快速理解Kafka分布式消息队列框架

    作者:刘旭晖 Raymond 转载请注明出处 Email:colorant at 163.com BLOG:http://blog.csdn.net/colorant/ ==是什么 == 简单的说,K ...

  6. [转载] 快速理解Kafka分布式消息队列框架

    转载自http://blog.csdn.net/xiaolang85/article/details/18048631 ==是什么 == 简单的说,Kafka是由Linkedin开发的一个分布式的消息 ...

  7. kafka 日常使用和数据副本模型的理解

    kafka 日常使用和数据副本模型的理解 在使用Kafka过程中,有时经常需要查看一些消费者的情况.Kafka健康状况.临时查看.同步一些数据,又由于Kafka只是用来做流式存储,又没有像Mysql或 ...

  8. 关于kafka生产者相关监控指标的理解(未解决)

    关于生产者相关的监控指标含义的理解,希望大神帮忙进行确定下.     这边找了官网,看了网上各样的资料,但都无法帮我理解监控项目相关含义.     相关的监控项目是从jconsole获取的,并接入到了 ...

  9. kafka的简单理解

    经典组合: Flume+Kafka+Storm+HDFS/HBase Flume:分布式采集 Kafka:分布式缓存 Kafka简介: 一种分布式的.基于发布/订阅的消息系统(Scala编写的) Ka ...

随机推荐

  1. 花了两个星期,我终于把 WSGI 整明白了

    在 三百六十行,行行转 IT 的现状下,很多来自各行各业的同学,都选择 Python 这门胶水语言做为踏入互联网大门的第一块敲门砖,在这些人里,又有相当大比例的同学选择了 Web 开发这个方向(包括我 ...

  2. ServerManager.exe 0xc0000135 应用程序错误

    问题: 将 Windows Server 2016 .NET Framework移除. IIS卸载后, Server Manager.exe.事件查看器等都无法正常开启. 解决方案: 在运行中,输入C ...

  3. E4A碰到打开自动闪退又自动打开又闪退一直循环的问题

    E4A碰到打开自动闪退又自动打开又闪退一直循环的问题 这几天写了一个脚本,自己手机上,模拟器上,均测试没有问题,可以正常操作 发给另一个人,他那边居然没有一个设备能运行成功! 而且一直闪退,闪退后又打 ...

  4. 【技术博客】JWT的认证机制Django项目中应用

    开发组在开发过程中,都不可避免地遇到了一些困难或问题,但都最终想出办法克服了.我们认为这样的经验是有必要记录下来的,因此就有了[技术博客]. JWT的认证机制Django项目中应用 这篇技术博客基于软 ...

  5. 敏捷项目管理—Scrum框架总结

    Scrum中的角色 Scrum Master——项目负责人.项目经理 保护团队不受外界干扰,是团队的领导和推进者,负责提升 Scrum 团队的工作效率,控制 Scrum 中的“检视和适应”周期过程.与 ...

  6. linux驱动由浅入深系列:高通sensor架构实例分析之三(adsp上报数据详解、校准流程详解)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/76180915 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  7. php 加载 zip 文件

    header('Content-type: application/zip');header('Content-Disposition: attachment; filename="Quer ...

  8. JVM探究之 —— 垃圾回收(一)

    垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和 ...

  9. 一个android任务提醒程序

    需求: 运行建立多个提醒,每个提醒设置一个时间,到达指定时间后跳出提醒窗体 每个提醒有一个执行按钮,点击后保存执行记录,并且当天不再触发提醒窗体 提醒出现后如果任务还没执行,那么需要在30分钟后再次提 ...

  10. OSPF协议介绍及配置

    一.OSPF概述 回顾一下距离矢量路由协议的工作原理:运行距离矢量路由协议的路由器周期性的泛洪自己的路由表,通过路由的交互,每台路由器都从相邻的路由器学习到路由,并且加载进自己的路由表中,而对于这个网 ...