kafka学习总结(简化个人版)(1)

kafka部署在了 IP为:192.168.56.10 
zookeeper部署在了ip为:192.168.56.11,端口为2181(尝试部署在一台虚拟机的时候行不通)
vgrant init centos/7
vagrant up
vagrant ssh
kafka所在的虚拟机文件在:C:\Users\yin_q>
zookeeper所在的虚拟机文件在:C:\Windows\system32> 
第一篇章:
启动kafka:cd  /usr/local/kafka/kafka/bin
                  ./kafka-server-start.sh -daemon  ../config/server.properties
启动zookeeper:
                  cd /usr/local/zookeeper/bin
                  ./zkServer.sh start
进入到zk中的节点看id是 0 的broker有没有存在:
                  cd /usr/local/zookeeper/bin
                  ./zkCli.sh
                  ls /brokers/ids/
创建名为“test”的topic: 
                  ./kafka-topics.sh --create --zookeeper 192.168.56.11:2181 --replication-factor 1 --partitions 1 --topic test
查看当前kafka内有哪些topic:
                  ./kafka-topics.sh --list --zookeeper 192.168.56.11:2181
发送消息:
                 ./kafka-console-producer.sh --broker-list 192.168.56.10:9092 --topic test
消费消息: 
                方式一:从最后一条消息的偏移量+1开始消费
                              ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --topic test
                方式二:从头开始消费
                              ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --from-beginning --topic test
消息储存在  :/usr/local/kafka/data/kafka-logs/主题-分区/00000000.log中  ,消息是顺序存储,消息是有偏移量的,消费时可以指明偏移量进行消费


单播消息:一个消费组里只会有一个消费者能消费到某一个topic中的消息。于是可以创建多个消费者,这些消费者在同一个消费组中
                ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --consumer-property group.id=testGroup --topic test
多播消息:
               ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --consumer-property group.id=testGroup1 --topic test

               ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --consumer-property group.id=testGroup2 --topic test

查看当前主题下有哪些消费组
                ./kafka-consumer-groups.sh --bootstrap-server 192.168.56.10:9092 --list
查看消费组中的具体信息:比如当前偏移量、最后一条消息的偏移量、堆积的消息数量
                ./kafka-consumer-groups.sh --bootstrap-server 192.168.56.10:9092 --describe --group testGroup
                        Currennt-offset: 当前消费组的已消费偏移量
                        Log-end-offset: 主题对应分区消息的结束偏移量(HW)
                        Lag: 当前消费组未消费的消息数

为一个主题“test1”创建多个分区:
                ./kafka-topics.sh --create --zookeeper 192.168.56.11:2181 --replication-factor 1 --partitions 2 --topic test1
查看topic的分区信息:
               ./kafka-topics.sh --describe --zookeeper 192.168.56.11:2181 --topic test1

(定期将自己消费分区的offset提交给kafka内部topic:__consumer_offsets,提交过去的时候,key是consumerGroupId+topic+分区号,value就是当前offset的值,kafka会定 期清理topic里的消息,最后就保留最新的那条数据 因为__consumer_offsets可能会接收高并发的请求,kafka默认给其分配 50 个分区(可以 通过offsets.topic.num.partitions设置),这样可以通过加机器的方式抗大并发。 通过如下公式可以选出consumer消费的offset要提交到__consumer_offsets的哪个分区 公式:hash(consumerGroupId) % __consumer_offsets主题的分区数)

第二篇章:Kafka集群及副本
搭建kakfa集群,3个broker
vi server.properties:
             broker.id= 0
             listeners=PLAINTEXT://192.168.56.10:9092
             log.dir=/usr/local/data/kafka-logs
vi server1.properties
             broker.id= 1
             listeners=PLAINTEXT://192.168.56.10:9093
             log.dir=/usr/local/data/kafka-logs-1
vi server2.properties
             broker.id= 2
             listeners=PLAINTEXT://192.168.56.10:9094
             log.dir=/usr/local/data/kafka-logs-2

启动三台服务器:
            ./kafka-server-start.sh -daemon ../config/server.properties
            ./kafka-server-start.sh -daemon ../config/server1.properties
            ./kafka-server-start.sh -daemon ../config/server2.properties
查看是否成功:
              进入zookeeper中的bin下:
                 ./zkCli.sh
                 ls /brokers/ids/
 在集群中,不同的副本会被部署在不同的broker上。下面例子:创建 1个主题, 2 个分区、 3 个副本:
                ./kafka-topics.sh --create --zookeeper 192.168.56.11:2181 --replication-factor 3 --partitions 2 --topic my-replicated-topic
查看topic情况:
               ./kafka-topics.sh --describe --zookeeper 192.168.56.11:2181 --topic my-replicated-topic
说明:isr: 可以同步的broker节点和已同步的broker节点,存放在isr集合中。如果isr中的节点性能较差,会被踢出isr集合
          leader:kafka的读写操作,都发生在leader,leader负责把数据同步给follower。当leader挂了,经过主从选举,从多个follwer中选举产生一个新的leader
          follower:接收leader的同步的数据


           broker,主题,分区,副本:集群中有多个broker,创建主题是可以指明主题有多少个分区(把消息拆分到不同的分区中储存),可以为分区创建多个副本,不同的副本存放在不同的broker中;
kafka集群消息的发送:      
               ./kafka-console-producer.sh --broker-list 192.168.56.10:9092,192.168.56.10:9093,192.168.56.10:9094 --topic my-replicated-topic          
kafka集群消息的消费:              
              ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092,192.168.56.10:9093,192.168.56.10:9094 --from-beginning --topic my-replicated-topic
带有消费组的kafka集群消息的消费:
              ./kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092,192.168.56.10:9093,192.168.56.10:9094 --from-beginning  --consumer-property  group.id=testGroup  --topic my-replicated-topic

第三篇章: java客户端连接kafka
1.生产者的基本实现:

引入依赖:
 

<dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>2.4.1</version>
</dependency>
public class MyProducer{
    private final static String TOPIC_NAME = "my-replicated-topic";

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.设置参数
        Properties props = new Properties();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
                "192.168.56.10:9092, 192.168.56.10:9093, 192.168.56.10:9094");
        // 把发送的key和value从字符串序列化为字节数组
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        // 2.创建生产消息的客户端,传入参数
        Producer<String, String> producer = new KafkaProducer<String, String>(props);

        // 3.创建消息
        // key:决定了往哪个分区发(若分区不存在,则自动创建),  value:要发送的内容,  未指定发送分区,具体发送的分区计算公式:hash(key)%partitionNum
        ProducerRecord<String, String> producerRecord = new ProducerRecord<>(TOPIC_NAME, "my_keyvalue", "hellokafka");

        // 4.同步发送消息,得到消息发送的元数据并输出(应该用try-catch)
        RecordMetadata metadata = producer.send(producerRecord).get();
        System.out.println("同步方式发送消息结果:" + "topic-" + metadata.topic() +
                " |partition-" + metadata.partition() + "|offset-" + metadata.offset());
        // 4.异步发送方法
//       producer.send(producerRecord, new Callback(){
//            @Override
//            public void onCompletion(RecordMetadata recordMetadata, Exception e) {
//                if(e != null){
//                    System.out.println("消息发送失败:" + e.getStackTrace());
//                }
//                if(recordMetadata != null){
//                    System.out.println("异步方式发送消息结果:" + "topic-" + recordMetadata.topic() +
//                            " |partition-" + recordMetadata.partition() + "|offset-" + recordMetadata.offset());
//                }
//            }
//        });
//        Thread.sleep(100000000000000L);  
    }
}

同步发送:如果生产者发送消息没有收到ack,生产者就会阻塞,等待3s,如果还没有收到ack,就会重发,重发3次还不行就认为发送失败
异步发送:生产者发消息,发送完后不用等待broker给回复,直接执行下面的业务逻辑。可以提供callback,让broker异步的调用callback,告知生产者,消息发送的结果

                   =======================基本配置==========================

关于生产者的ack参数配置:
  ★ ack配置
 在同步发送的前提下,生产者在获得集群返回的ack之前会阻塞。那么什么时候会返回ack呢?ack有以下三个配置
        -ack=0:kafka-cluster不需要任何broker收到消息,立即返回ack给生产者。效率最高,但是最不安全,容易丢失数据;
        -ack=1:多副本中的leader收到消息,并把消息写到本地log中,才返回ack给生产者。性能和安全性均衡;
        -ack=-1/all:再加上配置min-insync.replicas=2(默认为1,推荐>=2),此时需要leader和一个follower同步完成后,才会返回ack。最安全单性能最差。
        props.put(ProducerConfig.ACKS_CONFIG, "1");
  ★ 重试配置
发送失败后的重试配置,发送失败会重试,默认间隔100ms,默认重试3次
//        props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 100);
//        props.put(ProducerConfig.RETRIES_CONFIG,3);
  ★ 消息缓冲区配置
kafak默认创建一个消息缓冲区,用来存放要发送的消息,默认为32m,kafka本地线程会去缓冲区中一次拉16k的数据,发送到broker,如果拉不到16k,就间隔10ms将已拉到的数据发给broker
//        props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
//        props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
//        props.put(ProducerConfig.LINGER_MS_CONFIG, 10);


补充:kafka生产者消息分区策略:
         轮询分区策略
         随机分区策略
         按key分区分配celue
         自定义分区策略

2.消费者的基本实现:

 public class MyConsumer {
    private final static String TOPIC_NAME = "my-replicated-topic";
    private final static String CONSUMER_GROUP_NAME = "testGroup";

    public static void main(String[] args) {
        // 1.设置参数
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
                "192.168.56.10:9092, 192.168.56.10:9093, 192.168.56.10:9094");
        // 消费分组名
        props.put(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP_NAME);
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());


        // 2.创建一个消费者客户端
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        // 3.消费者订阅主题列表
        consumer.subscribe(Arrays.asList(TOPIC_NAME));

        // 4.poll消息
        while (true){
            // poll()是拉取消息的长循环
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));    // 每次花1s拉取消息,拉完就往下执行
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("收到消息:partition = %d, offset = %d, key = %s, value = %s",
                        record.partition(), record.offset(), record.key(), record.value());
            }
        }
    }

}

                 =======================基本配置==========================

  ★ 消费者提交方式   无论是手动还是自动提交,都要把消费者所属消费组+消费主题+分区+偏移量,这些消息提交到集群的_consumer_offsets主题中
自动提交(默认):消费者poll消息下来后就自动提交offset        会丢失消息,因为消费者在消费前提交offset,可能还没有消费消息就挂了
//        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
//        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");    // 自动提交offset的间隔时间

手动提交:
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
        手动同步提交:在消费完消息后调用同步提交的方法,当集群返回ack前一直阻塞,返回ack后表示提交成功,执行之后的逻辑
       

 while (true){
               // poll()是拉取消息的长循环
              ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
              for (ConsumerRecord<String, String> record : records) {
                    System.out.printf("收到消息:partition = %d, offset = %d, key = %s, value = %s",
                    record.partition(), record.offset(), record.key(), record.value());
              }
             // 所有消息已消费完
             if(records.count() > 0){  // 还有消息
                     // 手动同步提交offset,然后阻塞到offset提交成功(常用手动同步提交,因为一般提交后也没有什么操作了)
                     consumer.commitSync();
             }
        }


       手动异步提交:在消费完消息后提交,不需要等到集群ack,直接执行之后的逻辑,可以设置一个回调方法,供集群调用
      

  while (true){
                // poll()是拉取消息的长循环
               ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
               for (ConsumerRecord<String, String> record : records) {
                     System.out.printf("收到消息:partition = %d, offset = %d, key = %s, value = %s",
                     record.partition(), record.offset(), record.key(), record.value());
               }
              // 所有消息已消费完
              if(records.count() > 0){  // 还有消息
                     // 手动异步提交offset,不会阻塞,可以继续执行后面的逻辑
                     consumer.commitAsync(new OffsetCommitCallback(){
                  @Override
                   public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception){
                           if(exception != null){
                           System.err.println("Commit failed for " + offsets);
                           System.err.println("Commit failed exception: " + exception.getStackTrace());
                   }
                 }
               })
              }
        }


★ 长轮询poll消息
    默认情况下,消费者一次会poll500条消息
        props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);     // 一次poll最大拉取消息的条数,可以根据消费速度的快慢来设置
    设置poll长轮询的时间为1000ms
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));    // 每次花1s来拉取消息
    只要满足poll的条数要求或者时间要求,就可以继续往下执行for循环。 也就是说,如果在1s内poll的条数达到500条或者到了1s还没有poll到500条消息,会往下执行。

    如果两次poll的间隔超过30s,集群会认为该消费者的消费能力过弱,将该消费者踢出消费组,触发rebalance机制(造成性能开销),可以让一次poll的消息条数少一些。
        props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000);

 ★ 消费者的健康状态检查
    消费者每隔1s就给kafka集群发送心跳
        props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000);
    如果集群有10s没接收到心跳, 就把该消费者踢出消费组,触发该组的rebalance机制,将该分区分给该组的其它消费者
        props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000);
★ 指定分区、偏移量或时间进行消费
   指定分区0进行消费:
        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)));    //从头开始消费
//        consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);    //指定offset从10开始消费
   指定时间进行消费:根据时间,去所有的partition中确定该时间对应的offset,然后去所有的partition中找到该offset之后的消息开始消费
   

 List<PartitionInfo> topicPartitions = consumer.partitionsFor(TOPIC_NAME);
      //从1小时前开始消费
      long fetchDataTime = new Date().getTime - 1000 * 60 * 60;
      Map<TopicPartition, Long> map = new HashMap<>();
        for(PartitionInfo par=topicPartitions){
                map.put(new TopicPartition(TOPIC_NAME,par.partition()),fetchDataTime);
                }
                Map<TopicPartition, OffsetAndTimestamp> parMap=consumer.offsetsForTimes(map);
        for(Map.Entry(TopicPartition,OffsetAndTimestamp)entry:parMap.entrySet()){
        TopicPartition key=entry.getKey();
        OffsetAndTimestamp value=entry.getValue();
        if(key==null||value==null)continue;
        Long offset=value.offset();
        System.out.println("partition-"+key.partition()+"|offset-"+offset);
        if(value!=null){
        consumer.assign(Arrays.asList(key));
        consumer.seek(key,offset);
        }
        }


 ★ 新消费组的消费offset规则
    新消费组的消费者在启动之后,默认会从当前分区的最后一条消息的offset+1开始消费(新消息)
       props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")    // 默认从新消息开始消费
       props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest")    // 第一次从头开始消费,之后开始消费新消息(最后消费的offset+1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值