Apache Kafka 消息键

2025 年 1 月 23 日 | 阅读 16 分钟

理解 Kafka 消息键

在 Apache Kafka 中,消息键是 Kafka 消息的可选部分,用于确定消息应发送到主题中的哪个分区。每个 Kafka 消息都由键、值以及诸如时间戳和头信息之类的元数据组成。消息键是 Kafka 确保数据高效分发和处理的关键方面。

结构

Kafka 消息由以下几部分组成:

  • 键: 用于分区的标识符。它可以为 null,这意味着消息将被随机分发到各个分区。
  • 值: 实际的数据负载。
  • 元数据: 包括时间戳、头信息和主题名称等属性。

目的

消息键的主要目的是提供一种对消息进行分组和排序的方法。具有相同键的消息保证会发送到同一个分区,这确保了它们按照发送顺序进行处理。

消息键的作用和重要性

  • 确定性分区

消息键最重要的作用之一就是实现确定性分区。当生产者将消息发送到 Kafka 主题时,分区器(通常是默认分区器)会使用该键来确定分区。这确保了具有相同键的所有消息都发送到同一个分区,从而保留具有相同键的消息的顺序。

  • 确保消息顺序

Kafka 保证分区内的消息顺序。通过使用键,生产者可以确保具有相同键的消息是有序的。例如,如果您正在发送用户活动日志并使用用户 ID 作为键,那么与特定用户相关的消息都将发送到同一个分区,并按照它们发送的顺序进行处理。

  • 负载均衡

虽然键的主要作用是确保顺序和分组,但它也有助于跨分区的负载分发。当键分布均匀时,分区会保持平衡,这有助于优化性能并确保资源的有效利用。然而,选择不当的键可能导致分区倾斜,即某些分区处理的数据量远大于其他分区。

  • 消息亲和性

消息键实现了消息亲和性,即将相关消息分组在一起。这在金融交易等场景中尤其有用,其中特定账户的所有相关消息都必须一起处理以保持一致性。

  • 启用复杂处理

在使用 Kafka Streams 的流处理应用程序中,消息键对于诸如连接、聚合和窗口化之类的状态化处理操作至关重要。这些操作依赖于消息键来正确关联和聚合数据。

  • 支持精确一次语义 (EOS)

随着 Kafka 对精确一次语义的支持的引入,消息键在确保幂等性和事务保证方面发挥着至关重要的作用。通过使用键,生产者和消费者可以管理偏移量并确保消息被精确处理一次。

消息键在 Kafka 架构中的工作原理

Kafka 架构概述

Apache Kafka 的架构围绕着一个分布式、分区日志系统构建。关键组件包括:

  • Broker: 存储数据并处理客户端请求的服务器。
  • Topic: 记录被发送到的类别。每个主题都分为多个分区。
  • Partition: 存储数据的日志。每个分区都是一个按顺序排列的、不可变的记录序列。
  • Producer: 将记录发布到 Kafka 主题的客户端。
  • Consumer: 从 Kafka 主题读取记录的客户端。
  • Zookeeper: 管理元数据和 Broker 协调。
Apache Kafka Message Keys

分区机制

当生产者将消息发送到主题时,分区过程决定将消息发送到哪个分区。此过程涉及:

  1. 键分配
    • 如果提供了键,分区器将使用该键来确定分区。
    • 如果未提供键,则消息将使用轮询或其他平衡策略分配到某个分区。
  2. 分区选择
    • Kafka 使用分区器(默认是哈希分区器)来计算键的哈希值并将其映射到分区。公式通常是 (hash(key) % number_of_partitions)。
    • 这种确定性的方法确保相同的键始终映射到同一个分区,从而保留顺序和分组。

生产者配置

生产者可以配置键和值的序列化方式,然后再将它们发送到 Kafka。常见配置包括:

  • 键序列化器: 将键对象转换为字节数组。
  • 值序列化器: 将值对象转换为字节数组。

消费者处理

消费者按写入顺序从分区读取消息。他们可以配置反序列化器将字节数组转换回对象。消费者偏移量管理确保每条消息都被正确处理一次且按正确的顺序处理。

用例示例

考虑一个处理客户订单的零售系统。通过使用客户 ID 作为键,来自同一客户的所有订单都发送到同一个分区。这确保了订单以正确的顺序处理,从而保持数据一致性。

Kafka Streams

Kafka Streams 大量利用消息键来进行状态化操作。例如,在窗口连接或聚合中,键确保相关记录一起处理。Streams API 提供了按键分组的方法,并执行连接和聚合等操作。

分区重新平衡

当主题的分区被重新分配时(例如,由于分区数增加或 Broker 故障),Kafka 会尽可能确保键继续映射到相同分区。这最大限度地减少了对消息顺序和处理逻辑的影响。

挑战与最佳实践

  1. 键分布: 确保键分布均匀,以避免分区倾斜。
  2. 处理 Null 键: 决定没有键的消息的策略。如果顺序不重要,可以使用轮询分发。
  3. 序列化: 为键和值选择合适的序列化器,以确保高效的序列化和反序列化。
  4. 监控和指标: 监控分区分布和性能指标,以识别和解决潜在问题。

高级主题

  1. 自定义分区器: 如果默认哈希策略不满足特定要求,请实现自定义分区器。
  2. 键演进: 处理随时间变化的键模式,以确保向后和向前兼容性。
  3. 键轮换: 在具有动态键需求的系统中,实现键轮换策略。

Apache Kafka 中的分区和消息键

Apache Kafka 是一个分布式流处理平台,专为高吞吐量、容错数据流而设计。使 Kafka 强大且可扩展的关键功能之一是其分区机制。分区允许 Kafka 并行处理数据,水平扩展,并在每个分区内保持有序的消息处理。消息键在数据如何分区、路由和处理方面起着至关重要的作用。本节深入探讨了 Kafka 分区的概念、消息键如何影响分区以及使用键进行确定性消息路由。

Apache Kafka Message Keys

Kafka 主题和分区

在 Kafka 中,数据流被组织成主题。主题是记录被发布到的类别或馈送名称。每个主题可以分为多个分区。分区本质上是追加日志,消息在到达时按顺序存储。每个分区都由整数 ID 标识,并且可以驻留在 Kafka 群集的不同 Broker 上,从而实现并行性和可扩展性。

为什么需要分区?

分区在 Kafka 中服务于多种目的:

  1. 可扩展性: 通过将分区分布在多个 Broker 上,Kafka 可以处理大量数据并进行水平扩展。
  2. 并行性: 多个消费者可以同时读取同一主题的不同分区,从而提高吞吐量和性能。
  3. 容错性: 分区可以跨 Broker 进行复制,以确保高可用性。如果一个 Broker 发生故障,另一个 Broker 可以接管。
  4. 排序: Kafka 保证单个分区内的消息顺序,这对于许多应用程序至关重要。

分区如何工作

  1. 生产者: 当生产者将消息发送到 Kafka 主题时,它会被追加到该主题的某个分区。消息被发送到的分区可以由生产者指定,也可以由 Kafka 确定。
  2. 消费者: 消费者从分区读取消息。在消费者组中,每个分区被分配给一个消费者,确保该组中的每条消息仅被处理一次。
  3. Broker: 每个 Kafka Broker 负责一部分分区,并管理这些分区的存储、复制和数据检索。

分区示例

考虑一个名为“orders”的 Kafka 主题,其中包含三个分区。该主题可能在三个 Broker 上分布如下:

  • 分区 0 在 Broker 1 上
  • 分区 1 在 Broker 2 上
  • 分区 2 在 Broker 3 上

当生产者将消息发送到“orders”主题时,消息会被分发到这些分区。

消息键如何影响分区

消息键的作用

消息键是 Kafka 消息的可选属性,它们会影响消息如何在分区之间分发。当提供消息键时,Kafka 会使用该键来确定消息将被发送到的分区。如果未提供键,Kafka 会使用轮询或其他负载均衡策略将消息分发到各个分区。

Apache Kafka Message Keys

带键的分区逻辑

Kafka 带有键的默认分区逻辑如下:

  1. 带键的消息: 当生产者发送带键的消息时,Kafka 会计算该键的哈希值。然后,通过将哈希值与分区数取模(即 hash(key) % num_partitions)来使用此哈希值来确定分区。
  2. 不带键的消息: 未提供键时,Kafka 会使用轮询或其他算法将消息均匀地分发到各个分区,以平衡负载。

示例

对于一个有三个分区的主题,带键消息的分区逻辑可能如下所示:

  • 键为“A”的消息 -> hash("A") % 3 -> 分区 1
  • 键为“B”的消息 -> hash("B") % 3 -> 分区 0
  • 键为“C”的消息 -> hash("C") % 3 -> 分区 2

确定性路由

使用键可确保消息的确定性路由。这意味着具有相同键的所有消息将始终路由到同一个分区。这对于保持相关消息的顺序以及确保消息以一致的方式处理非常重要。

基于键的分区的优点

  1. 消息顺序: 通过使用键,Kafka 确保具有相同键的所有消息都按照发送顺序进行处理,这对于需要有序处理的应用程序至关重要。
  2. 数据亲和性: 键可用于将相关数据分组在一起。例如,与单个客户相关的所有消息都可以通过客户 ID 进行键控,从而确保所有客户相关数据都由同一消费者处理。
  3. 高效处理: 基于键的分区允许高效处理相关数据。例如,流处理应用程序可以按键维护状态,从而简化处理逻辑。

挑战与注意事项

  1. 分区倾斜: 键的分布不均可能导致分区倾斜,即某些分区接收的消息比其他分区多。这可能导致负载不平衡并影响性能。
  2. 键设计: 键的设计是否得当对于确保均匀分布和高效处理很重要。应仔细选择键以避免倾斜并确保相关消息分组在一起。

使用键进行确定性消息路由

确定性路由是 Kafka 分区机制的一个基本方面,它确保了消息如何在分区之间分发的_一致性和可预测性_。以下是使用键进行确定性路由的工作原理:

哈希函数

Kafka 使用哈希函数来计算给定键的分区。默认的哈希函数是 MurmurHash,它是一种快速高效的非加密哈希函数。然后使用键的哈希值来确定分区。

分区分配

分区分配公式为:

partition = hash(key) % num_partitions

只要分区数保持不变,此公式就能确保相同的键始终映射到同一个分区。

示例

考虑一个名为“transactions”的 Kafka 主题,其中有四个分区。如果我们有键为“user1”、“user2”和“user3”的消息,则分区分配可能如下所示:

  • 键为“user1”的消息 -> hash("user1") % 4 -> 分区 2
  • 键为“user2”的消息 -> hash("user2") % 4 -> 分区 0
  • 键为“user3”的消息 -> hash("user3") % 4 -> 分区 3

只要分区数保持不变,具有相同键的消息将始终路由到同一个分区。

处理分区重新分配

当主题中的分区数发生变化时(例如,如果添加分区来扩展主题),则需要重新计算哈希函数和分区分配。这可能导致重新平衡,其中一些消息可能会被路由到与之前不同的分区。在更改分区数时需要仔细考虑,以最大程度地减少中断。

自定义分区器

除了默认分区器之外,Kafka 还允许开发人员实现自定义分区器来控制如何根据键路由消息。自定义分区器可以使用不同的分区逻辑,例如:

  • 基于范围的分区
  • 具有自定义哈希函数的基于哈希的分区
  • 基于时间的分区

实现自定义分区器

自定义分区器的使用

要使用自定义分区器,请使用自定义分区器类的完全限定名称配置生产者:

Kafka 消息序列化器

在 Apache Kafka 中,序列化是一个关键过程,它将数据转换为适合在网络上传输和存储的格式。Kafka 消息序列化涉及在将消息的键和值发送到 Kafka Broker 之前将它们转换为字节数组。在接收端,反序列化将字节数组转换回原始数据格式。此过程可确保数据能够高效且准确地传输和存储。

序列化和反序列化对于 Kafka 处理各种数据类型和结构至关重要,使其能够与不同的应用程序和系统无缝集成。让我们更深入地探讨 Kafka 消息序列化器、它们的重要性、常见类型以及如何实现自定义序列化器。

什么是 Kafka 消息序列化器?

Kafka 消息序列化器是一种将对象或数据结构转换为字节数组的机制,该字节数组可以在网络上传输并在 Kafka 中存储。相应的反序列化器执行相反的操作,将字节数组转换回原始对象或数据结构。

在 Kafka 中,序列化应用于消息的键和值。Kafka 为常见数据类型提供了内置序列化器,并且还允许开发人员为更复杂或特定数据格式创建自定义序列化器。

Apache Kafka Message Keys

序列化的重要性

  1. 兼容性: 序列化可确保数据可以以生产者和消费者都理解的格式进行传输和存储。
  2. 效率: 序列化数据(通常是字节数组)比原始数据更易于传输和存储。
  3. 互操作性: 当使用通用的序列化格式时,不同的系统和应用程序可以无缝地通信和共享数据。
  4. 数据完整性: 正确的序列化和反序列化可确保数据在传输过程中其结构和内容得以保留。

Kafka 中的内置序列化器

Kafka 为常见数据类型提供了多种内置序列化器。这些序列化器是 Kafka 客户端库的一部分,可以在 Kafka 生产者和消费者中轻松配置。

1. StringSerializer: 将字符串转换为字节数组。

IntegerSerializer: 将整数转换为字节数组。

3. LongSerializer: 将长整型转换为字节数组。

4. ByteArraySerializer: 直接转换字节数组(当数据已经是字节数组格式时使用)。

5. ByteBufferSerializer: 将 ByteBuffer 转换为字节数组。

实现自定义序列化器

对于更复杂或特定的数据格式,开发人员可以实现自定义序列化器。自定义序列化器在处理专有数据格式、复杂对象或与需要特定序列化协议的系统集成时特别有用。

创建自定义序列化器

要在 Kafka 中创建自定义序列化器,您需要实现 Kafka 提供的 Serializer 接口。以下是 User 对象的自定义序列化器的示例:

1. 定义 User 类

2. 实现 Serializer 接口

使用自定义序列化器

要使用自定义序列化器,请使用序列化器类的完全限定名称配置生产者:

反序列化

对于反序列化,Kafka 提供了 Deserializer 接口。与序列化器类似,您可以通过实现此接口来创建自定义反序列化器。

实现自定义反序列化器

1. 实现 Deserializer 接口

使用自定义反序列化器

要使用自定义反序列化器,请使用反序列化器类的完全限定名称配置消费者:

输出

Apache Kafka Message Keys

Kafka 序列化的最佳实践

  1. 选择正确的序列化器: 对常见数据类型使用内置序列化器。对于复杂数据类型,创建自定义序列化器。
  2. 避免大型负载: 大消息会影响 Kafka 的性能。如果可能,请考虑将大型负载分解为更小的块。
  3. 模式演进: 对于复杂数据类型,请考虑使用支持模式演进的序列化格式,例如 Avro、Protocol Buffers 或 JSON Schema。
  4. 错误处理: 在序列化器和反序列化器中实现错误处理,以优雅地处理序列化和反序列化错误。
  5. 配置管理: 确保在 Kafka 客户端应用程序中正确配置和管理序列化器和反序列化器。

Kafka Streams 如何利用消息键

Kafka Streams 是 Apache Kafka 中的一个库,它以多种方式利用消息键来实现强大的流处理应用程序。理解 Kafka Streams 如何利用消息键对于设计高效且可扩展的流处理管道至关重要。

1. 状态化流处理

Kafka Streams 支持状态化操作,如聚合、连接和窗口化。消息键对于维护状态一致性和实现高效的状态查找和操作至关重要。通过按键对消息进行分组,Kafka Streams 可确保所有相关事件一起处理,从而促进状态化计算。

示例输出

Apache Kafka Message Keys

2. 分区和并行性

Kafka 主题被分区,分区内的消息按其偏移量排序。在处理流时,Kafka Streams 可确保具有相同键的相关事件被路由到相同的流任务,从而保持顺序和一致性。这种分区机制允许 Kafka Streams 应用程序进行水平扩展,并通过并行处理多个实例来实现高吞吐量。

示例输出

Apache Kafka Message Keys

3. 连接操作

Kafka Streams 支持各种连接操作,包括内连接、外连接和窗口连接。消息键用于匹配和连接来自不同流或表的相关事件。通过根据键对消息进行对齐,Kafka Streams 可以实现高效的连接操作,而无需进行昂贵的 shuffle 或数据重组。

示例输出

Apache Kafka Message Keys

案例研究

1. 电子商务平台:订单处理和履行

场景: 一个电子商务平台处理来自全球客户的大量订单。为了有效地管理订单处理和履行,该平台为其事件驱动的架构采用了 Apache Kafka。客户生成的每个订单都包含一个唯一的订单 ID,该 ID 用作消息键。

Kafka 消息键的利用

  • 订单路由: 平台使用订单 ID 作为消息键,以确保与特定订单相关的所有事件都被路由到同一个分区。这确保了订单的一致性,并使诸如库存管理和运输之类的下游服务能够按正确的顺序处理订单。
  • 订单跟踪: 通过将订单相关事件与相同的键关联,平台可以实时跟踪单个订单的状态。订阅特定订单 ID 的消费者可以接收有关订单处理、运输状态和交付确认的更新。
  • 并发控制: Kafka 消息键有助于实现乐观并发控制机制,确保对同一订单的并发更新以一致且可预测的方式进行处理。

2. 金融服务公司:实时欺诈检测

场景: 一家金融服务公司运营一个支付处理平台,该平台每天处理来自数百万客户的交易。为了实时检测和防止欺诈活动,该公司采用 Apache Kafka 作为其欺诈检测系统的骨干。发送进行处理的每笔交易都包含一个唯一的事务 ID 作为消息键。

Kafka 消息键的利用

  • 交易关联: 通过使用事务 ID 作为消息键,该公司确保与特定事务关联的所有事件,如授权请求、账户余额更新和欺诈警报,都被一起处理。
  • 流连接操作: Kafka Streams 使该公司能够基于事务 ID 执行流-流和流-表连接操作。例如,将交易数据与客户个人资料或交易历史记录进行连接有助于识别可疑模式和异常,这些模式和异常表明存在欺诈行为。
  • 状态化聚合: Kafka 消息键有助于对时间窗口内的交易数据进行状态化聚合。该公司可以计算每笔交易 ID 的指标,例如交易速率、平均交易金额和高风险活动频率,从而实现主动欺诈检测和缓解。