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与ActiveMQ区别

  • activeMQ是一个标准的JMS(java message)java消息队列实现

    • 它是一个发布和订阅过程

      • 服务端主动把数据推送给消费者

  • 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  等的一个基本架构

 

 

 

  • 1、producer

    • 生产者,它会把数据源写入到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保存

 

 

kafka集群安装部署

安装zookeeper

安装zookeeper之前一定要确保三台机器时钟同步

 

  • 1、下载安装包http://kafka.apache.org

    • 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
        
        

 

kafka集群启动和停止

  • 1、启动

    • 先启动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的命令行的管理使用

 

1、创建topic

 

    • 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();
            }
        }
    }
}

 

kafka分区策略

 就是消息会写入到哪一个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");

 

kafka消费策略

结论

  • 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 &

 

kafka如何保证数据的不丢失

  • 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,当前消费者挂了,再次重启,重启之后可以读取上一次消费的偏移量,然后继续消费。

     

 

kafkaManager监控工具的使用

  • kafkaManager它是由雅虎开源的可以监控整个kafka集群相关信息的一个工具。

  • 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

     

posted @ 2018-07-26 14:39  jiFeng丶  阅读(647)  评论(0编辑  收藏  举报