Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时处理大量数据
以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、Storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
基本概念
Kafka借鉴了JMS规范的思想,但是并没有完全遵循JMS规范,JMS规范
是一种旨在单点到单点消息传递的传统消息队列,Kafka则是一种分布式的发布订阅的消息系统,用于支持数据管道、流式处理和大规模数据集的处理等场景。
下面是消息(Message)相关术语:
名称 | 解释 |
Broker | 消息中间件处理节点,一个Kafka节点就是一个broker,一个或者多个Broker可以组成一个Kafka集群 |
Topic | Kafka根据topic对消息进行归类,发布到Kafka集群的每条消息都需要指定一个topic |
Producer | 消息生产者,向Broker发送消息的客户端 |
Consumer | 消息消费者,从Broker读取消息的客户端 |
ConsumerGroup | 每个Consumer属于一个特定的Consumer Group,一条消息可以被多个不同的Consumer Group消费,但是一个Consumer Group中只能有一个Consumer能够消费该消息 |
Partition | 物理上的概念,一个topic可以分为多个partition,每个partition内部消息是有序的 |
producer通过网络发送消息到Kafka集群,然后consumer来进行消费,服务端(brokers)和客户端(producer、consumer)之间通信通过TCP协议来完成 |
Kafka使用场景
- 日志收集:收集各种服务的log,再通过kafka发放给各种消费者,如hadoop、Hbase、Solr等
- 消息系统:缓存消息,解耦生产者和消费者
- 用户活动跟踪:用户活动(浏览网页、搜索等)信息发送到kafka的topic中,然后装载到hadoop、数据仓库中做离线分析和挖掘等
- 监控运营指标
基本使用
Kafka是用Scala语言开发的,运行在JVM上,因此在安装Kafka之前需要先安装JDK。
yum install java-1.8.0-openjdk* -y
安装zookeeper
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
tar -zxvf apache-zookeeper-3.5.8-bin.tar.gz
cd apache-zookeeper-3.5.8-bin
cp conf/zoo_sample.cfg conf/zoo.cfg
# 启动zookeeper
bin/zkServer.sh start
bin/zkCli.sh
ls / #查看zk的根目录相关节点
安装kafka
wget https://archive.apache.org/dist/kafka/2.4.1/kafka_2.11-2.4.1.tgz # 2.11是scala的版本,2.4.1是kafka的版本
tar -xzf kafka_2.11-2.4.1.tgz
cd kafka_2.11-2.4.1
修改配置文件
#broker.id属性在kafka集群中必须要是唯一
broker.id=0
#kafka部署的机器ip和提供服务的端口号
listeners=PLAINTEXT://192.168.65.60:9092
#kafka的消息存储文件
log.dir=/usr/local/data/kafka-logs
#kafka连接zookeeper的地址
zookeeper.connect=localhost:2181
# 消息保存多长时间,默认一周
log.retention.hours=168
server.properties核心配置详解:
Property | Default | Description |
broker.id | 0 | 每个broker都可以用一个唯一的非负整数id进行标识;这个id可以作为broker的“名字”,你可以选择任意你喜欢的数字作为id,只要id是唯一的即可。 |
log.dirs | /tmp/kafka-logs | kafka存放数据的路径。这个路径并不是唯一的,可以是多个,路径之间只需要使用逗号分隔即可;每当创建新partition时,都会选择在包含最少partitions的路径下进行。 |
listeners | PLAINTEXT://192.168.65.60:9092 | server接受客户端连接的端口,ip配置kafka本机ip即可 |
zookeeper.connect | localhost:2181 | zooKeeper连接字符串的格式为:hostname:port,此处hostname和port分别是ZooKeeper集群中某个节点的host和port;zookeeper如果是集群,连接方式为 hostname1:port1, hostname2:port2, hostname3:port3 |
log.retention.hours | 168 | 每个日志文件删除之前保存的时间。默认数据保存时间对所有topic都一样。 |
num.partitions | 1 | 创建topic的默认分区数 |
default.replication.factor | 1 | 自动创建topic的默认副本数量,建议设置为大于等于2 |
min.insync.replicas | 1 | 当producer设置acks为-1时,min.insync.replicas指定replicas的最小数目(必须确认每一个repica的写数据都是成功的),如果这个数目没有达到,producer发送消息会产生异常 |
delete.topic.enable | false | 是否允许删除主题 |
启动zookeeper
启动kafka
# 启动kafka,运行日志在logs目录的server.log文件里
bin/kafka-server-start.sh -daemon config/server.properties #后台启动,不会打印日志到控制台
或者用
bin/kafka-server-start.sh config/server.properties &
kafka信息注册到zookeeper中
基本操作
# 创建主题,
bin/kafka-topics.sh --create --zookeeper 192.168.65.60:2181 --replication-factor 1 --partitions 1 --topic test
# 查看主题
bin/kafka-topics.sh --list --zookeeper 192.168.65.60:2181
# 删除主题
bin/kafka-topics.sh --delete --topic test --zookeeper 192.168.65.60:2181
# 发送消息
bin/kafka-console-producer.sh --broker-list 192.168.65.60:9092 --topic test
# 消费消息
bin/kafka-console-consumer.sh --bootstrap-server 192.168.65.60:9092 --topic test
如果想要消费之前的消息可以通过--from-beginning参数指定,如下命令:
bin/kafka-console-consumer.sh --bootstrap-server 192.168.65.60:9092 --from-beginning --topic test
# 消费多主题
bin/kafka-console-consumer.sh --bootstrap-server 192.168.65.60:9092 --whitelist "test|test-2"
# 单播,queue模式,在同一消费者组中,一个消息只能被一个消费者消费
bin/kafka-console-consumer.sh --bootstrap-server 192.168.119.134:9092 --consumer-property group.id=testGroup --topic test
# 多播,pub/sub,一个消息可以被不同消费者组中的一个消费者消费
bin/kafka-console-consumer.sh --bootstrap-server 192.168.119.134:9092 --consumer-property group.id=testGroup-2 --topic test
# 查看消费者组
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.119.134:9092 --list
# 查看消费者组当前消费的偏移量,偏移量的记录与组对应,与单个消费者无关
bin/kafka-consumer-groups.sh --bootstrap-server 192.168.119.134:9092 --describe --group testGroup
current-offset:当前消费组的已消费偏移量
log-end-offset:主题对应分区消息的结束偏移量(HW)
lag:当前消费组未消费的消息数
每个消费者会维护一个消费的偏移量,下次启动时接着记录的偏移量重新消费,消息默认保存一周(可以通过配置文件修改)
主题Topic和消息日志
Topic
是逻辑上的队列,Partition
是物理上的队列,一个Topic可以分成多个分区(partition)日志文件存储在不同的broker上,分为多个分区是为了避免数据量过大导致队列太长
,单台机器无法存储,有了分区后可以实现分布式存储;不同的分区也会有单独的消费者,提高消费速度
分区(partition)是一个有序的message序列,每个message都有一个唯一的offset,保存在commit log
日志文件中,每个消费者都是基于自己在commit log中的消费进度offset
来工作的,offset由消费者自己维护,可以指定offset重复消费某些消息或者跳过某些消息
kafka的性能与保留的消息数据量大小没有关系,因此保存大量的数据消息日志信息不会有什么影响。
创建多分区主题
bin/kafka-topics.sh --create --zookeeper 192.168.119.134:2181 --replication-factor 1 --partitions 2 --topic test1
查看topic详细信息
bin/kafka-topics.sh --describe --zookeeper 192.168.119.134:2181 --topic test1
Kafka集群
在kafka中,一个单独的broker代表kafka集群中的一个节点,要增加kafka集群中的节点数量,可以多启动几个broker实例
集群搭建 1、复制配置文件
cp config/server.properties config/server-1.properties
cp config/server.properties config/server-2.properties
2、修改配置文件内容
#保证唯一
broker.id=1
listeners=PLAINTEXT://192.168.65.60:9093
log.dir=/usr/local/data/kafka-logs-1
#kafka连接zookeeper的地址,要把多个kafka实例组成集群,对应连接的zookeeper必须相同
zookeeper.connect=192.168.65.60:2181
zookeeper注册成功
3、创建3备份,2分区Topic
bin/kafka-topics.sh --create --zookeeper 192.168.119.134:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic
查看Topic,每一行表示一个partition信息
leader
:负责处理partition
的所有读写请求,不同分区的主分区分在不同机器上,保证容灾。Replicas
:表示当前的partition在哪几个broker上备份Isr
: 是Replicas
的子集,表示当前还存活的,已同步备份了该partition的节点
每个分区对应的replica日志文件
集群消费
Leader
处理所有针对这个partition的读写请求,follower被动复制Leader的结果,不提供读写;如果leader失效,其中的一个follower会自动变成新的leader
单播
:多个消费者在同一个消费者组
下
多播
:多个消费者在不同的消费者组
下
一个partirion只能被一个消费者组中的一个消费者
消费,如果消费者组中的消费者数量
大于分区数
,多出来的消费者消费不到消息
,但是一个消费者可以消费多个partition
顺序消费
:kafka只在partition范围内保证顺序消费,不能在一个Topic中的多个partition保证总的消费顺序性,如果要实现总的顺序消费,可以把分区数设置为1,消费者设置为1,会影响性能
JAVA常用API
生产者
Properties props = new Properties();
// 发出消息leader分区持久到log后,再发送下一条
props.put(ProducerConfig.ACKS_CONFIG, "1");
//重试机制,重试3次,每次间隔300ms,如果上一次的成功返回因为网络抖动导致超过300ms,会导致消息重复发送
props.put(ProducerConfig.RETRIES_CONFIG, 3);
props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 300);
//发送消息的本地缓冲区,32M,kafka线程从缓冲区取数据批量发送到broker,一次发16k
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
消费者
Properties props = new Properties();
// 自动提交
props.put("enable.auto.commit", "true");
//自动提交间隔,1s
props.put("auto.commit.interval.ms", "1000");
//手动同步提交,可以消费一条提交一次,也可以最后批量提交
consumer.commitSync()
//异步提交
consumer.commitAsync(callback)
//指定分区消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
//回溯消费,消费者需要指定从开始消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
//指定offset消费,指定从offSet=10开始消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);
//一次poll最大拉多少条消息,数值大小可以根据消费者处理速度决定
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 50);
// 消费者poll时间间隔30s,如果消费者处理时间超过30s,服务端会踢掉这个消费者
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000);
//消费者给broker发送心跳时间间隔1s,保持连接
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000);
// broker 10s 感知不到心跳,就踢出该消费者,触发rebalance
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000);
//新的消费者组默认从 latest消费,设置earliest会从topic的第一条消息消费,随后开始接着offset消费;consumer.seekToBeginning(每次都从头开始消费)
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");