行情源不断推来 Tick 时,怎样让多个下游各自消费、互不拖累,还能从故障点回放?

消息队列 Kafka:实时行情分发

约 10 分钟5 道自测更新于 2026-10-28

一句话记住Kafka 用 topic、partition、key、offset 和消费组把实时行情变成可分发、可回放的日志;它保证分区内有序,不是全局有序,也不是数据库。

01学完你能做到

解释 Kafka 的 topic、partition、offset、key 和 consumer group;为行情消息选择分区键;区分分区内有序、消费组并行和不同投递语义;设计回放、去重和积压监控方案。

02这课解决什么问题

行情源不断推来 Tick 时,怎样让实时 K 线、风控、前端、归档和数据接口各自消费,互不拖累,还能在故障后从某个位置回放?消息队列提供了采集与加工之间的缓冲、分发和回放层。

03Kafka 的核心概念

  • Topic:一类消息的逻辑名称,例如 ticks.sh。
  • Partition:topic 的并行子日志,是扩展吞吐和消费组并行度的基本单位。
  • Offset:消息在分区中的位置编号,消费者可从已提交位置继续读取。
  • Key:消息分区键。按股票代码分区时,同一代码通常进入同一分区,从而保持该分区内的处理顺序。
  • Consumer group:共同分担一个 topic 的消费者集合。同一消费组内,一个分区在同一时刻通常只分配给一个成员;不同消费组各自维护读取位置,互不抢数据。
  • Retention:日志按时间或大小保留,保留多久由 broker、topic 和集群配置决定,不能把某个默认值当成所有部署的承诺。
  • 投递语义:至多一次可能丢失,至少一次可能重复,精确一次需要生产端、处理端和下游共同配合。

Kafka 只保证分区内有序,不保证跨分区全局有序。分区数增加后,key 到分区的映射和扩展策略也要重新评估。热门股票可能形成热点分区,应监控每个分区的消费积压。

04行情链路中的位置

行情源 -> 生产者(解码、标准化) -> Kafka topic
                                      -> K 线聚合消费组
                                      -> 风控消费组
                                      -> 落库与归档消费组

Kafka 负责缓冲、分发和回放;长期存储与随机查询应交给数据湖、数据库或缓存。下游处理慢于生产速度时,consumer lag 会持续增加,实时接口返回的可能是旧行情。

05代码:模拟 topic、分区和回放

下面的代码只模拟核心语义,不是真实 Kafka 客户端;示例用 CRC32 代替默认分区器的具体哈希实现。

import zlib
from collections import defaultdict

class Topic:
    def __init__(self, partitions):
        self.logs = [[] for _ in range(partitions)]

    def send(self, key, value):
        partition = zlib.crc32(key.encode()) % len(self.logs)
        self.logs[partition].append((key, value))
        return partition, len(self.logs[partition]) - 1

class ConsumerGroup:
    def __init__(self, topic, members):
        self.topic = topic
        self.committed = defaultdict(int)
        self.assignment = defaultdict(list)
        for p in range(len(topic.logs)):
            self.assignment[members[p % len(members)]].append(p)

    def poll(self, member):
        out = []
        for p in self.assignment[member]:
            start = self.committed[p]
            batch = self.topic.logs[p][start:]
            out.extend((p, start + i, k, v) for i, (k, v) in enumerate(batch))
            self.committed[p] = start + len(batch)
        return out

真实系统还要定义提交 offset 的时机、失败重试、幂等写入、消息版本、序列号和回放边界。单纯把 offset 重置到 0 不能自动保证外部数据库不重复。

06常见误区

  1. 认为 Kafka 全局有序;跨分区的消息没有全局顺序。
  2. 不设 key 或 key 选错,导致同一证券消息分散到多个分区。
  3. 分区数随意增加;并行度提高的同时,映射变化和热点问题也要评估。
  4. 认为精确一次可以覆盖所有外部系统;写入数据库或下游服务仍需事务或幂等策略。
  5. 把 Kafka 当数据库;它不适合按代码查询某时刻的最终状态。
  6. 忽视 consumer lag;积压意味着下游延迟,可能已经影响实时服务。

07延伸阅读

08一分钟讲给别人

试着用自己的话讲一遍Kafka 像一条可回放的公共传送带。行情源只管往上放,风控、K 线计算和落库各自组队来取;同一只股票用同一个 key,能在分区内保持顺序。出了问题可以按 offset 回放,但下游仍要做去重或幂等写入。

想一想

5 道自测

先在心里答一遍,再展开对照。能说清理由,比答对数字更重要。

  1. 01Kafka 保证什么范围内的消息顺序?

  2. 02为什么行情消息常用证券代码作为 key?

  3. 03两个不同消费组会互相抢同一 topic 的消息吗?

  4. 04消费组成员多于分区数时会发生什么?

  5. 05至少一次投递语义要求下游做什么?

已看 0/5 题

练动手练习

任务:把模拟消费组改成 4 个成员,但 topic 仍只有 3 个分区,观察是否有成员空闲;再写出一个按证券代码去重的消息主键,并说明它在重放时如何使用。

完成这一课

完成这一课

读完讲义、看完自测答案,这一课就算学完了。

  • 读完讲义
  • 看完全部自测

本课为投资者教育内容,案例数字均为示意,不构成任何投资建议,也不承诺收益。规则和费率会变,实际操作前请以交易所、监管机构和产品的最新公告为准。