kafka发送者与消费者参数详解

kafka发送端​​​​​​​

全量代码示例

private final static String TOPIC_NAME = "my-replicated-topic"; public static void main(String[] args) throws InterruptedException, ExecutionException { Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094"); props.put(ProducerConfig.ACKS_CONFIG, "1"); props.put(ProducerConfig.RETRIES_CONFIG, 3); props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 300); props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); props.put(ProducerConfig.LINGER_MS_CONFIG, 10); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); Producer<String, String> producer = new KafkaProducer<String, String>(props); int msgNum = 5; final CountDownLatch countDownLatch = new CountDownLatch(msgNum); for (int i = 1; i <= msgNum; i++) { Order order = new Order(i, 100 + i, 1, 1000.00); ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME , order.getOrderId().toString(), JSON.toJSONString(order)); RecordMetadata metadata = producer.send(producerRecord).get(); System.out.println("同步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset()); //异步回调方式发送消息 /*producer.send(producerRecord, new Callback() { public void onCompletion(RecordMetadata metadata, Exception exception) { if (exception != null) { System.err.println("发送消息失败:" + exception.getStackTrace()); } if (metadata != null) { System.out.println("异步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset()); } countDownLatch.countDown(); } });*/ } countDownLatch.await(5, TimeUnit.SECONDS); producer.close(); }

ProducerConfig

BOOTSTRAP_SERVERS_CONFIG

示例:

props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094");

集群连接的ip+端口

ACKS_CONFIG

示例:

props.put(ProducerConfig.ACKS_CONFIG, "1");

发出消息持久化机制参数

  • acks=0: 表示producer不需要等待任何broker确认收到消息的回复,就可以继续发送下一条消息。性能最高,但是最容易丢消息
  • acks=1: 至少要等待leader已经成功将数据写入本地log,但是不需要等待所有follower是否成功写入。就可以继续发送下一条消息。这种情况下,如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失
  • acks=-1或all: 需要等待 min.insync.replicas(默认为1,推荐配置大于等于2) 这个参数配置的副本个数都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的数据保证。一般除非是金融级别,或跟钱打交道的场景才会使用这种配置

RETRIES_CONFIG

示例:

props.put(ProducerConfig.RETRIES_CONFIG, 3);

发送失败的重试次数,默认重试间隔100ms,重试能保证消息发送的可靠性,但是也可能造成消息重复发送,比如网络抖动,所以需要在接收者那边做好消息接收的幂等性处理

RETRY_BACKOFF_MS_CONFIG

示例:

props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 300);

重试时间间隔设置

BUFFER_MEMORY_CONFIG

示例:

props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);

设置发送消息的本地缓冲区,如果设置了该缓冲区,消息会先发送到本地缓冲区,可以提高消息发送性能,默认值是33554432,即32MB

BATCH_SIZE_CONFIG

示例:

props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);

kafka本地线程会从缓冲区取数据,批量发送到broker,设置批量发送消息的大小,默认值是16384,即16kb,就是说一个batch满了16kb就发送出去

LINGER_MS_CONFIG

示例:

props.put(ProducerConfig.LINGER_MS_CONFIG, 10);

发送消息的延时配置:

  • 默认值是0,意思就是消息必须立即被发送,但这样会影响性能
  • 一般设置10毫秒左右,就是说这个消息发送完后会进入本地的一个batch,如果10毫秒内,这个batch满了16kb就会随batch一起被发送出去
  • 如果10毫秒内,batch没满,那么也必须把消息发送出去,不能让消息的发送延迟时间太长

KEY_SERIALIZER_CLASS_CONFIG

示例:

props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

把发送的key从字符串序列化为字节数组

VALUE_SERIALIZER_CLASS_CONFIG

示例:

props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

把发送消息value从字符串序列化为字节数组

消息发送方式

不管是那种发送方式,都合理的运用了Future来提高性能

同步发送

org.apache.kafka.clients.producer.Producer#send(org.apache.kafka.clients.producer.ProducerRecord)

示例:

RecordMetadata metadata = producer.send(producerRecord).get();

阻塞等待返回的结果,对于不太敏感的动作,可以放在send之后,get之前来做

异步发送

org.apache.kafka.clients.producer.Producer#send(org.apache.kafka.clients.producer.ProducerRecord, org.apache.kafka.clients.producer.Callback)

producer.send(producerRecord, new Callback() { public void onCompletion(RecordMetadata metadata, Exception exception) { if (exception != null) { System.err.println("发送消息失败:" + exception.getStackTrace()); } if (metadata != null) { System.out.println("异步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-" + metadata.partition() + "|offset-" + metadata.offset()); } countDownLatch.countDown(); } });

异步的方式可以更好的提升性能

kafka消费端

全量代码示例

private final static String TOPIC_NAME = "my-replicated-topic"; private final static String CONSUMER_GROUP_NAME = "testGroup"; public static void main(String[] args) throws Exception { Properties props = new Properties(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094"); props.put(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP_NAME); /*props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");*/ props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); //props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000); props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000); props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 50); props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList(TOPIC_NAME)); //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.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0))); consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);*/ /*List<PartitionInfo> topicPartitions = consumer.partitionsFor(TOPIC_NAME); 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); System.out.println(); //根据消费里的timestamp确定offset if (value != null) { consumer.assign(Arrays.asList(key)); consumer.seek(key, offset); } }*/ while (true) { 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%n", record.partition(), record.offset(), record.key(), record.value()); } if (records.count() > 0) { // 手动同步提交offset,当前线程会阻塞直到offset提交成功 // 一般使用同步提交,因为提交之后一般也没有什么逻辑代码了 //consumer.commitSync(); // 手动异步提交offset,当前线程提交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()); } } });*/ } } }

ConsumerConfig

BOOTSTRAP_SERVERS_CONFIG

示例:

props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094");

集群连接的ip+端口与生产者一致,相同的还有KEY_DESERIALIZER_CLASS_CONFIG和VALUE_DESERIALIZER_CLASS_CONFIG

GROUP_ID_CONFIG

示例:

props.put(ConsumerConfig.GROUP_ID_CONFIG, CONSUMER_GROUP_NAME);

消费分组名

ENABLE_AUTO_COMMIT_CONFIG

示例:

props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");

是否定期自动提交offset,默认为true

AUTO_COMMIT_INTERVAL_MS_CONFIG

示例:

props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");

自动提交offset的间隔时间

AUTO_OFFSET_RESET_CONFIG

示例:

props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");

当前参数的含义主要是:当消费主题的是一个新的消费组,或者指定offset的消费方式,offset不存在,那么应该如何消费

  • latest(默认) :只消费自己启动之后发送到主题的消息
  • earliest:第一次从头开始消费,以后按照消费offset记录继续消费,这个需要区别于consumer.seekToBeginning(每次都从头开始消费)

HEARTBEAT_INTERVAL_MS_CONFIG

示例:

props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000);

consumer给broker发送心跳的间隔时间,broker接收到心跳如果此时有rebalance发生会通过心跳响应将rebalance方案下发给consumer,这个时间可以稍微短一点

SESSION_TIMEOUT_MS_CONFIG

示例:

props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000);

服务端broker多久感知不到一个consumer心跳就认为他故障了,会将其踢出消费组,对应的Partition也会被重新分配给其他consumer,默认是10秒

MAX_POLL_RECORDS_CONFIG

示例:

props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 50);

一次poll最大拉取消息的条数,如果消费者处理速度很快,可以设置大点,如果处理速度一般,可以设置小点

MAX_POLL_INTERVAL_MS_CONFIG

示例:

props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000);

如果两次poll操作间隔超过了这个时间,broker就会认为这个consumer处理能力太弱,会将其踢出消费组,将分区分配给别的consumer消费

消息消费方式

普通消费

示例:

consumer.subscribe(Arrays.asList(TOPIC_NAME))

指定topic即可

指定分区消费

示例:

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)));

对于某个分区的消息,重新消费,无需考虑偏移量,需要指定分区

指定offset消费

示例:

consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0))); consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);

传入消息的偏移量消费,需要指定分区,因为只有在同一个分区下,偏移量才是唯一的

从指定时间点开始消费

示例:

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); System.out.println(); //根据消费里的timestamp确定offset if (value != null) { consumer.assign(Arrays.asList(key)); consumer.seek(key, offset); } }

可以指定某个具体的时间,从那儿开始往后消费

消费消息确认方式

同步确认

示例:

consumer.commitSync();

异步确认

示例:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在 Kafka 消费者进程中使用 WebClient 发起网络请求,您可以使用 Spring Framework 中的 WebClient 库来完成。WebClient 是一种异步、非阻塞的 HTTP 客户端,可以与任何 HTTP 服务进行交互,并支持许多功能,例如基本身份验证、Cookie 管理和文件上传等。 以下是一些使用 Spring WebClient 发起网络请求的示例代码: 首先,您需要添加以下 Maven 依赖项: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> ``` 然后,您可以使用 WebClient 创建一个 GET 请求: ``` WebClient client = WebClient.create(); client.get() .uri("http://example.com") .retrieve() .bodyToMono(String.class) .subscribe(System.out::println); ``` 此代码将向 http://example.com 发送一个 GET 请求,并将响应主体转换为字符串。当收到响应时,它将在控制台上打印响应。 如果您需要向服务器发送 POST 请求并传递一些数据,您可以使用以下代码: ``` Mono<Void> result = WebClient.create() .post() .uri("http://example.com") .contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue("{\"name\": \"John\", \"age\": 30}")) .retrieve() .bodyToMono(Void.class); result.block(); ``` 这个例子将使用 JSON 格式的数据向 http://example.com 发送一个 POST 请求。在这种情况下,我们使用了 BodyInserters.fromValue() 方法将数据转换为 JSON 格式,并将 Content-Type 头设置为 application/json。在这个例子中,我们不关心响应体,所以我们将响应体转换为 Void 类型。 需要注意的是,在 Kafka 消费者进程中使用 WebClient 发起网络请求可能会阻塞消费者线程,因此建议使用异步的方式来处理网络请求,以确保消费者线程的及时释放。 ### 回答2: 在Kafka消费者进程中使用WebClient发起网络请求可以通过以下步骤实现: 1. 引入相关依赖:首先,需要在项目的构建文件(如pom.xml或build.gradle)中添加对WebFlux和WebClient的依赖。例如,对于Maven项目,可以在pom.xml文件中添加以下依赖: ```xml <dependencies> <!-- 其他依赖项 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <!-- 其他依赖项 --> </dependencies> ``` 2. 创建WebClient实例:在消费者进程的代码中,首先需要创建一个WebClient实例。可以使用WebClient.builder()方法来创建一个WebClient.Builder对象,并使用不同的配置方法来设置需要的特性,例如超时时间、连接池等。 ```java WebClient webClient = WebClient.builder() .baseUrl("http://example.com") // 设置请求的基本URL .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // 设置请求头 .build(); ``` 3. 发起网络请求:一旦WebClient实例创建好,就可以使用其提供的方法来发起网络请求。常用的方法包括get()、post()、put()、delete()等。根据具体需求,可以设置请求的URL、请求体、请求参数等信息,并通过调用exchange()或retrieve()方法来执行请求并获取响应。 ```java Mono<String> response = webClient.get() .uri("/api/endpoint") // 设置请求的相对URL .retrieve() // 发起请求并获取响应 .bodyToMono(String.class); // 将响应体解析为字符串 response.subscribe(res -> { // 处理响应结果 System.out.println(res); }, err -> { // 处理请求失败的情况 err.printStackTrace(); }); ``` 通过上述步骤,我们就可以在Kafka消费者进程中使用WebClient发起网络请求,并处理返回结果或错误。根据实际需求,还可以配置更多WebClient的特性,例如添加拦截器、设置代理等,以满足不同的业务需求。 ### 回答3: Kafka消费者进程可以通过使用WebClient类,实现通过发起网络请求来进行交互。 首先,我们需要在Kafka消费者的代码中引入WebClient类的相关依赖库。可以使用Maven或者Gradle将以下依赖添加到项目中: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> ``` 然后,在Kafka消费者进程中创建一个WebClient实例,用于发起网络请求。可以通过WebClient.builder()方法来创建实例,并设置相关的请求配置。例如: ```java WebClient webClient = WebClient.builder() .baseUrl("http://example.com") // 设置请求的基础URL .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // 设置请求头 .build(); ``` 接下来,可以使用WebClient实例来发起网络请求。例如,使用GET方法请求一个URL,并读取响应内容: ```java webClient.get() .uri("/api/getData") // 设置请求的URI .retrieve() // 发起请求并获取响应 .bodyToMono(String.class) // 将响应转换为字符串 .subscribe(response -> { System.out.println(response); // 打印响应内容 }); ``` 使用POST方法发送包含请求体的请求: ```java webClient.post() .uri("/api/submitData") // 设置请求的URI .bodyValue(requestData) // 设置请求体数据 .retrieve() // 发起请求并获取响应 .bodyToMono(String.class) // 将响应转换为字符串 .subscribe(response -> { System.out.println(response); // 打印响应内容 }); ``` 需要注意的是,WebClient是一种基于响应式编程的客户端工具,所以使用它时需要注意异步操作和订阅响应的处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值