Kafka
Kafka介绍
Kafka最初是由LinkedIn开发,并于2011年初开源。2012年10月从Apache Incubator毕业。该项目的目标是为处理实时数据提供一个统一、高吞吐量、低延迟的平台。
Kafka是一个分布式消息队列:生产者、消费者的功能。它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。
Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。
无论是kafka集群,还是producer和consumer都依赖于zookeeper集群保存一些meta信息,来保证系统可用性
-
-
它是一个发布和订阅过程
-
服务端主动把数据推送给消费者
-
-
-
kafka不是一个标准的JMS(java message)java消息队列实现,它是一个类似于JMS框架
-
它是一个发布和拉取过程
-
服务端不会主动把数据推送给消费者,需要消费者自己主动把数据拉取过来
-
-
在架构模型方面
RabbitMQ遵循AMQP协议,RabbitMQ的broker由Exchange,Binding,queue组成,其中exchange和binding组成了消息的路由键;客户端Producer通过连接channel和server进行通信,Consumer从queue获取消息进行消费(长连接,queue有消息会推送到consumer端,consumer循环从输入流读取数据)。rabbitMQ以broker为中心;有消息的确认机制。
kafka遵从一般的MQ结构,producer,broker,consumer,以consumer为中心,消息的消费信息保存的客户端consumer上,consumer根据消费的点,从broker上批量pull数据;无消息确认机制。
在吞吐量
kafka具有高的吞吐量,内部采用消息的批量处理,zero-copy机制,数据的存储和获取是本地磁盘顺序批量操作,具有O(1)的复杂度,消息处理的效率很高。
rabbitMQ在吞吐量方面稍逊于kafka,他们的出发点不一样,rabbitMQ支持对消息的可靠的传递,支持事务,不支持批量的操作;基于存储的可靠性的要求存储可以采用内存或者硬盘。
在可用性方面
rabbitMQ支持miror的queue,主queue失效,miror queue接管。kafka的broker支持主备模式。
在集群负载均衡方面
kafka采用zookeeper对集群中的broker、consumer进行管理,可以注册topic到zookeeper上;通过zookeeper的协调机制,producer保存对应topic的broker信息,可以随机或者轮询发送到broker上;并且producer可以基于语义指定分片,消息发送到broker的某分片上。
消息队列的作用
消息系统的核心作用就是三点:解耦,异步和并行
以用户注册的案列来说明消息系统的作用
用户注册的一般流程
问题:随着后端流程越来越多,每步流程都需要额外的耗费很多时间,从而会导致用户更长的等待延迟。
用户注册的并行执行
问题:系统并行的发起了4个请求,4个请求中,如果某一个环节执行1分钟,其他环节再快,用户也需要等待1分钟。如果其中一个环节异常之后,整个服务挂掉了。
用户注册的最终一致
1、 保证主流程的正常执行、执行成功之后,发送MQ消息出去。
2、 需要这个destination的其他系统通过消费数据再执行,最终一致。
kafka的架构模型
基于producer consumer topic broker 等的一个基本架构
-
-
生产者,它会把数据源写入到kafka集群中
-
-
2、broker
-
一个broker就是kafka集群的节点,可以存放数据
-
-
3、topic
-
消息的主题,它是一类消息的集合
-
-
4、partition
-
分区概念
-
也就是说一个topic有多个分区
-
-
-
5、replication
-
副本
-
一个分区可以设置多个副本,副本保证数据的安全性
-
-
-
6、segment
-
每一个分区数据都很多segment,一个segment里面有2个文件,一个是.log文件,它是topic数据存储的文件,还有一个文件叫.index文件,它是.log文件索引文件。
-
-
7、zookeeper
-
通过zk保存kafka集群元数据信息,这些元数据信息包括:kafka集群地址、有哪些topic,以及每一个topic的分区数等等信息
-
-
8、consumer
-
消费者
-
消费者去kafka集群中拉取数据然后进行消费
-
-
-
9、offset
-
消息的偏移量
-
保存消息消费到哪里了,它会把消息消费的数据记录,当前这个记录信息叫做offset偏移量
-
消息偏移量的保存有2种方式
-
第一种: 可以kafka自己去保存(这个偏移量由整个kafka集群自带一个topic:__consumer_offsets)
-
第二种:由zookeeper保存
-
-
-
安装zookeeper
安装zookeeper之前一定要确保三台机器时钟同步
-
-
kafka_2.11-1.0.0.tgz
-
-
2、规划安装目录
-
/export/servers
-
-
3、上传安装包到服务器中
-
4、解压安装包到指定规划目录
-
tar -zxvf kafka_2.11-1.0.0.tgz -C /export/servers
-
-
5、修改配置文件
-
在node1上修改
-
进入到kafka安装目录下有一个config目录
-
vi server.properties
#broker的全局唯一编号,不能重复
broker.id=0
#用来监听链接的端口,producer或consumer将在此端口建立连接
port=9092
#处理网络请求的线程数量
num.network.threads=3
#用来处理磁盘IO的线程数量
num.io.threads=8
#发送套接字的缓冲区大小
socket.send.buffer.bytes=102400
#接受套接字的缓冲区大小
socket.receive.buffer.bytes=102400
#请求套接字的缓冲区大小
socket.request.max.bytes=104857600
#kafka运行日志存放的路径
log.dirs=/export/servers/kafka/kafka-logs
#topic在当前broker上的分片个数
num.partitions=2
#用来恢复和清理data下数据的线程数量
num.recovery.threads.per.data.dir=1
#segment文件保留的最长时间,超时将被删除(默认保留168小时=7天)
log.retention.hours=168
#一个topic的数据量大小达到一定阀值时,会删除topic的数据,默认等于-1,表示没有限制
log.retention.bytes=-1
#滚动生成新的segment文件的最大时间
log.roll.hours=1
#日志文件中每个segment的大小,默认为1G
log.segment.bytes=1073741824
#周期性检查文件大小的时间
log.retention.check.interval.ms=300000
#日志清理是否打开
log.cleaner.enable=true
#broker需要使用zookeeper保存meta数据
zookeeper.connect=node1:2181,node2:2181,node3:2181
#zookeeper链接超时时间
zookeeper.connection.timeout.ms=6000
#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
#此处的host.name为本机IP(重要),如果不改,则客户端会抛出:Producer connection to localhost:9092 unsuccessful 错误!
host.name=node1
#广播地址,主要用于外网连接kafka集群,一般用不到
advertised.host.name=192.168.200.100
-
-
配置kafka环境变量
-
vi /etc/profile
export KAFKA_HOME=/export/servers/kafka export PATH=$PATH:$KAFKA_HOME/bin
-
-
-
-
6、分发kafka安装目录到其他节点
scp -r kafka node2:/export/servers scp -r kafka node3:/export/servers scp /etc/profile node2:/etc scp /etc/profile node3:/etc
-
7、修改node2和node3上的配置
-
node2
-
vi server.properties
#指定kafka对应的broker id ,唯一 broker.id=1 #指定数据存放的目录 log.dirs=/export/servers/kafka/kafka-logs #指定zk地址 zookeeper.connect=node1:2181,node2:2181,node3:2181 #指定是否可以删除topic ,默认是false 表示不可以删除 delete.topic.enable=true #指定broker主机名 host.name=node2
-
-
node3
-
vi server.properties
#指定kafka对应的broker id ,唯一 broker.id=2 #指定数据存放的目录 log.dirs=/export/servers/kafka/kafka-logs #指定zk地址 zookeeper.connect=node1:2181,node2:2181,node3:2181 #指定是否可以删除topic ,默认是false 表示不可以删除 delete.topic.enable=true #指定broker主机名 host.name=node3
-
-
-
-
先启动zk集群
-
然后在所有节点执行脚本
nohup kafka-server-start.sh /export/servers/kafka/config/server.properties >/dev/null 2>&1 &
-
一键启动kafka脚本
-
start_kafka.sh
#!/bin/sh for host in node1 node2 node3 do ssh $host "source /etc/profile;nohup kafka-server-start.sh /export/servers/kafka/config/server .properties >/dev/null 2>&1 &" echo "$host kafka is running" done
-
-
-
2、停止
-
然后在所有节点执行脚本
需要修改信息 kafka-server-stop.sh 这个脚本的逻辑就是通过下面的命令获取kafka进程号,这个默认的kafka集群是获取不到(脚本有问题) 原始: PIDS=$(ps ax | grep -i 'kafka\.Kafka' | grep java | grep -v grep | awk '{print $1}') 再所有的节点都需要修改成: PIDS=$(ps ax | grep -i 'kafka' | grep java | grep -v grep | awk '{print $1}') 可以在所有节点执行: kafka-server-stop.sh
-
一键停止kafka脚本
-
stop_kafka.sh
#!/bin/sh for host in node1 node2 node3 do ssh $host "source /etc/profile;nohup /export/servers/kafka/bin/kafka-server-stop.sh&" echo "$host kafka is stopping" done
-
-
-
-
kafka-topics.sh
kafka-topics.sh --create --partitions 3 --replication-factor 2 --topic test --zookeeper node1:2181,node2:2181,node3:2181
-
2、查询所有的topic
-
-
kafka-topics.sh
kafka-topics.sh --list --zookeeper node1:2181,node2:2181,node3:2181
-
3、模拟生产者写入数据到topic中
-
-
kafka-console-producer.sh
kafka-console-producer.sh --broker-list node1:9092,node2:9092,node3:9092 --topic test
-
4、模拟消费者拉取topic中的数据
-
-
kafka-console-consumer.sh
kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092,node3:9092 --topic test --from-beginning
或
kafka-console-consumer.sh --zookeeper node1:2181,node2:2181,node3:2181 --topic test --from-beginning
-
kafka的API
引入依赖
<dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>1.0.0</version> </dependency>
代码开发
import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import java.util.Properties; //todo:需求:开发kafka生产者代码 public class KafkaProducerStudy { public static void main(String[] args) { //准备配置属性 Properties props = new Properties(); //kafka集群地址 props.put("bootstrap.servers", "node1:9092,node2:9092,node3:9092"); //acks它代表消息确认机制 props.put("acks", "all"); //重试的次数 props.put("retries", 0); //批处理数据的大小,每次写入多少数据到topic props.put("batch.size", 16384); //可以延长多久发送数据 props.put("linger.ms", 1); //缓冲区的大小 props.put("buffer.memory", 33554432); props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); Producer<String, String> producer = new KafkaProducer<String, String>(props); for (int i = 0; i < 100; i++) //这里需要三个参数,第一个:topic的名称,第二个参数:表示消息的key,第三个参数:消息具体内容 producer.send(new ProducerRecord<String, String>("test", Integer.toString(i), "hello-kafka-"+i)); producer.close(); } }
自动提交偏移量代码开发
import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import java.util.Arrays; import java.util.Properties; //todo:需求:开发kafka消费者代码(自动提交偏移量) public class KafkaConsumerStudy { public static void main(String[] args) { //准备配置属性 Properties props = new Properties(); //kafka集群地址 props.put("bootstrap.servers", "node1:9092,node2:9092,node3:9092"); //消费者组id props.put("group.id", "test"); //自动提交偏移量 props.put("enable.auto.commit", "true"); //自动提交偏移量的时间间隔 props.put("auto.commit.interval.ms", "1000"); //默认是latest //earliest: 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费 //latest: 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据 //none : topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常 props.put("auto.offset.reset","earliest"); props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props); //指定消费哪些topic consumer.subscribe(Arrays.asList("test")); while (true) { //指定每个多久拉取一次数据 ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value()); } } }
手动提交偏移量代码开发
import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; //todo:需求:开发kafka消费者代码(手动提交偏移量) public class KafkaConsumerControllerOffset { public static void main(String[] args) { Properties props = new Properties(); props.put("bootstrap.servers", "node1:9092,node2:9092,node3:9092"); props.put("group.id", "test"); //手动提交偏移量 props.put("enable.auto.commit", "false"); props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(props); consumer.subscribe(Arrays.asList("test")); final int minBatchSize = 20; List<ConsumerRecord<String, String>> buffer = new ArrayList<ConsumerRecord<String, String>>(); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) { buffer.add(record); } if (buffer.size() >= minBatchSize) { //insertIntoDb(buffer); 拿到数据之后,进行消费 System.out.println("缓冲区的数据条数:"+buffer.size()); consumer.commitSync(); buffer.clear(); } } } }
就是消息会写入到哪一个topic对应的分区中
1、指定具体的分区号
//1、给定具体的分区号,数据就会写入到指定的分区中 producer.send(new ProducerRecord<String, String>("test", 0,Integer.toString(i), "hello-kafka-"+i));
2、不给定具体的分区号,给定key的值(key不断变化)
//2、不给定具体的分区号,给定一个key值,这一块也是按照key的 hashcode%分区数=分区号(这个时候key需要变化) producer.send(new ProducerRecord<String, String>("test", Integer.toString(i), "hello-kafka-"+i));
3、不给定具体的分区号,也不给对应的key
//3、不给定具体的分区号,也不给定对应的key ,这个它会进行轮训的方式把数据写入到不同分区中 producer.send(new ProducerRecord<String, String>("test", "hello-kafka-"+i));
4、自定义分区
定义一个类实现接口Partitioner
import org.apache.kafka.clients.producer.Partitioner; import org.apache.kafka.common.Cluster; import java.util.Map; //todo:需求:自定义kafka的分区函数 public class MyPartitioner implements Partitioner{ /** * 通过这个方法来实现消息要去哪一个分区中 * @param topic * @param key * @param bytes * @param value * @param bytes1 * @param cluster * @return */ public int partition(String topic, Object key, byte[] bytes, Object value, byte[] bytes1, Cluster cluster) { //获取topic分区数 int partitions = cluster.partitionsForTopic(topic).size(); //key.hashCode()可能会出现负数 -1 -2 0 1 2 //Math.abs 取绝对值 return Math.abs(key.hashCode()%partitions); } public void close() {} public void configure(Map<String, ?> map) {} }
import org.apache.kafka.clients.producer.Partitioner; import org.apache.kafka.common.Cluster; import java.util.Map; //todo:需求:自定义kafka的分区函数 public class MyPartitioner implements Partitioner{ /** * 通过这个方法来实现消息要去哪一个分区中 * @param topic * @param key * @param bytes * @param value * @param bytes1 * @param cluster * @return */ public int partition(String topic, Object key, byte[] bytes, Object value, byte[] bytes1, Cluster cluster) { //获取topic分区数 int partitions = cluster.partitionsForTopic(topic).size(); //key.hashCode()可能会出现负数 -1 -2 0 1 2 //Math.abs 取绝对值 return Math.abs(key.hashCode()%partitions); } public void close() {} public void configure(Map<String, ?> map) {} }
在生产者代码需要配置自定义分区类
//添加自定义分区函数 props.put("partitioner.class","cn.itcast.kafka.MyPartitioner");
-
1、在同一个消费者组中,不同的线程在同一时间不能够处理同一分区中的数据,也就是说在同一时间一个线程只能消费一个分区数据
-
2、在不同的消费者里面,不同的线程在同一时间可以消费同一分区中的数据
-
3、多个线程在消费数据的时候,只能够保证每一个分区内部有序(按照数据进入的先后),而全局是没有顺序的。
-
可以极端一点,只设置一个分区,那么就保证全局有序,这个时候只有一个分区,跟对应kafka分布式框架理念相违背
-
kafka存储机制
Partition是以文件的形式存储在文件系统中,比如,创建了一个名为test的topic,其有5个partition,那么在Kafka的数据目录中(由配置文件中的log.dirs指定的)中就会有这样5个目录: test-0,test-1,test-2,test-3,test-4,其命名规则为<topic_name>-<partition_id>,里面存储的分别就是这5个partition的数据。
每一个partition目录下的文件被平均切割成大小相等(默认一个文件是1G,可以手动去设置)的数据文件,每一个数据文件都被称为一个段(segment file),但每个段消息数量不一定相等,这种特性能够使得老的segment可以被快速清除。默认保留7天的数据。
每次满1G后,在写入到一个新的文件中。
数据消费问题讨论
问题:如何保证消息消费的有序性呢?比如说生产者生产了0到100个商品,那么消费者在消费的时候按照0到100这个从小到大的顺序消费,
*** 那么kafka如何保证这种有序性呢?***
难度就在于,生产者生产出0到100这100条数据之后,通过一定的分组策略存储到broker的partition中的时候,
比如0到10这10条消息被存到了这个partition中,10到20这10条消息被存到了那个partition中,这样的话,消息在分组存到partition中的时候就已经被分组策略搞得无序了。
那么能否做到消费者在消费消息的时候全局有序呢?
遇到这个问题,我们可以回答,在大多数情况下是做不到全局有序的。但在某些情况下是可以做到的。比如我的partition只有一个,这种情况下是可以全局有序的。
那么可能有人又要问了,只有一个partition的话,哪里来的分布式呢?哪里来的负载均衡呢?
所以说,全局有序是一个伪命题!全局有序根本没有办法在kafka要实现的大数据的场景来做到。但是我们只能保证当前这个partition内部消息消费的有序性。
结论:一个partition中的数据是有序的吗?回答:间隔有序,不连续。
针对一个topic里面的数据,只能做到partition内部有序,不能做到全局有序。特别是加入消费者的场景后,如何保证消费者的消费的消息的全局有序性,
这是一个伪命题,只有在一种情况下才能保证消费的消息的全局有序性,那就是只有一个partition。
Segment文件特点
segment文件命名的规则:partition全局的第一个segment从0(20个0)开始,后续的每一个segment文件名是上一个segment文件中最后一条消息的offset值
那么这样命令有什么好处呢?假如我们有一个消费者已经消费到了368776(offset值为368776),那么现在我们要继续消费的话,怎么做呢?
分2个步骤,第1步是从所有文件log文件的的文件名中找到对应的log文件,第368776条数据位于上图中的“00000000000000368769.log”这个文件中,这一步涉及到一个常用的算法叫做“二分查找法”(假如我现在给你一个offset值让你去找,你首先是将所有的log的文件名进行排序,然后通过二分查找法进行查找,很快就能定位到某一个文件,紧接着拿着这个offset值到其索引文件中找这条数据究竟存在哪里);第2步是到index文件中去找第368776条数据所在的位置。
索引文件(index文件)中存储这大量的元数据,而数据文件(log文件)中存储这大量的消息。
索引文件(index文件)中的元数据指向对应的数据文件(log文件)中消息的物理偏移地址。
kafka的稀疏存储
上图的左半部分是索引文件,里面存储的是一对一对的key-value,其中key是消息在数据文件(对应的log文件)中的编号,比如“1,3,6,8……”,分别表示在log文件中的第1条消息、第3条消息、第6条消息、第8条消息……,那么为什么在index文件中这些编号不是连续的呢?这是因为index文件中并没有为数据文件中的每条消息都建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。
其中以索引文件中元数据3,497为例,其中3代表在右边log数据文件中从上到下第3个消息(在全局partiton表示第368772个消息),其中497表示该消息的物理偏移地址(位置)为497。
总结:
• Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
• 通过索引信息可以快速定位message。
• 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
• 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。
kafka配置文件说明
Server.properties配置文件说明见上面安装步骤
producer生产者配置文件说明
#指定kafka节点列表,用于获取metadata,不必全部指定(老版本这样指定) metadata.broker.list=node1:9092,node2:9092,node3:9092 # 指定分区处理类。默认kafka.producer.DefaultPartitioner,表通过key哈希到对应分区 partitioner.class=kafka.producer.DefaultPartitioner # 是否压缩,默认0表示不压缩,1表示用gzip压缩,2表示用snappy压缩。压缩后消息中会有头来指明消息压缩类型,故在消费者端消息解压是透明的无需指定。 gzip snappy compression.codec=none # 指定序列化处理类 serializer.class=kafka.serializer.DefaultEncoder # 如果要压缩消息,这里指定哪些topic要压缩消息,默认empty,表示不压缩。 #compressed.topics= # 设置发送数据是否需要服务端的反馈,有三个值0,1,-1 # 0: producer不会等待broker发送ack # 1: 当leader接收到消息之后发送ack # -1: 当所有的follower都同步消息成功后发送ack. request.required.acks=0 # 在向producer发送ack之前,broker允许等待的最大时间 ,如果超时,broker将会向producer发送一个error ACK.意味着上一次消息因为某种原因未能成功(比如follower未能同步成功) request.timeout.ms=10000 # 同步还是异步发送消息,默认“sync”表同步,"async"表异步。异步可以提高发送吞吐量, 也意味着消息将会在本地buffer中,并适时批量发送,但是也可能导致丢失未发送过去的消息 producer.type=sync # 在async模式下,当message被缓存的时间超过此值后,将会批量发送给broker,默认为5000ms # 此值和batch.num.messages协同工作. queue.buffering.max.ms = 5000 # 在async模式下,producer端允许buffer的最大消息量 # 无论如何,producer都无法尽快的将消息发送给broker,从而导致消息在producer端大量沉积 # 此时,如果消息的条数达到阀值,将会导致producer端阻塞或者消息被抛弃,默认为10000 queue.buffering.max.messages=20000 # 如果是异步,指定每次批量发送数据量,默认为200 batch.num.messages=500 # 当消息在producer端沉积的条数达到"queue.buffering.max.meesages"后 # 阻塞一定时间后,队列仍然没有enqueue(producer仍然没有发送出任何消息) # 此时producer可以继续阻塞或者将消息抛弃,此timeout值用于控制"阻塞"的时间 # -1: 无阻塞超时限制,消息不会被抛弃 # 0:立即清空队列,消息被抛弃 queue.enqueue.timeout.ms=-1 # 当producer接收到error ACK,或者没有接收到ACK时,允许消息重发的次数,因为broker并没有完整的机制来避免消息重复,所以当网络异常时(比如ACK丢失) # 有可能导致broker接收到重复的消息,默认值为3. message.send.max.retries=3 # producer刷新topic metada的时间间隔,producer需要知道partition leader的位置,以及当前topic的情况,因此producer需要一个机制来获取最新的metadata,当producer遇到特定错误时,将会立即刷新 。 (比如topic失效,partition丢失,leader失效等),此外也可以通过此参数来配置额外的刷新机制,默认值600000 topic.metadata.refresh.interval.ms=60000
consumer消费者配置详细说明
# zookeeper连接服务器地址 zookeeper.connect=node1:2181,node2:2181,node3:2181 # zookeeper的session过期时间,默认5000ms,用于检测消费者是否挂掉 zookeeper.session.timeout.ms=5000 #当消费者挂掉,其他消费者要等该指定时间才能检查到并且触发重新负载均衡 zookeeper.connection.timeout.ms=10000 #ZooKeeper集群中leader和follower之间的同步时间 zookeeper.sync.time.ms=2000 #指定 消费者组id group.id=itcast # 当consumer消费一定量的消息之后,将会自动向zookeeper提交offset信息 # 注意offset信息并不是每消费一次消息就向zk提交一次,而是现在本地保存(内存),并定期提交,默认为true auto.commit.enable=true # 自动更新时间。默认60 * 1000 auto.commit.interval.ms=1000 # 当前consumer的标识,可以设定,也可以有系统生成,主要用来跟踪消息消费情况,便于观察 conusmer.id=xxx # 消费者客户端编号,用于区分不同客户端,默认客户端程序自动产生 client.id=xxxx # 当有新的consumer加入到group时,将会reblance,此后将会有partitions的消费端迁移到新的consumer上,如果一个consumer获得了某个partition的消费权限,那么它将会向zk注册 "Partition Owner registry"节点信息,但是有可能此时旧的consumer尚没有释放此节点, 此值用于控制,注册节点的重试次数. rebalance.max.retries=5 # 发送到消费端的最小数据,若是不满足这个数值则会等待直到满足指定大小。默认为1表示立即接收 fetch.min.bytes=1 # 当消息的尺寸不足时,server阻塞的时间,如果超时,消息将立即发送给consumer fetch.wait.max.ms=5000 socket.receive.buffer.bytes=655360 # 如果zookeeper没有offset值或offset值超出范围。那么就给个初始的offset。有smallest、largest、anything可选,分别表示给当前最小的offset、当前最大的offset、抛异常。默认largest auto.offset.reset=smallest # 指定序列化处理类 derializer.class=kafka.serializer.DefaultDecoder
flume与kafka的整合
实现flume监控某个目录下面的所有文件,然后将文件收集发送到kafka消息系统中
第一步:flume下载地址
http://archive.apache.org/dist/flume/1.8.0/apache-flume-1.8.0-bin.tar.gz
第二步:上传解压flume
第三步:配置flume-kafka.conf
#为我们的source channel sink起名 a1.sources = r1 a1.channels = c1 a1.sinks = k1 #指定我们的source收集到的数据发送到哪个管道 a1.sources.r1.channels = c1 #指定我们的source数据收集策略 a1.sources.r1.type = spooldir a1.sources.r1.spoolDir = /export/servers/flumedata a1.sources.r1.deletePolicy = never a1.sources.r1.fileSuffix = .COMPLETED a1.sources.r1.ignorePattern = ^(.)*\\.tmp$ a1.sources.r1.inputCharset = utf-8 #指定我们的channel为memory,即表示所有的数据都装进memory当中 a1.channels.c1.type = memory #指定我们的sink为kafka sink,并指定我们的sink从哪个channel当中读取数据 a1.sinks.k1.channel = c1 a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink a1.sinks.k1.kafka.topic = test a1.sinks.k1.kafka.bootstrap.servers = node1:9092,node2:9092,node3:9092 a1.sinks.k1.kafka.flumeBatchSize = 20 a1.sinks.k1.kafka.producer.acks = 1
启动flume
nohup bin/flume-ng agent -n a1 -c conf -f conf/flume-kafka.conf -Dflume.root.logger=info,console >/dev/null 2>&1 &
-
-
同步模式
producer.type=sync (同步模式) 每次发送一条,每条数据都需要确认,效率比较低,但是数据安全 request.required.acks=1
-
异步模式
producer.type=async (异步模式) 每一次发送一个批次的数据 request.required.acks=1 //每次到达5s一起发送 queue.buffering.max.ms=5000 //每次缓存区数据达到10000 queue.buffering.max.messages=10000 //针对于缓存区的数据已经达到发送给kafka集群阈值,可能由于一些原因,导致数据没有发送出去,是否清空缓存区 等于0 表示清空,清空之后数据丢失,等于-1 保留 queue.enqueue.timeout.ms = -1 //每次发送给kafka集群数据量200 batch.num.messages=200
-
-
2、broker---kafka集群 保证数据不丢失
一个topic有很多个分区,每一个分区有很多个副本,可以通过副本保证数据的安全性
-
3、消费者保证数据的安全性
针对于每一个消费者在消费数据的时候,都会把当前消费的偏移量保存在kafka集群或者zookeeper,当前消费者挂了,再次重启,重启之后可以读取上一次消费的偏移量,然后继续消费。
-
-
1、上传安装包到node1服务器中
-
kafka-manager-1.3.3.7.zip
-
-
2、解压安装包
-
unzip kafka-manager-1.3.3.7.zip -d /export/servers
-
-
3、修改配置文件
-
进入到conf目录
-
vi application.conf
kafka-manager.zkhosts="node1:2181,node2:2181,node3:2181"
-
-
-
4、启动kafkaManager
nohup bin/kafka-manager -Dconfig.file=/export/kafka-manager-1.3.3.7/conf/application.conf -Dhttp.port=8070 >/dev/null 2>&1 &
-
5、访问web服务
http://node1:8070