kafka-consumer1 consumer的一些配置以及同步消费

1 实现在同一个线程消费的consumer

  实现producer

package wyp;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.slf4j.LoggerFactory;

import java.util.Properties;
import java.util.stream.IntStream;

/**
 * 一劳永逸模式
 * 发送以后就不关心server的应答
 */
public class FireAndForgetSend {



    public static void main(String[] args) {
        Properties properties = initPro();
        KafkaProducer<String,String> kafkaProducer = new KafkaProducer<>(properties);
        IntStream.range(0,100).forEach(i -> {
            ProducerRecord<String,String> producerRecord =
                    new ProducerRecord<>("test_c",String.valueOf(i),"hello"+i);
            kafkaProducer.send(producerRecord);
            System.out.println("The message is send done and key is "+i);
        });
        kafkaProducer.flush();
        kafkaProducer.close();

    }


    private  static Properties initPro(){

        final   Properties properties = new Properties();
        properties.put("bootstrap.servers","localhost:9092");
        properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
        return properties;
    }
}

   实现consumer

package wyp.comsumer;

import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;

/**
 * @author : miles wang
 * @date : 2019/7/5  3:34 PM
 * 符合逻辑的消费应该是
 *  收到消息以后,丢给另外一个线程池去异步消费,否则如果处理业务时间过长,或者处理中抛出异常都会是consumer线程死掉
 */
public class SimpleConsumer {

    public static void main(String[] args) {
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(initPro());
        //参数形式一个集合,可以接受多个topic
        //设置消费的topic
        consumer.subscribe(Collections.singletonList("test_c"));
        for (;;){
            ConsumerRecords<String, String> poll = consumer.poll(100);
            toString(poll);

        }

    }

    public static void toString(ConsumerRecords<String,String> records){
      records.forEach(record -> {
          //TODO 业务处理
          String topic = record.topic();
          String time = longToDate(record.timestamp());
          int partition = record.partition();
          System.out.println("*********************************************************");
          System.out.println("topic is :"+topic+"|| " +"time is :"+time+"|| "+"offset is :"+record.offset()+" || "+"partition is :"+partition);

      });

    }
    public static String longToDate(long lo){
        Date date = new Date(lo);
        SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sd.format(date);
    }


    private  static Properties initPro(){

        final   Properties properties = new Properties();
        properties.put("bootstrap.servers","localhost:9092");
        properties.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("group.id","test_group1");
        properties.put("client.id","client-demo1");
        return properties;
    }
}

  开启kafka创建topic

zookeeper-server-start /usr/local/etc/kafka/zookeeper.properties
kafka-server-start /usr/local/etc/kafka/server.properties 
kafka-topics --create --topic test_c --partitions 3 --replication-factor 1 --zookeeper localhost:2182

 测试结果:

2 consumer的一些配置

   fetch.min.bytes:

  该属性指定了消费者从服务器获取记录的最小字节数。broker 在收到消费者的数据请求时,如果可用的数据量小于 fetch.min.bytes 指定的大小,那么它会等到有足够的可用数据时才把它返回给消费者。这样可以降低消费者和 broker 的工作负载,因为它们在主题不是很活跃的时候(或者一天里的低谷时段)就不需要来来回回地处理消息。如果没有很多可用数据,但消费者的 CPU 使用率却很高,那么就需要把该属性的值设得比默认值大。如果消费者的数量比较多,把该属性的值设置得大一点可以降低 broker 的工作负载。

  fetch.max.wait.ms

  我们通过 fetch.min.bytes 告诉 Kafka,等到有足够的数据时才把它返回给消费者。而 feth.max.wait.ms 则用于指定 broker 的等待时间,默认是 500ms。

如果没有足够的数据流入 Kafka,消费者获取最小数据量的要求就得不到满足,最终导致 500ms 的延迟。如果要降低潜在的延迟(为了满足 SLA),可以把该参数值设置得小一些。如果 fetch.max.wait.ms 被设为 100ms,并且 fetch.min.bytes 被设为 1MB,那么 Kafka 在收到消费者的请求后,要么返回 1MB 数据,要么在 100ms 后返回所有可用的数据,就看哪个条件先得到满足。
以上两个属性用人话解释就是:

   假如comsumer把bytes设置为1M,那么消费时候broker至少提供1M的数据,如果不够,那么comsuer就等一等(被阻塞),那么等多久呢,如果一直不够也不能一直等下去,这个值有wait.ms来设置,假如说超过这个时间仍然不够,那么broker有多少数据就会给多少数据

  session.timeout.ms

该属性指定了消费者在被认为死亡之前可以与服务器断开连接的时间,默认是 3s。

如果消费者没有在 session.timeout.ms 指定的时间内发送心跳给群组协调器,就被认为已经死亡,协调器就会触发再均衡,把它的分区分配给群组里的其他消费者。

该属性与 heartbeat.interval.ms 紧密相关。heartbeat.interval.ms 指定了 poll() 方法向协调器发送心跳的频率,session.timeout.ms 则指定了消费者可以多久不发送心跳。

所以,一般需要同时修改这两个属性,heartbeat.interval.ms 必须比 session.timeout.ms 小,一般是 session.timeout.ms 的三分之一。如果session.timeout.ms 是 3s,那么 heartbeat.interval.ms 应该是 1s。把 session.timeout.ms 值设得比默认值小,可以更快地检测和恢复崩溃的节点,不过长时间的轮询或垃圾收集可能导致非预期的再均衡。把该属性的值设置得大一些,可以减少意外的再均衡,不过检测节点崩溃需要更长的时间。

  auto.offset.reset

该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下(因消费者长时间失效,包含偏移量的记录已经过时并被删除)的处理方式。

它的默认值是 latest,意思是说,在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)。另一个值是 earliest,意思是说,在偏移量无效的情况下,消费者将从起始位置读取分区的记录。

  enable.auto.commit

  明确一个概念 offset是有consumer自己维护的,但是由kafka的broker保存,也就是说,consumer第一次读取数据假如offset是1000,那么它下次读取数据还会从这个offset开始读

这个offset的值由consumer自己控制,但是consumer会提交offset交由broker保存。

  我们稍后将介绍几种不同的提交偏移量的方式。该属性指定了消费者是否自动提交偏移量,默认值是 true。为了尽量避免出现重复数据和数据丢失,可以把它设为 false,由自己控制何时提交偏移量。如果把它设为 true,还可以通过配置 auto.commit.interval.ms 属性来控制提交的频率(多长时间提交一次)。

  默认为true。如果consumer在消费过程中意外死亡,没有来得及提交offset给broker,下次读的时候就会读取重复数据,但是在生产环境中,可以忍受数据重复,无法忍受数据丢失。

关于读取重复消息

    其实consumer是有很多情况读取重复消息的,比如1 producer提交message给broker, broker会返回ack,如果返回ack的时候意外中断,producer会再次发送消息,那么消息就重复了

  2 consumer未来得及提交offset就意外中断

partition.assignment.strategy
我们知道,分区会被分配给群组里的消费者。PartitionAssignor 根据给定的消费者和主题,决定哪些分区应该被分配给哪个消费者。Kafka 有两个默认的分配策略。

  Range
该策略会把主题的若干个连续的分区分配给消费者。

假设消费者 C1 和消费者 C2 同时订阅了主题 T1 和主题 T2,并且每个主题有 3 个分区。那么消费者 C1 有可能分配到这两个主题的分区 0 和分区 1,而消费者 C2 分配到这两个主题的分区 2。因为每个主题拥有奇数个分区,而分配是在主题内独立完成的,第一个消费者最后分配到比第二个消费者更多的分区。只要使用了 Range 策略,而且分区数量无法被消费者数量整除,就会出现这种情况。

  RoundRobin
该策略把主题的所有分区逐个分配给消费者。

依旧是上面的例子:假设消费者 C1 和消费者 C2 同时订阅了主题 T1 和主题 T2,并且每个主题有 3 个分区。如果使用 RoundRobin 策略来给消费者 C1 和消费者 C2 分配分区,那么消费者 C1 将分到主题 T1 的分区 0 和分区 2 以及主题 T2 的分区 1,消费者 C2 将分配到主题 T1 的分区 1 以及主题 T2 的分区 0 和分区 2。一般来说,如果所有消费者都订阅相同的主题(这种情况很常见),RoundRobin 策略会给所有消费者分配相同数量的分区(或最多就差一个分区)。

max.poll.records

  该属性用于控制单次调用 call() 方法能够返回的记录数量,可以帮你控制在轮询里需要处理的数据量。

 

 

 

 

    

 

转载于:https://www.cnblogs.com/wangpipi/p/11139642.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值