霸王餐用户行为埋点:Kafka Connect+ClickHouse实时OLAP分析

霸王餐用户行为埋点:Kafka Connect+ClickHouse实时OLAP分析

整体架构概述

"吃喝不愁"App需对用户点击、下单、分享等行为进行毫秒级追踪与分析,以支撑运营决策与推荐策略。系统采用三层架构:

  1. 前端/客户端:通过 SDK 上报 JSON 格式埋点事件至 Kafka;
  2. Kafka Connect:使用官方或自研 Sink Connector 将消息写入 ClickHouse;
  3. ClickHouse:提供亚秒级 OLAP 查询能力。

该方案避免了传统批处理延迟,实现端到端秒级数据可见。

埋点事件格式定义

每条事件包含公共字段与业务字段:

json 复制代码
{
  "event_id": "evt_123456",
  "user_id": "u_7890",
  "event_type": "click_claim_button",
  "timestamp": 1717020800000,
  "app_version": "2.3.1",
  "device_id": "d_abc123",
  "properties": {
    "restaurant_id": "r_456",
    "campaign_id": "camp_789"
  }
}

ClickHouse 表结构设计

使用 MergeTree 引擎,按日期分区,按用户ID和事件类型排序:

sql 复制代码
CREATE TABLE eatfree.user_events (
    event_id String,
    user_id String,
    event_type LowCardinality(String),
    timestamp DateTime64(3),
    app_version String,
    device_id String,
    restaurant_id String,
    campaign_id String
) ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(timestamp)
ORDER BY (user_id, event_type, timestamp)
SETTINGS index_granularity = 8192;

Kafka Connect Sink 配置

使用开源 clickhouse-kafka-connect 插件,配置如下:

properties 复制代码
name=eatfree-clickhouse-sink
connector.class=com.clickhouse.kafka.connect.ClickHouseSinkConnector
tasks.max=3
topics=user_events

key.converter=org.apache.kafka.connect.storage.StringConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
value.converter.schemas.enable=false

clickhouse.server.url=http://clickhouse:8123
clickhouse.database=eatfree
clickhouse.table=user_events
clickhouse.username=default
clickhouse.password=

batch.size=1000
linger.ms=1000

部署后,Kafka 中 user_events 主题的消息将自动批量写入 ClickHouse。

Java 埋点上报示例(SDK 端)

java 复制代码
package juwatech.cn.tracking;

import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class EventTracker {

    private final KafkaProducer<String, String> producer;
    private static final String TOPIC = "user_events";

    public EventTracker() {
        Properties props = new Properties();
        props.put("bootstrap.servers", "kafka:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        this.producer = new KafkaProducer<>(props);
    }

    public void track(String userId, String eventType, String propertiesJson) {
        long ts = System.currentTimeMillis();
        String eventId = "evt_" + ts + "_" + userId.hashCode();
        String payload = String.format(
            "{\"event_id\":\"%s\",\"user_id\":\"%s\",\"event_type\":\"%s\",\"timestamp\":%d,\"app_version\":\"2.3.1\",\"device_id\":\"d_abc123\",%s}",
            eventId, userId, eventType, ts, propertiesJson.substring(1)
        );
        ProducerRecord<String, String> record = new ProducerRecord<>(TOPIC, userId, payload);
        producer.send(record, (metadata, exception) -> {
            if (exception != null) {
                System.err.println("Failed to send event: " + exception.getMessage());
            }
        });
    }

    public void close() {
        producer.close();
    }
}

调用示例:

java 复制代码
EventTracker tracker = new EventTracker();
tracker.track("u_7890", "click_claim_button", 
    "{\"restaurant_id\":\"r_456\",\"campaign_id\":\"camp_789\"}");

实时查询场景示例

每小时霸王餐领取次数:

sql 复制代码
SELECT
    toStartOfHour(timestamp) AS hour,
    count(*) AS claim_count
FROM eatfree.user_events
WHERE event_type = 'click_claim_button'
  AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY hour
ORDER BY hour DESC;

活跃用户地域分布(假设 device_id 关联 IP 地理库):

sql 复制代码
SELECT
    multiIf(
        startsWith(device_id, 'BJ'), 'Beijing',
        startsWith(device_id, 'SH'), 'Shanghai',
        'Other'
    ) AS region,
    uniq(user_id) AS dau
FROM eatfree.user_events
WHERE timestamp >= today()
GROUP BY region;

容错与性能保障

  • Kafka 设置副本数 ≥ 2,确保消息不丢失;
  • ClickHouse 使用 ReplicatedMergeTree 实现集群高可用;
  • Kafka Connect 启用 exactly.once 语义(需 broker 支持)避免重复写入;
  • 对高频事件(如曝光)可采样上报,降低吞吐压力。

扩展:自定义 Transformer(可选)

若需在 Connect 层清洗数据,可实现 Transformation 接口:

java 复制代码
package juwatech.cn.connect;

import org.apache.kafka.connect.connector.ConnectRecord;
import org.apache.kafka.connect.transforms.Transformation;
import java.util.Map;

public class EnrichEventTransformer<R extends ConnectRecord<R>> implements Transformation<R> {

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

    @Override
    public R apply(R record) {
        // 示例:添加处理时间字段
        // 实际中可解析 JSON 并注入新字段
        return record;
    }

    @Override
    public void close() {}
}

注册后在 connector 配置中启用:

properties 复制代码
transforms=enrich
transforms.enrich.type=juwatech.cn.connect.EnrichEventTransformer

本文著作权归吃喝不愁app开发者团队,转载请注明出处!

相关推荐
利刃大大20 小时前
【RabbitMQ】安装详解 && 什么是MQ && RabbitMQ介绍
分布式·中间件·消息队列·rabbitmq·mq
新猿一马20 小时前
Spring Kafka核心参数说明
kafka
QQ_43766431420 小时前
kafka
分布式·kafka
是一个Bug20 小时前
Java后端开发面试题清单(50道) - 分布式基础
java·分布式·wpf
大猫和小黄20 小时前
Java ID生成策略全面解析:从单机到分布式的最佳实践
java·开发语言·分布式·id
ZePingPingZe20 小时前
CAP—ZooKeeper ZAB协议:从理论到实践的一致性与可用性平衡之道
分布式·zookeeper
掘金-我是哪吒20 小时前
完整的Kafka项目启动流程
分布式·kafka
无心水21 小时前
【分布式利器:腾讯TSF】4、TSF配置中心深度解析:微服务动态配置的终极解决方案
分布式·微服务·架构·wpf·分布式利器·腾讯tsf·分布式利器:腾讯tsf
无心水1 天前
【分布式利器:腾讯TSF】7、TSF高级部署策略全解析:蓝绿/灰度发布落地+Jenkins CI/CD集成(Java微服务实战)
java·人工智能·分布式·ci/cd·微服务·jenkins·腾讯tsf
Yeats_Liao1 天前
MindSpore开发之路(二十四):MindSpore Hub:快速复用预训练模型
人工智能·分布式·神经网络·机器学习·个人开发