【kafka】生产者

Kafka 的 Producer发送消息采用的是异步发送的方式。

在消息发送的过程中,涉及到了两个线程——main 线程和 Sender 线程,以及一个共享变量——RecordAccumulator。

main 线程将消息发送给 RecordAccumulator, Sender 线程不断从 RecordAccumulator 中拉取消息发送到 Kafka broker。

/**
     * 注意:消息发送失败会自动重试,不需要我们在回调函数中手动重试。
     */
    public static void send() throws ExecutionException, InterruptedException {
        Properties props = new Properties();
        //kafka 集群,broker-list
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
        props.put(ProducerConfig.ACKS_CONFIG, "all");
        //重试次数;消息发送失败会自动重试,不需要我们在回调函数中手动重试
        props.put(ProducerConfig.RETRIES_CONFIG, 1);
        //批次大小.只有数据积累到 batch.size 之后,sender 才会发送数据。
        props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
        //等待时间.如果数据迟迟未达到 batch.size,sender 等待 linger.time 之后就会发送数据。
        props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
        //RecordAccumulator 缓冲区大小:32M
        props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
        
         //构建拦截链
        List<String> interceptors = new ArrayList<String>();
        interceptors.add(TimeInterceptor.class.getName());
        //添加拦截器
        props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);

        //key、value的fan序列化
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                StringSerializer.class.getName());

        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                StringSerializer.class.getName());
        //自定义分区器
        props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,
                MyPartition.class.getName());
        KafkaProducer<String, String> producer = new KafkaProducer<String, String>(props);
        for (int i = 0; i < 100; i++) {
            //发送到的主题
            String topic = "firsTopic";
            //key可做分区使用
            String key = Integer.toString(i);
            //value为消息数据
            String value = Integer.toString(i);
//            int partition=1;
//            ProducerRecord<String, String> record = new ProducerRecord<String, String>(topic, partition,key, value);
            ProducerRecord<String, String> record = new ProducerRecord<String, String>(topic, key, value);

            //(1)只管发,没有回调函数
            //Future<RecordMetadata> future = producer.send(record);

            //(2)Producer 收到 ack 时,异步回调Callback函数
            //如果 Exception 为 null,说明消息发送成功,反之为失败
            Future<RecordMetadata> future = producer.send(record, new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    if (exception == null) {
                        System.err.println("Producer收到消息发送成功的ack:" + metadata.offset());
                    } else {
                        //消息发送失败会自动重试,不需要我们在回调函数中手动重试
                        System.err.println("Producer收到消息发送失败的ack");
                        exception.printStackTrace();
                    }
                }
            });
            //返回future,可以根据需要调用get阻塞线程
            //future.get();
        }
        producer.close();
    }
/**
 * 自定义分区器
 */
public class MyPartition implements Partitioner {

    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        //根据不同业务,可以根据key、value等 返回分区id
        return key.toString().hashCode() % 3;
    }

    @Override
    public void close() {
    }

    @Override
    public void configure(Map<String, ?> map) {
    }
}

 实现一个简单的双 interceptor 组成的拦截链。第一个 interceptor 会在消息发送前将时间 戳信息加到消息 value 的最前部;第二个 interceptor 会在消息发送后更新成功发送消息数或 失败发送消息数。

public class TimeInterceptor implements ProducerInterceptor<String, String> {

    //获取配置信息和初始化数据时调用。
    @Override
    public void configure(Map<String, ?> configs) {
    }

    //该方法封装进 KafkaProducer.send 方法中,即它运行在用户主线程中。Producer 确保在
    //消息被序列化以及计算分区前调用该方法。
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        // 创建一个新的 record,把时间戳写入消息体的最前部
        String topic = record.topic();
        int partition = record.partition();
        String key = record.key();
        String value = record.value();
        String newValue=System.currentTimeMillis() + "," + value;
        Long timestamp = record.timestamp();
        ProducerRecord recordResult= new ProducerRecord(topic, partition, timestamp, key, newValue);
        return recordResult;
    }

    //该方法会在消息从 RecordAccumulator 成功发送到 Kafka Broker 之后,或者在发送过程
    //中失败时调用。
    @Override
    public void onAcknowledgement(RecordMetadata metadata,
                                  Exception exception) {
    }

    //关闭 interceptor,主要用于执行一些资源清理工作
    @Override
    public void close() {
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值