earliest
当各partition有消费者组已提交的offset时,从提交的offset开始消费;无提交的offset时,从起始开始消费
latest
当各partition下有消费者组已提交的offset时,从提交的offset开始消费;无提交的offset时,消费最新的该partition下的数据
broker producer consumer
topic 多个partition(区) 存储层面是append log 文件,消息都会被直接追加到log 文件的尾部,每条消息在文件中的位置称为offset(偏移量)!!! 为了存在多个broker上,扩容
在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1!!!!!
partitions 需要备份的个数(replicas),每个partition 将会被备份到多台机器上,以提高可用性
索引机制来存储offset!!! index指向多个段segment 多叉树!!! 几乎不允许对消息进行“随机读写”。
消息被消费,消息仍然不会被立即删除。日志文件将会根据broker 中的配置要求,保留一定的时间之后删除;比如log 文件保留2 天,那么两天后,文件会被清除,无论其中的消息是否被消费
kafka集群几乎不需要维护任何consumer和producer 状态信息,这些信息由zookeeper.offset 将会保存在zookeeper 中!!!!
传统的消息传递有两种方式: 队列方式(queuing)、发布-订阅(publish-subscribe)方式.
队列方式:一组消费者从机器上读消息,每个消息只传递给这组消费者中的一个。
分布-订阅方式:消息被广播到所有的消费者。Kafka提供了一个消费组(consumer group)的说法来概括这两种方式
kafka 中,一个partition 中的消息只会被group 中的一个consumer 消费!!!!每个group 中consumer 消息消费互相独立,不过一个consumer 可以消费多个partitions 中的消息!!
消费者的数量不能超过分区数 如果你想要消息是全局有序的,你可以设置主题只有一个分区,同时这意味着只能有一个消费者
Producer根据指定的partition方法(round-robin轮询、key-hash等)用户可以自己指定,消息发布到指定topic的partition里面,Consumer从kafka集群pull数据,并控制获取消息的offset
消费者的迭代器不会停止。如果当前没有消息,迭代器将阻塞直至有新的消息发布到该话题!!! 应该是有详细就会唤醒线程
当发布的消息数量达到设定值或者经过一定的时间后!!!,段文件真正写入磁盘中。写入完成后,消息公开给消费者。!!!!
broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数. log.retention.hours
broker会在zookeeper注册并保持相关的元数据(topic,partition信息等)更新。
吞吐量:数据磁盘持久化:消息不在内存中cache,直接写入到磁盘,充分利用磁盘的顺序读写性能,数据批量发送
负载均衡:roducer根据用户指定算法,将消息发送到指定的partition,每个partition有自己的replica,每个replica分布在不同的Broker节点上。一般不会在一个机器上,就是为了高可用
拉取系统:会持久化数据,broker没有内存压力,根据消费能力自主控制消息拉取速度!!!,根据自身情况自主选择消费模式,例如批量,重复消费,从尾端开始消费等
可扩展性:当需要增加broker结点时,新增的broker会向zookeeper注册
应用场景:消息队列 行为跟踪(产生消息消费者实时处理,或实时监控) 流处理
ACK一致性级别 如何保证发出的消息不丢失!!!! producer properties props.put("acks", "1")!!!
生产者ack=0(不需要ACK,至多一次), ack=all(leader和所有follows都写入成功,默认), ack=1(leader成功即可)。
消费者层面,kafka支持至多一次和至少一次两种模式 props.put("enable.auto.commit", "false"); consumer.commitSync();
offset下标自动提交其实在很多场景都不适用,因为自动提交是在kafka拉取到数据之后就直接提交 将提交方式改成false之后
需要手动提交只需加上这段代码
优化 发送方面,使用批量发送代替实时发送。 vm方面,默认kafka用的是cms gc,可以考虑g1垃圾回收期
消费慢:
kafka消息增量一般都堆积比较严重,消息数变化曲
消费者消费能力不行导致!! 多线程消费 批量消费
max.poll.interval.ms=300!!! 默认 业务时间不能超过这个 max.poll.records = 50 拉去条数减少 默认值为500!!! 之前业务太长,需要分析,直接拉取入库,后续后台分析
如果处理时间太久,超过了kafka的认定时间,kafka就会认为这个消费者挂了,从而踢掉重新分配一个消费者。处理方式可以是:1,一次不拉过多的数据。2,kafka认定消费者挂了的时间设置久点。!!!!!!!!!!!!!!!
#broker的全局唯一编号,不能重复
broker.id=01
#broker需要使用zookeeper保存meta数据
zookeeper.connect=master:2181,slave1:2181,slave2:2181
#segment文件保留的最长时间,超时将被删除
log.retention.hours=168
#partion buffer中,消息的条数达到阈值,将触发flush到磁盘
log.flush.interval.messages=10000
#消息buffer的时间,达到阈值,将触发flush到磁盘
log.flush.interval.ms=3000
#删除topic需要server.properties中设置delete.topic.enable=true否则只是标记删除
delete.topic.enable=true
replication不能多broker
./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic test
./kafka-topics.sh --list --zookeeper localhost:2181
Kafka集群保证数据的不丢失
生产者生产数据保证数据不丢失
消费者消费数据保证数据的不丢失
同步发送模式
producer.type=sync
request.required.acks=1
异步发送模式 https://www.cnblogs.com/wangzhuxing/p/10099894.html !!!!!
producer.type=async
request.required.acks=1
queue.buffering.max.ms=5000
queue.buffering.max.messages=10000
queue.enqueue.timeout.ms = -1
batch.num.messages=200
版本要一致客户端和kafka 消费者必须有groupId
找不到Host 配置hosts映射!!!
@KafkaListener(topics = {"test"},groupId = "test")
@Async
public void getTopic(ConsumerRecord<?, ?> record)
@KafkaListener(topics = "teemo", id = "consumer", containerFactory = "batchFactory")
public void listen(List<ConsumerRecord<?, ?>> list)
kafka学习
是一个分布式、支持分区的(partition)、多副本的(replica),多订阅者。基于zookeeper协调的分布式消息系统 文件存储机制设计很关键
高吞吐量、低延迟 kafka每秒可以处理几十万条消息
可扩展性:kafka集群支持热扩展
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
高并发:支持数千个客户端同时读写
数据缓存和削峰值和异步通信和缓冲
使用场景:
日志收集 一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等
消息系统:解耦和生产者和消费者、缓存消息等。
流式处理:比如spark streaming和storm
Broker Leader的选举:Kakfa Broker集群受Zookeeper管理,只会有一个注册成功。失败了,watcher会选举其他的broker
bug:Zookeeper通信的timeout时间是6s,controller如果有6s中没有和Zookeeper做心跳,就会判断死掉了,重新选举 由于网络等原因慢
概念意思
broker:服务器节点,存储topic的数据。如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition,一个broker可以存topic 零个,一个,多个partition
Topic: 每条发布到Kafka集群的消息都有一个类别,物理上不同Topic的消息分开存储到多个broker,逻辑上一个Topic一起。 类似于数据库的表名
Partition:topic中的数据分割为一个或多个partition,在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1!!!不同partition间的数据丢失了数据的顺序,用segment文件存储
由于每台机器的磁盘大小是有限的,所以即使有再多的机器,可处理的消息还是被磁盘所限制,无法超越当前磁盘大小.因此有了partition的概念.
segment:分成了多个segment(段),然后通过一个index,索引,来标识第几段 segment文件保留的最长时间,默认保留7天(168小时)log.retention.hours 每个segment的大小,默认为1G
offset:偏移量,读到数据,根据二分发查找segment中的index索引来快速定位到位置
Producer:生产者发送的消息,存储到一个partition中
Consumer:可以消费多个topic中的数据
Consumer Group:每个Consumer属于一个特定的Consumer Group
Leader:每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition
Follower:Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader
ISR:in sync replicas(复制)从复制品中删除。
kafka搭建 安装到/opt/kafka https://blog.csdn.net/qq_37598011/article/details/88980317
基本操作和可视化: https://www.jianshu.com/p/2d83a67e2c79?nomobile=yes
首先安装java https://blog.csdn.net/qq_37598011/article/details/88980317
kafaka里面已经带了zookeeper
Zookeeper集群的工作是超过半数才能对外提供服务,3台中超过两台超过半数,允许1台挂掉
安装zookeeper wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz /config/zookeeper.properties
#broker.id=0 每台服务器的broker.id都不能相同
安装Kafka wget http://mirror.bit.edu.cn/apache/kafka/2.0.0/kafka_2.11-2.0.0.tgz /config/server.properties
启动 ./bin/*.sh config/*.properties 后台 nohup ./kafka-server-start.sh ../config/server.properties 1>/dev/null 2>/dev/null &
集群监控体量太大和数据监控kafka tool
#broker的全局唯一编号,不能重复
broker.id=01
#broker需要使用zookeeper保存meta数据
zookeeper.connect=master:2181,slave1:2181,slave2:2181
#segment文件保留的最长时间,超时将被删除
log.retention.hours=168
#partion buffer中,消息的条数达到阈值,将触发flush到磁盘
log.flush.interval.messages=10000
#消息buffer的时间,达到阈值,将触发flush到磁盘
log.flush.interval.ms=3000
#删除topic需要server.properties中设置delete.topic.enable=true否则只是标记删除
delete.topic.enable=true
listeners=PLAINTEXT://{内网ip}:9092 标识很重要
版本匹配!!!1.0 和 0.9坑!!!
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.0.0.RELEASE</version>//与springboot版本不合适
</dependency>
配置 https://blog.csdn.net/fenglibing/article/details/82117166
https://www.cnblogs.com/yx88/p/11013338.html
spring:
kafka:
producer:
retries: 0
batch-size: 16384
buffer-memory: 33554432
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
bootstrap-servers: xxx:9092,xxx:9092
consumer:
bootstrap-servers: xxx:9092,xxx:9092
group-id: test
auto-offset-reset: earliest
enable-auto-commit: true
auto-commit-interval: 100
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.producer.retries=0
spring.kafka.producer.batch-size= 16384
spring.kafka.producer.buffer-memory= 33554432
spring.kafka.producer.key-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.bootstrap-servers= xx:9092
spring.kafka.producer.acks=1
spring.kafka.consumer.bootstrap-servers= xx:9092
spring.kafka.consumer.group-id= test1
spring.kafka.consumer.auto-offset-reset= earliestf
spring.kafka.consumer.enable-auto-commit= true
spring.kafka.consumer.auto-commit-interval= 100
spring.kafka.consumer.key-deserializer= org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer= org.apache.kafka.common.serialization.StringDeserializer
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void send(String message) {
System.out.println("sendssssssssssssssssssssssssssss");
kafkaTemplate.send("test", message);
}
@PostConstruct
public void sendM(){
send("heqiang1");
}
@KafkaListener(topics = {"test"})
@Async
public void getTopic(ConsumerRecord<?, ?> record) {
Optional<?> kafkaMessage = Optional.ofNullable(record.value());
if (kafkaMessage.isPresent()) {
Object message = kafkaMessage.get();
System.out.println("----------------- record =" + record);
System.out.println("------------------ message =" + message);
System.out.printf("topic = %s, offset = %d, value = %s \n", record.topic(), record.offset(), record.value());
}
}
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "47.52.199.51:9092");
props.put("acks", "all"); // 发送所有ISR
props.put("retries", 2); // 重试次数
props.put("batch.size", 16384); // 批量发送大小
props.put("buffer.memory", 33554432); // 缓存大小,根据本机内存大小配置
props.put("linger.ms", 1000); // 发送频率,满足任务一个条件发送
props.put("client.id", "producer-syn-1"); // 发送端id,便于统计
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
return props;
}
KafkaProducer<String, String> producer = new KafkaProducer<>(getProps());
private static Properties getProps(){
Properties props = new Properties();
props.put("bootstrap.servers", "47.52.199.52:9092");
props.put("group.id", "test_3");
props.put("session.timeout.ms", 30000); // 如果其超时,将会可能触发rebalance并认为已经死去,重新选举Leader
props.put("enable.auto.commit", "true"); // 开启自动提交
props.put("auto.commit.interval.ms", "1000"); // 自动提交时间
props.put("max.poll.records","1000"); // 每次批量拉取条数
props.put("auto.offset.reset","earliest"); // 从最早的offset开始拉取,latest:从最近的offset开始消费
props.put("client.id", "producer-syn-1"); // 发送端id,便于统计
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
return props;
}
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(getProps()));
ConsumerRecords<String,String> records = consumer.poll(1000);!!!拉取条数
// 当前批次offset 提交拉去到哪里了
consumer.commitSync();
#如果该值大于零时,表示启用重试失败的发送次数
spring.kafka.producer.retries=2
spring.kafka.producer.batch-size= 16384
spring.kafka.producer.buffer-memory= 33554432
spring.kafka.producer.key-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.bootstrap-servers= master:9092,slave1:9092,slave2:9092,slave3:9092
#procedure要求leader在考虑完成请求之前收到的确认数,用于控制发送记录在服务端的持久化,其值可以为如下:
#acks = 0 如果设置为零,则生产者将不会等待来自服务器的任何确认,该记录将立即添加到套接字缓冲区并视为已发送。在这种情况下,无法保证服务器已收到记录,并且重试配置将不会生效(因为客户端通常不会知道任何故障),为每条记录返回的偏移量始终设置为-1。
#acks = 1 这意味着leader会将记录写入其本地日志,但无需等待所有副本服务器的完全确认即可做出回应,在这种情况下,如果leader在确认记录后立即失败,但在将数据复制到所有的副本服务器之前,则记录将会丢失。
#acks = all 这意味着leader将等待完整的同步副本集以确认记录,这保证了只要至少一个同步副本服务器仍然存活,记录就不会丢失,这是最强有力的保证,这相当于acks = -1的设置。
#可以设置的值为:all, -1, 0, 1
spring.kafka.producer.acks=1
spring.kafka.consumer.bootstrap-servers= master:9092,slave1:9092,slave2:9092,slave3:9092
#spring.kafka.consumer.group-id= simpleGroup
#testearliest当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
#latest当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
#nonetopic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
#spring.kafka.consumer.auto-offset-reset= earliest
#如果'enable.auto.commit'为true,则消费者偏移自动提交给Kafka的频率(以毫秒为单位),默认值为5000。
spring.kafka.consumer.enable-auto-commit= true
spring.kafka.consumer.auto-commit-interval= 100
spring.kafka.consumer.max.poll.records=1000
#consumer在这段时间内没有发送心跳信息,则它会被认为挂掉了
#spring.kafka.consumer.properties.session.timeout.ms=10000
#超过指定时间间隔没有向服务端发送poll()请求,会认为该consumer锁死,就会将该consumer退出group,并进行再分配
#spring.kafka.consumer.properties.max.poll.interval.ms=300000
spring.kafka.consumer.key-deserializer= org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer= org.apache.kafka.common.serialization.StringDeserializer
@PostConstruct
public void init() throws InterruptedException {
int i = 0;
while (true){
send("heqiang" + i);
i++;
Thread.sleep(10000);
}
}
//异步
public void send(String message){
System.out.println(message);
System.out.println(LocalDateTime.now().toString());
try{
kafkaTemplate.send("test", message);
}catch (Exception e){
e.printStackTrace();
}
}
@KafkaListener(topics = {"test"},groupId = "test")
@Async
public void getTopic(ConsumerRecord<?, ?> record) {
Optional<?> kafkaMessage = Optional.ofNullable(record.value());
if (kafkaMessage.isPresent()) {
Object message = kafkaMessage.get();
System.out.println("----------------- record =" + record);
System.out.println("------------------ message =" + message);
System.out.printf("topic = %s, offset = %d, value = %s \n", record.topic(), record.offset(), record.value());
}
}
// @KafkaListener(topics = {"test"},groupId = "test1")
@KafkaListener(id = "id0", topicPartitions = { @TopicPartition(topic = "test", partitions = { "0" }) })
@Async
public void getTopic1(List<ConsumerRecord<?, ?>> records) {
for (ConsumerRecord<?, ?> record : records) {
Optional<?> kafkaMessage = Optional.ofNullable(record.value());
if (kafkaMessage.isPresent()) {
Object message = kafkaMessage.get();
System.out.println("----------------- record =" + record);
System.out.println("------------------ message =" + message);
System.out.printf("topic = %s, offset = %d, value = %s \n", record.topic(), record.offset(), record.value());
}
}
}