持续学习,持续更新中。
kafka是使用gradle管理代码。
编译kafka源码
- 安装scala插件,要与idea版本一致。
使用idea远程连接下载速度较慢,这边可以在scala插件网站下载插件,要注意scala版本要与idea对应,在idea-settings-plugins里搜索scala查看版本 。然后将下载的zip(不用解压)放到idea的plugins文件夹内,重启idea在plugins中就有了。 - 安装gradle,配置环境变量
安装方法与maven一模一样。前往下载gradle,注意下载binary-only,下载完成后解压,配置环境变量即可。 - 在idea中配置gradle
File | Settings | Build, Execution, Deployment | Build Tools | Gradle
配置gradle的路径,一开始配置地下个Gradle | Use Gradle from 会操作,选择Specified location就可以了。 - 在idea中使用gradle编译源码
可以在Terimal中输入gradle idea
也可以在右边Gradle框内,点击右上角的刷新键编译。
这边我编译的时候遇到了几个问题:
1.没有下载scala插件
2.下载scala插件后,build.gradle文件中报错org.scoverage关联不到
报Could not create an instance of type org.scoverage.ScoverageExtension的错误
解决方法:
buildscript {
repositories {
mavenCentral()
jcenter()
}
apply from: file('gradle/buildscript.gradle'), to: buildscript
dependencies {
// For Apache Rat plugin to ignore non-Git files
classpath "org.ajoberstar:grgit:1.5.0"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
classpath 'org.scoverage:gradle-scoverage:2.1.0' //改成2.5.0后即可
}
}
3.可以成功编译,但是最后报错
在mvn_repository\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar not found
下载了一个slf4j-api-1.7.21.jar放在这个路径下后,问题解决。
重新编译后,终于成功了!!
开始撸kafka源码
之前看源码都会陷入一个怪圈,看到每一行代码都想要点进去看细节,然后就把自己给绕晕了。
形成看源码的习惯:
第一遍:看大致流程
第二遍:看重要细节
第三遍:理解源码,形成脑图
看源码的方法:
1.场景驱动:先写一个生产者代码,根据代码一步步分析
2.画图分析:看源码的时候理解分析,边画图。画图工具可以使用ProcessOn免费在线作图、实时协作,持流程图、思维导图、原型图、UML、网络拓扑图、组织结构图等
kafka主要的模块如下
模块名 | 描述 |
core | kafka核心模块 |
clients | 客户端模块 |
examples | 案例、事例存放的模块 |
首先让我们先来看看怎么使用kafka
通过Properties类配置kafka常用的配置(有什么配置可看KafkaProducer,然后点进去看ProducerConfig看配置信息。),然后将Properties放入KafkaProducer内,再使用kafkaProducer.send(ProducerRecord)发送数据。代码如下:
private final KafkaProducer<Integer, String> producer;
private final String topic;
private final Boolean isAsync;
/**
* 构造方法,初始化生产者对象
* @param topic 消息标题
* @param isAsync 是否异步发送消息
*/
public Producer(String topic, Boolean isAsync) {
/**
* todo:发送数据时的可以进行的配置,不一定需要全部配置
*/
Properties properties = new Properties();
//设置重试间隔时间,默认100ms
properties.put("retry.backoff.ms",200);
//设置生产者每隔一段多久获取元数据信息,默认5分钟
properties.put("metadata.max.age.ms",5*1000*60);
//设置生产者往服务端发送消息数据的大小,默认只有1M,基本不够用,所以这边设置10M
properties.put("max.request.size",10*1024*1024);
//设置缓冲区大小,默认是32M,根据情况也可以自行修改
properties.put("buffer.memory",32*1024*1024);
/**
* 配置压缩格式
* NONE GZIP SNAPPY LZ4
* 四种压缩格式
* 要设置小写
*/
properties.put("compression.type","gzip");
//设置延迟发送的时间ms,为0就不等待,一有数据就会直接发送,这种配置会降低程序的吞吐量
properties.put("linger.ms",0);
/**
* NetworkClient配置,不一定必须配置
*/
//设置网络连接最大空闲时间,如果空闲时间超过指定时间,则会断掉连接。默认9分钟
properties.put("connections.max.idle.ms",9*60*1000);
//最大容忍度,发送数据时,每个网络连接,最多容忍发送数据到broker没有响应的个数
//由于kafka有重试机制,所以若有多个消息发送,可能会导致消息乱序,这时需要设置为1,保证消息的顺序,但是会降低吞吐量
properties.put("max.in.flight.requests.per.connection",5);
//重新连接指定主机的时间
properties.put("reconnect.backoff.ms",50l);
//发送数据的缓冲区大小,默认128kb
properties.put("send.buffer.bytes",128*1024);
//接收数据的缓冲区大小,默认32kb
properties.put("receive.buffer.bytes",32*1024);
/**
* 0:只要数据发送到broker就不管了,不管有没有成功,有很大的数据丢失的风险
* 1:数据发送到broker后,要把数据放入leader partition后,才会响应,还是有数据丢失的风险
* -1或all:数据发送到broker后,要把数据放入leader partition后,还需要同步到follow partition后才会响应
*/
properties.put("acks","1");
//设置序列化
properties.setProperty("keySerializer","org.apache.kafka.common.serialization.StringSerializer");
properties.setProperty("valueSerializer","org.apache.kafka.common.serialization.StringSerializer");
//初始化KafkaProducer对象
//KafkaProducer最核心的一个类
producer = new KafkaProducer<>(props);
this.topic = topic;
this.isAsync = isAsync;
}
public void run() {
int messageNo = 1;
//一直往kafka发送数据
while (true) {
String messageStr = "Message_" + messageNo;
long startTime = System.currentTimeMillis();
//判断是否同步发送
//1为异步发送
//0为同步发送
if (isAsync) { // Send asynchronously
//异步发送
//这种方式,性能比较好
producer.send(new ProducerRecord<>(topic,
messageNo,
messageStr), new DemoCallBack(startTime, messageNo, messageStr));
} else { // Send synchronously
try {
//同步发送,得等这条信息完成后,才能进行下一条信息的发送
producer.send(new ProducerRecord<>(topic,
messageNo,
messageStr)).get();
System.out.println("Sent message: (" + messageNo + ", " + messageStr + ")");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
++messageNo;
}
}
看到代码就知道,KafkaProducer是最核心的类,看了内部内容后,大致的流程如下:
先看流程,再看细节,最后成脑图。
- 拦截数据,对key、value的数据进行序列化。
- 通过分区器(Partition)将每条消息进行分区后分发到每个topic不同的分区中。
- 将分区器添加到RecordAccumulator缓冲区中。
- 更新一下元数据信息
- NetworkClient获取到请求然后将每个请求交给kafkaChannel缓冲区,再发送给kafka集群
- kafka集群响应给Selector->Networkclient->Sender
- 然后执行绑定的回调函数