发送消息(二)RoutingKafkaTemplate,DefaultKafkaProducerFactory和 ReplyingKafkaTemplate

本文介绍了Spring Kafka中的RoutingKafkaTemplate,强调了其根据目标topic动态选择生产者的能力以及使用前提。接着详细讲解了DefaultKafkaProducerFactory的创建和更新过程,以及如何避免多线程问题。最后,讨论了ReplyingKafkaTemplate的使用,它是KafkaTemplate的子类,用于实现请求/回复功能。
摘要由CSDN通过智能技术生成

一、RoutingKafkaTemplate

1.1、RoutingKafkaTemplate 能做什么

RoutingKafkaTemplate可以根据目标topic名称在运行时选择生产者。

RoutingKafkaTemplate 不支持事务、execute、flush或metrics操作,因为这些操作的主题未知。

1.2、使用前提

RoutingKafkaTemplate 和 KafkaTemplate使用前要做的工作差不多,都需要一个前期的配置。
下面是一个例子:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public RoutingKafkaTemplate routingTemplate(GenericApplicationContext context,
            ProducerFactory<Object, Object> pf) {

        // 使用不同的序列化程序克隆生产者工厂,向Spring注册以关闭
        Map<String, Object> configs = new HashMap<>(pf.getConfigurationProperties());
        configs.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
        DefaultKafkaProducerFactory<Object, Object> bytesPF = new DefaultKafkaProducerFactory<>(configs);
        context.registerBean(DefaultKafkaProducerFactory.class, "bytesPF", bytesPF);

        Map<Pattern, ProducerFactory<Object, Object>> map = new LinkedHashMap<>();
        map.put(Pattern.compile("two"), bytesPF);
        map.put(Pattern.compile(".+"), pf); // 带StringSerializer的默认生产者工厂
        return new RoutingKafkaTemplate(map);
    }

    @Bean
    public ApplicationRunner runner(RoutingKafkaTemplate routingTemplate) {
        return args -> {
            routingTemplate.send("one", "thing1");
            routingTemplate.send("two", "thing2".getBytes());
        };
    }

}

二、使用 DefaultKafkaProducerFactory

2.1、创建

在KafkaTemplate配置的时候,有这么一段代码:

@Bean
public ProducerFactory<Integer, String> producerFactory() {
    return new DefaultKafkaProducerFactory<>(producerConfigs());
}

它用于创建生产者,在不使用事务的前提下 DefaultKafkaProducerFactory 会创建一个由所有客户端使用的单例生产者,
这个状态下如果调用flush()模板,可能会导致使用同一生产者的其他线程出现延迟。要避免这种情况可以将属性producerPerThread设置为true,这个时候工厂将为每个线程创建(并缓存)一个单独的生产者。

当producerPerThread是true且不再需要生产者时,用户代码必须调用 closeThreadBoundProducer()从物理上关闭生产者并将其从 ThreadLocal 中删除. 调用reset()或destroy()不会清理这些生产者。

创建 DefaultKafkaProducerFactory 时,可以通过调用仅接受属性映射的构造函数从配置中获取键和值,
或者可以将 Serializer 实例传递给 DefaultKafkaProducerFactory 构造函数(在 在这种情况下,所有 Producer 共享相同的实例)。
或者,提供Supplier,它将用于为每个生产者获取单独的Serializer实例:

@Bean
public ProducerFactory<Integer, CustomValue> producerFactory() {
    return new DefaultKafkaProducerFactory<>(producerConfigs(), null, () -> new CustomValueSerializer());
}

@Bean
public KafkaTemplate<Integer, CustomValue> kafkaTemplate() {
    return new KafkaTemplate<Integer, CustomValue>(producerFactory());
}

2.2、更新

在创建工厂后更新生产者属性可以调用:

void updateConfigs(Map<String, Object> updates);

void removeConfig(String configKey);

这两个方法不会影响现有的生产者实例,调用reset()可以关闭任何现有的生产者,之后使用新属性创建新的生产者。

不能将事务性生产者工厂更改为非事务性工厂,或者颠倒更改

三、使用 ReplyingKafkaTemplate

3.1、创建和简单使用

ReplyingKafkaTemplate是KafkaTemplate的子类,用来提供请求/回复。

@SpringBootApplication
public class KRequestingApplication {

    public static void main(String[] args) {
        SpringApplication.run(KRequestingApplication.class, args).close();
    }

    @Bean
    public ApplicationRunner runner(ReplyingKafkaTemplate<String, String, String> template) {
        return args -> {
            ProducerRecord<String, String> record = new ProducerRecord<>("kRequests", "foo");
            RequestReplyFuture<String, String, String> replyFuture = template.sendAndReceive(record);
            SendResult<String, String> sendResult = replyFuture.getSendFuture().get(10, TimeUnit.SECONDS);
            System.out.println("Sent ok: " + sendResult.getRecordMetadata());
            ConsumerRecord<String, String> consumerRecord = replyFuture.get(10, TimeUnit.SECONDS);
            System.out.println("Return value: " + consumerRecord.value());
        };
    }

    @Bean
    public ReplyingKafkaTemplate<String, String, String> replyingTemplate(
            ProducerFactory<String, String> pf,
            ConcurrentMessageListenerContainer<String, String> repliesContainer) {

        return new ReplyingKafkaTemplate<>(pf, repliesContainer);
    }

    @Bean
    public ConcurrentMessageListenerContainer<String, String> repliesContainer(
            ConcurrentKafkaListenerContainerFactory<String, String> containerFactory) {

        ConcurrentMessageListenerContainer<String, String> repliesContainer =
                containerFactory.createContainer("replies");
        repliesContainer.getContainerProperties().setGroupId("repliesGroup");
        repliesContainer.setAutoStartup(false);
        return repliesContainer;
    }

    @Bean
    public NewTopic kRequests() {
        return TopicBuilder.name("kRequests")
            .partitions(10)
            .replicas(2)
            .build();
    }

    @Bean
    public NewTopic kReplies() {
        return TopicBuilder.name("kReplies")
            .partitions(10)
            .replicas(2)
            .build();
    }

}

可以使用 Boot 的自动配置容器工厂来创建回复容器,比如配置回复容器并使用相同共享回复主题

@Bean
public ConcurrentMessageListenerContainer<String, String> replyContainer(
        ConcurrentKafkaListenerContainerFactory<String, String> containerFactory) {

    ConcurrentMessageListenerContainer<String, String> container = containerFactory.createContainer("topic2");
    container.getContainerProperties().setGroupId(UUID.randomUUID().toString()); // 唯一的
    Properties props = new Properties();
    props.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); // 不会收到旧回复
    container.getContainerProperties().setKafkaConsumerProperties(props);
    return container;
}

3.2、使用 Message<?> 回复/请求

Message<?>是 ReplyingKafkaTemplate 的一个抽象方法。

@KafkaListener(id = "requestor", topics = "request")
@SendTo
public Message<?> messageReturn(String in) {
    return MessageBuilder.withPayload(in.toUpperCase())
            .setHeader(KafkaHeaders.TOPIC, replyTo)
            .setHeader(KafkaHeaders.MESSAGE_KEY, 42)
            .setHeader(KafkaHeaders.CORRELATION_ID, correlation)
            .build();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

timi先生

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值