You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于条件实时拆分Kafka Connect同步的ES数据主题

用Kafka Streams实现基于关键词的主题实时拆分

核心思路

直接用Kafka Streams做流式处理:从ES同步过来的源主题消费消息,解析payload里的description字段,匹配关键词后转发到对应目标主题。相比Python消费,Streams原生基于Kafka的消费/生产机制,自带容错、负载均衡和低延迟,完全满足实时性要求。

具体实现步骤(Java版,Kafka Streams原生支持最优)

1. 基础配置

先搭好Streams应用的核心配置,指定Kafka地址、应用ID(用来做状态管理)和默认的序列化/反序列化器:

import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.common.serialization.Serdes;
import java.util.Properties;

Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "es-data-splitter");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "your-kafka-broker:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());

2. 读取源主题并解析Payload

假设同步过来的消息是JSON格式,用Jackson解析payload,提取description字段:

import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.kstream.KStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

StreamsBuilder builder = new StreamsBuilder();
// 替换成你的ES同步源主题
KStream<String, String> sourceStream = builder.stream("prefix_your_es_index");

ObjectMapper jsonMapper = new ObjectMapper();

3. 关键词匹配与主题转发

分两种场景处理:

场景1:一个消息只匹配一个关键词(互斥转发)

用branch方法把流拆分成多个分支,每个分支对应一个关键词,最后分别转发到目标主题:

import org.apache.kafka.streams.kstream.Predicate;

// 定义每个关键词的匹配规则
Predicate<String, String> matchEnergy = (key, value) -> {
    try {
        Map<String, Object> payload = jsonMapper.readValue(value, Map.class);
        String desc = (String) payload.get("description");
        return desc != null && desc.contains("energy");
    } catch (Exception e) {
        return false;
    }
};

Predicate<String, String> matchElectric = (key, value) -> {
    try {
        Map<String, Object> payload = jsonMapper.readValue(value, Map.class);
        String desc = (String) payload.get("description");
        return desc != null && desc.contains("electric");
    } catch (Exception e) {
        return false;
    }
};

Predicate<String, String> matchTemperature = (key, value) -> {
    try {
        Map<String, Object> payload = jsonMapper.readValue(value, Map.class);
        String desc = (String) payload.get("description");
        return desc != null && desc.contains("temperature");
    } catch (Exception e) {
        return false;
    }
};

// 分支:最后一个Predicate是匹配所有未命中的消息
KStream<String, String>[] streamBranches = sourceStream.branch(
    matchEnergy,
    matchElectric,
    matchTemperature,
    (key, value) -> true
);

// 每个分支转发到对应主题
streamBranches[0].to("topic_energy");
streamBranches[1].to("topic_electric");
streamBranches[2].to("topic_temperature");
// 未命中的消息转发到备用主题
streamBranches[3].to("topic_unmatched");

场景2:一个消息可能匹配多个关键词(多主题转发)

如果一条消息的description同时包含多个关键词(比如既提到energy又提到electric),需要同时转发到多个主题,用foreach遍历处理:

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;

// 配置Producer(也可以复用Streams的配置)
Properties producerProps = new Properties();
producerProps.put("bootstrap.servers", "your-kafka-broker:9092");
producerProps.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
producerProps.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(producerProps);

sourceStream.foreach((key, value) -> {
    try {
        Map<String, Object> payload = jsonMapper.readValue(value, Map.class);
        String desc = (String) payload.get("description");
        if (desc == null) {
            producer.send(new ProducerRecord<>("topic_unmatched", key, value));
            return;
        }
        // 匹配一个就发送一个
        if (desc.contains("energy")) {
            producer.send(new ProducerRecord<>("topic_energy", key, value));
        }
        if (desc.contains("electric")) {
            producer.send(new ProducerRecord<>("topic_electric", key, value));
        }
        if (desc.contains("temperature")) {
            producer.send(new ProducerRecord<>("topic_temperature", key, value));
        }
        // 如果没有匹配任何关键词,转发到备用主题
        if (!desc.contains("energy") && !desc.contains("electric") && !desc.contains("temperature")) {
            producer.send(new ProducerRecord<>("topic_unmatched", key, value));
        }
    } catch (Exception e) {
        // 解析失败的消息发送到死信队列
        producer.send(new ProducerRecord<>("topic_dead_letter", key, value));
        e.printStackTrace();
    }
});

4. 错误处理

一定要处理JSON解析失败的情况,把这类消息转发到死信队列(topic_dead_letter),避免丢消息,后续还可以手动排查解析问题。

部署运行

把代码打包成jar包,用Kafka官方提供的kafka-streams-application.sh脚本启动,或者直接用java -jar your-app.jar运行。Streams会自动加入消费者组,做负载均衡和故障转移。

为什么不选Python消费?

Python的Kafka客户端虽然能实现消费转发,但需要自己处理偏移量提交、多线程负载、故障恢复等逻辑,实时性和可靠性远不如Kafka Streams——Streams是原生流式处理框架,延迟在毫秒级,而且自带状态管理和容错机制,不用自己造轮子。

内容的提问来源于stack exchange,提问作者Sarindra Thérèse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:10:29