Kafka是一个分布式的基于发布/订阅模式的消息队列,主要应用于大数据实时处理领域。
Kafka三大好处:削峰、异步、解耦
Apache Kafka是分布式发布-订阅消息系统,在 kafka官网上对 kafka 的定义:一个分布式发布-订阅消息传递系统。 它最初由LinkedIn公司开发,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。Kafka是一种快速、可扩展的、设计内在就是分布式的,分区的和可复制的提交日志服务。
(1)高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个主题可以分多个分区, 消费组对分区进行消费操作;
(2)可扩展性:kafka集群支持热扩展;
(3)持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失;
(4)容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败);
(5)高并发:支持数千个客户端同时读写;
(6)支持实时在线处理和离线处理:可以使用Storm这种实时流处理系统对消息进行实时进行处理,同时还可以使用Hadoop这种批处理系统进行离线处理;
(1)日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如Hadoop、Hbase、Solr等;
(2)消息系统:解耦和生产者和消费者、缓存消息等;
(3)用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到Hadoop、数据仓库中做离线分析和挖掘;
(4)运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
(5)流式处理:比如spark streaming和storm;
(6)事件源;
可伸缩性:Kafka 的两个重要特性造就了它的可伸缩性。 Kafka 集群在运行期间可以轻松地扩展或收缩(可以添加或删除代理),而不会宕机。 可以扩展一个 Kafka 主题来包含更多的分区。由于一个分区无法扩展到多个代理,所以它的容量受到代理磁盘空间的限制。能够增加分区和代理的数量意味着单个主题可以存储的数据量是没有限制的。
容错性和可靠性:Kafka 的设计方式使某个代理的故障能够被集群中的其他代理检测到。由于每个主题都可以在多个代理上复制,所以集群可以在不中断服务的情况下从此类故障中恢复并继续运行。
吞吐量:代理能够以超快的速度有效地存储和检索数据。
为使用Apache Kafka消息传递系统或者大数据分析领域发展事业的专业人士做好准备,它将给你足够的理解如何使用Kafka集群。
Apache官网:http://apache.org
Kafka官网:http://kafka.apache.org
MQ传统应用场景--同步处理
MQ传统应用场景--异步处理
(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。
消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
(2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息)
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
1.为了方便扩展,并提高吞吐量,一个topic分为多个partiton
2.配合分区的设计,提出消费组的概念,组内每个消费者并行消费
3.为了提高可用性,为每个partition增加若干副本,类似NameNode HA
Producer 消息生产者,就是向kafka broker发消息的客户端;生产者即数据的发布者,该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后, broker将该消息追加到当前用于追加数据的segment文件中。生产者发送的消息,存储到一个partition 中,生产者也可以指定数据存储的partition。
Consumer 消息消费者,向kafka broker取消息的客户端;消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。
Consumer Group (CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
Topic 在Kafka中,使用一个类别属性来划分数据的所属类,划分数据的这个类称为topic。如果把Kafka看做 为一个数据库,topic可以理解为数据库中的一张表,topic的名字即为表名。
Partition topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使 用多个segment文件存储。partition中的数据是有序的,partition间的数据丢失了数据的顺序。如果 topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下, 需要将partition数目设为1。
Partition offset 每条消息都有一个当前Partition下唯一的64字节的offset,它指明了这条消息的起始位置。
Replicas of partition 副本是一个分区的备份。副本不会被消费者消费,副本只用于防止数据丢失,即消费者不从为follower 的partition中消费数据,而是从为leader的partition中读取数据。副本之间是一主多从的关系。
Broker Kafka 集群包含一个或多个服务器,服务器节点称为broker。broker存储topic的数据。如果某topic有 N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。如果某topic有N个 partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个 broker不存储该topic的partition数据。如果某topic有N个partition,集群中broker数目少于N个,那么 一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种 情况容易导致Kafka集群数据不均衡。
Follower 中选举出一个新的Leader。当Follower与 Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重 新创建一个Follower。
Zookeeper Zookeeper负责维护和协调broker。当Kafka系统中新增了broker或者某个broker发生故障失效时,由 ZooKeeper通知生产者和消费者。生产者和消费者依据Zookeeper的broker状态信息与broker协调数据 的发布和订阅任务。
AR(Assigned Replicas) 分区中所有的副本统称为AR。
ISR(In-Sync Replicas) 所有与Leader部分保持一定程度的副(包括Leader副本在内)本组成ISR。
OSR(Out-of-Sync-Replicas) 与Leader副本同步滞后过多的副本。
HW(High Watermark) 高水位,标识了一个特定的offset,消费者只能拉取到这个offset之前的消息。
LEO(Log End Offset) 即日志末端位移(log end offset),记录了该副本底层日志(log)中下一条消息的位移值。注意是下一条消 息!也就是说,如果LEO=10,那么表示该副本保存了10条消息,位移值范围是[0, 9]
其中:Linux环境下输入ifconfig不显示ens33的解决方法
依次执行了下面三行命令就ok了
systemctl stop NetworkManager 临时关闭
systemctl disable NetworkManager 永久关闭网络管理命令
systemctl start network.service 开启网络服务
首先需要安装Java环境,同时配置环境变量,步骤如下:
官网下载JDK,我的网盘链接往下面kafka安装那里翻
https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html
放到指定目录下解压,我自己虚拟机上的是jdk12.0.1
接下来要配置环境变量,修改profile文件。
sudo vi /etc/profile
插入下面的数据
export JAVA_HOME=/usr/java/jdk-12.0.1
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
export PATH=.:$JAVA_HOME/bin:$JRE_HOME/bin:$KE_HOME/bin:${MAVEN_HOME}/bin:$PATH
配置完环境,要重启一下虚拟机,才能生效
测试jdk
which java
java -version
Zookeeper是安装Kafka集群的必要组件,Kafka通过Zookeeper来实施对元数据信息的管理,包括集 群、主题、分区等内容。
同样在官网下载安装包到指定目录解压缩,步骤如下:
ZooKeeper官网:https://zookeeper.apache.org/
我这里将zookeeper放在/mnt/zookeeper文件夹下面,并且解压
我的zookeeper目录
修改Zookeeper的配置文件,首先进入安装路径conf目录,并将zoo_sample.cfg文件修改为 zoo.cfg,并对核心参数进行配置。 文件内容如下:
# The number of milliseconds of each tick
# zk服务器的心跳时间
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
# 投票选举新Leader的初始化时间
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
# do not use /tmp for storage, /tmp here is just # example sakes. # 数据目录 dataDir=/mnt/zookeeper/data
# 日志目录
dataLogDir=/mnt/zookeeper/log
# the port at which the clients will connect
# Zookeeper对外服务端口,保持默认
clientPort=2181
修改文件名称
sudo mv zoo_sample.cfg zoo.cfg
启动zookeeper
bin/zkServer.sh start
官网下载安装解压缩:http://kafka.apache.org/downloads
我这里给出一个网盘链接:
pan.baidu.com/s/11j24piAukP5doCJpAcORfg
eu9o
(1)解压,随意放一个目录,我这里放在/mnt/kafka(自己创建的文件夹)
(2)然后进入kafka目录下,解压
tar xzvf kafka_2.12-2.2.1.tgz
创建一个log文件夹
修改server.properties配置文件
[root@mo config]# vi server.properties
broker.id=0 表示broker的编号,如果集群中有多个broker,则每个broker的编号需要设置的不同
listeners=PLAINTEXT://0.0.0.0:9092 brokder对外提供的服务入口地址
advertised.listeners=PLAINTEXT://192.168.0.183:9092 --这里的ip为自己的ip地址
log.dirs=/mnt/kafka/log 设置存放消息日志文件的地址
zookeeper.connect=localhost:2181 Kafka所需Zookeeper集群地址
(3)进入kafka_2.12-2.2.1.tgz目录,执行该启动命令
./bin/kafka-server-start.sh config/server.properties &
(4)启动成功了
(5)启动成功之后,重新打开一个终端,验证启动情况
进入kafka
如图,执行jps -l
(1)首先创建一个主题
命令如下
bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic xiaomo --partitions 2 --replication-factor 1
其中
--zookeeper:指定了Kafka所连接的Zookeeper服务地址
--topic:指定了所要创建主题的名称
--partitions:指定了分区个数
--replication-factor:指定了副本因子
--create:创建主题的动作指令
(2)显示所有主题
bin/kafka-topics.sh --zookeeper localhost:2181 --list
(3)查看主题详情
bin/kafka-topics.sh --zookeeper localhost:2181 --describe --topic xiaomo
其中--describe 查看详情动作指令
(4)指定连接kafka集群地址和消费订阅的主题
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic xiaomo
--bootstrap-server 指定了连接Kafka集群的地址
--topic 指定了消费端订阅的主题
(5) 生产端发送消息
命令:
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic xiaomo
--broker-list 指定了连接的Kafka集群的地址
--topic 指定了发送消息时的主题
查看防火墙状态,并关掉防火墙
firewall-cmd --state --查看防火墙状态
systemctl stop firewalld.service
systemctl disable firewalld.service
3.2.1 创建工程
第一步
第二步
项目结构
给出pom.xml代码
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.2.RELEASE
cn.test1.kafka
test1-kafka
0.0.1-SNAPSHOT
test1-kafka
Demo project for Spring Boot
1.8
2.11
1.7.21
2.0.0
1.18.8
4.11
2.2.4
1.5.4
2.3.1
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.apache.kafka
kafka-clients
${kafka.version}
org.springframework.boot
spring-boot-maven-plugin
3.2.2 编写生产者ProducerFastStart代码
/**
* @program: test1-kafka
* @description: 创建生产者
* @author: WEN
* @create: 2020-08-03
**/
public class ProducerFastStart {
//kafka集群地址
public static final String brokerList = "192.168.0.183:9092";
//主题名称,我之前创建的是xiaomo
private static final String topic = "xiaomo";
public static void main(String[] args) {
//properties是配置bai文件,主要的作用是通过修改配置文件可du以方便的修改代码中的参数,实现不zhi用改class文件即可灵活变更参数
//解释:java运行中java文件会变成class文件,之后无法通过反编译找到原样的代码,这样的话,如果java类中某个参数变更,
// 就很难灵活的实现参数修改,这个时候properties 文件就能很灵活的实现配置,减少代码的维护成本和提高开发效率
Properties properties = new Properties();
//设置key序列化器
properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
//另外一种写法
//properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//设置重试次数
properties.put(ProducerConfig.RETRIES_CONFIG,10);
//设置值序列化器
properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
//设置集群地址
properties.put("bootstrap.servers",brokerList);
// kafkaProducer 线程安全
KafkaProducer producer = new KafkaProducer(properties);
ProducerRecord record = new ProducerRecord<>(topic,"kafka-demo","xiaomo");
try {
producer.send(record);
}catch (Exception e) {
e.printStackTrace();
}
//关闭
producer.close();
}
}
测试运行
因为之前已经在虚拟机上开启了消费者客户端,所以直接在虚拟机上查看接收的命令
3.2.3 编写消费者代码
/**
* @program: test1-kafka
* @description: 消息消费者
* @author: WEN
* @create: 2020-08-03
**/
public class ConsumerFastStart {
//kafka集群地址
private static final String brokerList = "192.168.0.183:9092"; //自己的虚拟机ip地址
//主题名称,我创建的是xiaomo
private static final String topic = "xiaomo";
//消费组
private static final String groupId = "group.demo";
public static void main(String[] args) {
Properties properties = new Properties();
//设置key序列化器
properties.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
//设置value
properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
//集群地址
properties.put("bootstrap.servers",brokerList);
//组ID
properties.put("group.id",groupId);
//kafka消费者,组id
KafkaConsumer consumer = new KafkaConsumer(properties);
consumer.subscribe(Collections.singletonList(topic));
while (true){
//一秒监听一次
ConsumerRecords records = consumer.poll(Duration.ofMillis(1000));
//循环输出
for (ConsumerRecord record : records){
System.out.println(record.value());
}
}
}
}
先运行消费者,再运行生产者
然后查看消费者打印
生产者代码
/**
* @program: test1-kafka
* @description: 创建生产者
* @author: WEN
* @create: 2020-08-03
**/
public class ProducerFastStart {
//kafka集群地址
public static final String brokerList = "192.168.0.183:9092";
//主题名称,我之前创建的是xiaomo
private static final String topic = "xiaomo";
public static void main(String[] args) {
//properties是配置bai文件,主要的作用是通过修改配置文件可du以方便的修改代码中的参数,实现不zhi用改class文件即可灵活变更参数
//解释:java运行中java文件会变成class文件,之后无法通过反编译找到原样的代码,这样的话,如果java类中某个参数变更,
// 就很难灵活的实现参数修改,这个时候properties 文件就能很灵活的实现配置,减少代码的维护成本和提高开发效率
Properties properties = new Properties();
//设置key序列化器
//properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
//另外一种写法
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//设置重试次数
properties.put(ProducerConfig.RETRIES_CONFIG,10);
//设置值序列化器
//properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
//设置集群地址
//properties.put("bootstrap.servers",brokerList);
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,brokerList);
// kafkaProducer 线程安全
KafkaProducer producer = new KafkaProducer(properties);
//封装了发送消息的对象
ProducerRecord record = new ProducerRecord<>(topic,"kafka-demo","hello,wuyifan");
try {
producer.send(record);
}catch (Exception e) {
e.printStackTrace();
}
//关闭
producer.close();
}
}
运行测试
消费者代码优化
/**
* @program: test1-kafka
* @description: 消息消费者
* @author: WEN
* @create: 2020-08-03
**/
public class ConsumerFastStart {
//kafka集群地址
private static final String brokerList = "192.168.0.183:9092"; //自己的虚拟机ip地址
//主题名称,我创建的是xiaomo
private static final String topic = "xiaomo";
//消费组
private static final String groupId = "group.demo";
public static void main(String[] args) {
Properties properties = new Properties();
//设置key序列化器
//properties.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//设置value
//properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class.getName());
//集群地址
//properties.put("bootstrap.servers",brokerList);
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,brokerList);
//组ID
//properties.put("group.id",groupId);
properties.put(ConsumerConfig.GROUP_ID_CONFIG,groupId);
//kafka消费者,消息的接收
KafkaConsumer consumer = new KafkaConsumer(properties);
consumer.subscribe(Collections.singletonList(topic));
while (true){
//一秒监听一次
ConsumerRecords records = consumer.poll(Duration.ofMillis(1000));
//循环输出
for (ConsumerRecord record : records){
System.out.println(record.value());
}
}
}
}
测试,先运行消费者,再运行生产者,然后查看消费者打印
可以
同步发送,修改生产者代码
/**
* @program: test1-kafka
* @description: 创建生产者
* @author: WEN
* @create: 2020-08-03
**/
public class ProducerFastStart {
//kafka集群地址
public static final String brokerList = "192.168.0.183:9092";
//主题名称,我之前创建的是xiaomo
private static final String topic = "xiaomo";
public static void main(String[] args) {
//properties是配置bai文件,主要的作用是通过修改配置文件可du以方便的修改代码中的参数,实现不zhi用改class文件即可灵活变更参数
//解释:java运行中java文件会变成class文件,之后无法通过反编译找到原样的代码,这样的话,如果java类中某个参数变更,
// 就很难灵活的实现参数修改,这个时候properties 文件就能很灵活的实现配置,减少代码的维护成本和提高开发效率
Properties properties = new Properties();
//设置key序列化器
//properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
//另外一种写法
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//设置重试次数
properties.put(ProducerConfig.RETRIES_CONFIG,10);
//设置值序列化器
//properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
//设置集群地址
//properties.put("bootstrap.servers",brokerList);
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,brokerList);
// kafkaProducer 线程安全
KafkaProducer producer = new KafkaProducer(properties);
//封装了发送消息的对象
ProducerRecord record = new ProducerRecord<>(topic,"kafka-demo","hello,zhangsanfeng");
try {
//同步发送
/*Future 表示一个任务的生命周期,并提供了相应的方法来判断任务是否已经完成或取消,
以及获取任务的结果和取消任务等。既然KafkaProducer.send()方法的返回值是一个Future
类型的对象,那么完全可以用Java 语言层面的技巧来丰富应用的实现,
比如使用Future 中的 get(longtimeout, TimeUnit unit)方法实现可超时的阻塞。*/
Future send = producer.send(record);
/*这样可以获取一个RecordMetadata 对象,在RecordMetadata 对象里包含了消息的一些元数据信息,
比如当前消息的主题、分区号、分区中的偏移量(offset)、时间戳等。
如果在应用代码中需要这些信息,则可以使用这个方式。
如果不需要,则直接采用producer.send(record).get()的方式更省事。*/
RecordMetadata recordMetadata = send.get();
System.out.println("topic:"+recordMetadata.topic());
System.out.println("partition:"+recordMetadata.partition());
System.out.println("offset:"+recordMetadata.offset());
}catch (Exception e) {
e.printStackTrace();
}
//关闭
producer.close();
}
}
运行测试
异步发送
//封装了发送消息的对象
ProducerRecord record = new ProducerRecord<>(topic,"kafka-demo","hello,zhangsanfeng");
try {
//异步发送
producer.send(record, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception == null) {
System.out.println("topic:"+metadata.topic());
System.out.println("partition:"+metadata.partition());
System.out.println("offset:"+metadata.offset());
}
}
});
}catch (Exception e) {
e.printStackTrace();
}
运行查看
参数配置:config/server.properties
zookeeper.connect
指明Zookeeper主机地址,如果zookeeper是集群则以逗号隔开,如:
172.6.14.61:2181,172.6.14.62:2181,172.6.14.63:2181
listeners
监听列表,broker对外提供服务时绑定的IP和端口。多个以逗号隔开,如果监听器名称不是一个安全的 协议,
listener.security.protocol.map也必须设置。
主机名称设置0.0.0.0绑定所有的接口,主机名称为 空则绑定默认的接口。
如:PLAINTEXT://myhost:9092,SSL://:9091 CLIENT://0.0.0.0:9092,REPLICATION://localhost:9093
broker.id
broker的唯一标识符,如果不配置则自动生成,建议配置且一定要保证集群中必须唯一,默认-1
log.dirs
日志数据存放的目录,如果没有配置则使用log.dir,建议此项配置。
message.max.bytes
服务器接受单个消息的最大大小,默认1000012 约等于976.6KB