Kafka入门笔记(三) -- kafka生产者

本文详细介绍了ApacheKafka中的Producer组件,包括消息发送流程、异步和同步发送API、事务模式以及配置参数。重点讲解了异步API的使用、ack模式的选择以及如何实现同步发送和事务处理。
摘要由CSDN通过智能技术生成


前言

建议对kafka还不了解的小伙伴先看此文章:Kafka入门笔记(一) --kafka概述+kafka集群搭建Kafka入门笔记(二) --kafka常用命令


一、Producer(生产者)

1、消息发送流程

  • Kafka 的 Producter 发送消息采用的是异步发送的方式。涉及两个线程:main线程和Sender线程,以及一个线程共享变量:RecordAccumulator。
  • 执行过程:main线程将消息发送给RecordAccumulator,Sender线程不断从RecordAccumulator中拉取消费发送到Kafka broker。
  • 发送消息流程:在这里插入图片描述
    相关参数:
    1) batch.size:只有数据累积到batch.size之后,sender才会发送数据;
    2)linger.ms:如果数据迟迟未达到batch.size,sender等待linger.time之后就会发送数据。

2、异步发送API

  1. 导入kafka依赖:
<!--kafka依赖-->
 <dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>0.11.0.0</version>
</dependency>
<!--Json转换相关依赖-->
<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib-ext-spring</artifactId>
    <version>1.0.2</version>
</dependency>
  1. 涉及类
  • ProducerConfig:获取所需的一系列配置参数:
// 生产者相关配置
Properties properties = new Properties();
//设置kafka集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094");
//ack模式
properties.put(ProducerConfig.ACKS_CONFIG,"all");
//发送失败重试次数
properties.put(ProducerConfig.RETRIES_CONFIG,"3");
//批次大小
properties.put(ProducerConfig.BATCH_SIZE_CONFIG,"16384");
//等待时间
properties.put(ProducerConfig.LINGER_MS_CONFIG,1);
//缓存区大小
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,3355432);
//key序列化 
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
//value序列化
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");

ack应答机制:kafka为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行配置。
acks参数配置:
① 0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没写入磁盘就以及返回,当broker故障时有可能丢失数据;
② 1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;
③ -1(all):producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack,但是如果在follower同步完成后,broker发送ack之前,leader发生故障,那么会造成数据重复。

  • KafkaProducer:需要创建一个生产者对象,用来发送数据
//创建生产者对象
Producer producer = new  KafkaProducer<>(properties);
  • ProducerRecord:每条数据都要封装成一个ProducerRecord对象
//生产者要发送的消息
map = new HashMap<>();
map.put("globalTaskId","T2024011600007");
JSONObject json = JSONObject.fromObject(map);
// ProducerRecord(topic,key,val)
producer.send(new ProducerRecord("modelTopic","risk", json.toString()));
//关闭资源
producer.close();
  1. API带回调函数的生产者
    ProducerConfig配置同上就不复述了。
    方法一:发送数据,并创建回调函数
//创建生产者对象
Producer producer = new  KafkaProducer<>(properties);
map = new HashMap<>(4);
map.put("globalTaskId","T2024011600007");
JSONObject json = JSONObject.fromObject(map);
producer.send(new ProducerRecord("modelTopic", "risk", json.toString()), new Callback() {
    @Override
    public void onCompletion(RecordMetadata recordMetadata, Exception e) {
        if (e == null) {
            System.out.println("finalI:" + finalI +";topic:" + recordMetadata.topic() + ";offset:" + recordMetadata.offset()
                    + ";partition:" + recordMetadata.partition() + ";serializedKeySize:" + recordMetadata.serializedKeySize()
                    + ";serializedValueSize:" + recordMetadata.serializedValueSize() + ";timestamp:" + recordMetadata.timestamp());
        }else{
            e.printStackTrace();
        }
    }
});
//关闭资源
producer.close();

方法二:发送数据,使用lambda表达式创建回调函数

//创建生产者对象
Producer producer = new  KafkaProducer<>(properties);
map = new HashMap<>(4);
map.put("globalTaskId","T2024011600007");
JSONObject json = JSONObject.fromObject(map);
producer.send(new ProducerRecord("modelTopic","risk", json.toString()), (recordMetadata, e) -> {
   if (e == null) {
       System.out.println("finalI:" + finalI +";topic:" + recordMetadata.topic() + ";offset:" + recordMetadata.offset()
               + ";partition:" + recordMetadata.partition() + ";serializedKeySize:" + recordMetadata.serializedKeySize()
               + ";serializedValueSize:" + recordMetadata.serializedValueSize() + ";timestamp:" + recordMetadata.timestamp());
   }else{
       e.printStackTrace();
   }
});
//关闭资源
producer.close();
  1. API自定义分区的生产者
    ①自定义分区类
package com.ts.kafka.producer;

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;

import java.util.Map;

/**
 * @author
 */
public class MyPartitioner implements Partitioner {

    @Override
    public int partition(String topic, Object key, byte[] bytes, Object o1, byte[] bytes1, Cluster cluster) {
        // 自定义分区方法
        //获取该topic分区数
        Integer integer = cluster.partitionCountForTopic(topic);
        //分区逻辑
        return key.toString().hashCode() % integer;
    }
    @Override
    public void close() {}
    @Override
    public void configure(Map<String, ?> map) {}
}

②分区器的使用,在配置中添加分区器

//在生产者相关配置中添加分区器
properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"com.ts.kafka.producer.MyPartitioner");

3、同步发送API

  1. 同步发送:一条消息发送之后,会阻塞当前线程,直至返回ack;
  2. 由于send方法返回的是一个Future对象,根据Futrue对象的特点,我们也可以实现同步发送的效果,只需在调用Future对象的get方法即可;
  3. 具体实现如下:
//发送消息
// 返回 Future
Future<RecordMetadata> send = producer.send(new ProducerRecord("topic", "key", "val"));
try {
	// 阻塞
    RecordMetadata metadata =  send.get();
    System.out.println("partition:"+metadata.partition());
} catch (ExecutionException e) {
    e.printStackTrace();
}
  1. 在同一分区,又同一个同步发送,保证发送数据与接收到顺序一直

4、事务模式

  1. 事务模式要求数据发送必须包含在事务中,在事务中可以向多个topic发送数据,消费者端最好也使用事务模式读,保证一次能将整个事务的数据全部读取过来。当然消费者也可以不设置为事务读的模式。
  2. 事务包括:
    • 初始化事务:producer.initTransactions();
    • 开始事务:producer.beginTransaction();
    • 提交事务:producer.commitTransaction();
    • 终止事务:producer.abortTransaction();
  3. 实现如下:
 public void ProducerData(){
     Properties properties = new Properties();
     properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094");
     //ack模式
     properties.put(ProducerConfig.ACKS_CONFIG,"all");
     //发送失败重试次数
     properties.put(ProducerConfig.RETRIES_CONFIG,"3");
     //批次大小
     properties.put(ProducerConfig.BATCH_SIZE_CONFIG,"16384");
     //等待时间
     properties.put(ProducerConfig.LINGER_MS_CONFIG,1);
     //缓存区大小
     properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG,3355432);
     //key序列化
     properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
     //value序列化
     properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
     Producer producer = new  KafkaProducer<>(properties);
     //初始化事务
     producer.initTransactions();
     try {
         //数据发送必须在beginTransaction()和commitTransaction()中间,否则会报状态不对的异常
         //开始事务 
         producer.beginTransaction(); 
         for (int i = 0; i < 100; i++) {
         	producer.send(new ProducerRecord<>("mytopic1", Integer.toString(i), Integer.toString(i)));
     	}
      //提交事务
      producer.commitTransaction();
	}catch (ProducerFencedException | Out0fOrderSeguenceException |AuthorizationException e){
	   //这些异常不能被恢复,因此必须要关闭并退出
	   producer.close();
	} catch (KafkaException e){
	   //出现其它异常,终止事务
	   producer.abortTransaction();
	}
	producer.close();
}

最近比较忙,kafka消费者相关的API使用在下一节更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无题白水

一分关爱,无限可能。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值