创建 MirrorMaker 2.0 来源连接器

借助 MirrorMaker 2.0 源连接器,您可以将某个 Kafka 集群(源)的主题和数据复制到另一个 Kafka 集群(目标)。

此连接器的典型应用场景包括:

  • 数据迁移。将 Kafka 工作负载迁移到新的 Managed Service for Apache Kafka 集群。

  • 灾难恢复。创建备份集群,以确保在发生故障时业务连续性。

  • 数据汇总。将多个 Kafka 集群中的数据整合到一个中央 Managed Service for Apache Kafka 集群中,以便执行分析。

此连接器在 Kafka 集群之间执行基本数据复制。为确保更强大的灾难恢复能力,请将以下连接器与 MirrorMaker 2.0 Source 连接器结合使用:

  • MirrorMaker 2.0 检查点连接器。复制消费者偏移量,以便消费者可以故障切换到目标集群,而不会丢失数据。

  • MirrorMaker 2.0 检测信号连接器。生成定期检测信号消息,以便您监控连接性以及复制延迟时间。

了解 MirrorMaker 2.0 中的集群角色

MirrorMaker 2.0 连接器会从源 Kafka 集群读取数据,并写入目标 Kafka 集群。在 Managed Service for Apache Kafka 中,Connect 集群的主集群必须是目标集群。此配置可最大限度地缩短写入操作的延迟时间。源集群可以是另一个 Managed Service for Apache Kafka 集群,也可以是外部或自行管理的 Kafka 集群。

如果您想将数据从 Managed Service for Apache Kafka 集群复制到外部或自行管理的集群,我们建议您在另一个集群所在的同一位置或区域中运行 Kafka Connect,并使用 MirrorMaker 2.0 从 Managed Service for Apache Kafka 注入数据。

您必须正确配置连接器的所有属性。这些属性还包括针对辅助集群的生产者身份验证属性。如需详细了解潜在问题,请参阅改进 MirrorMaker 2.0 客户端配置。

所需的角色和权限

如需获得创建连接器所需的权限,请让您的管理员为您授予项目的 Managed Kafka Connector Editor (roles/managedkafka.connectorEditor) IAM 角色。如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

此预定义角色可提供创建连接器所需的权限。如需查看所需的确切权限,请展开所需权限部分:

所需权限

创建连接器需要以下权限:

  • 创建连接器: managedkafka.connectors.create

您也可以使用自定义角色或其他预定义角色来获取这些权限。

配置网络

本部分介绍了 MirrorMaker 2.0 连接到辅助 Kafka 集群的网络要求。以下是一般要求:

  • 连接。Connect 集群工作器必须能够连接到 Kafka 辅助集群。

  • DNS 解析。您必须将辅助集群的 DNS 域名添加到 Connect 集群的可解析 DNS 域名。此外,DNS 域名必须可从 Connect 集群的子网进行解析,例如通过创建 Cloud DNS 区域。

  • 防火墙规则。防火墙规则必须允许 Connect 集群工作器访问 Kafka 源集群和目标集群。

许多细节取决于辅助 Kafka 集群的托管方式和位置,无论它是 Managed Service for Apache Kafka 集群、 Google Cloud中的自行管理集群还是外部集群。

  • Managed Service for Apache Kafka 集群

    • 如果辅助集群位于其他 VPC 网络中,请将 Connect 集群的 VPC 网络中的子网添加到辅助集群的已连接子网中。如需了解详情,请参阅网络配置。
  • Google Cloud中的自行管理 Kafka 集群

  • 外部 Kafka 集群

    • 连接到外部集群的选项包括:

      • Cloud VPN:一种经济实惠的解决方案,适用于较低带宽。它在公共互联网上建立一条加密隧道。

      • Cloud Interconnect:在本地网络和 Google Cloud之间提供专用的高带宽连接。您可以选择专用互连(用于直接物理连接)或合作伙伴互连(用于通过服务提供商进行连接)。

    • 配置 Cloud NAT,以允许 Connect 工作器访问互联网。

    • 在 Connect 集群的 VPC 网络中创建 Cloud DNS 转发区域,以解析外部集群的引导地址和代理端点的 DNS 查询。

配置身份验证

如果来源或目标 Kafka 集群是 Managed Service for Apache Kafka 集群,则连接器会自动配置为使用 OAuthBearer 身份验证。您无需进行任何其他配置。

对于自行管理的 Kafka 集群或外部 Kafka 集群,您必须配置身份验证和 TLS 加密。配置取决于 Kafka 集群支持的身份验证机制。如需了解详情,请参阅 Apache Kafka 文档中的 MirrorMaker 通用配置。

如果集群支持 OAuthBearer 身份验证,请使用以下设置:

source.cluster.security.protocol=SASL_SSL
source.cluster.sasl.mechanism=OAUTHBEARER
source.cluster.sasl.login.callback.handler.class=com.google.cloud.hosted.kafka.auth.GcpLoginCallbackHandler
source.cluster.sasl.jaas.config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;

将所需凭据存储在 Secret Manager 中,如下一部分中所述。

使用 Secret Manager 管理凭证

连接到自行管理的 Kafka 集群或外部 Kafka 集群时,请使用 Secret Manager 存储身份验证所需的敏感配置值。这些信息可能包括密码、密钥库文件和受信任证书存储区文件,具体取决于集群中身份验证的配置方式。

如需详细了解如何将 Secret Manager Secret 与 Connect 集群相关联,请参阅 Secret Manager 资源。

在连接器配置中,按如下方式引用 Secret:

  • 对于文件路径,请使用 /var/secrets/PROJECT_NAME-SECRET_NAME-SECRET_VERSION 格式。示例:ssl.truststore.location=/var/secrets/project1-truststore-1。

  • 对于密码,请使用 ${directory:/var/secrets:PROJECT_NAME-SECRET_NAME-SECRET_VERSION} 格式。示例:password=${directory:/var/secrets:project1-database_password-3}。

替换以下内容:

  • PROJECT_NAME: Google Cloud 项目的名称。
  • SECRET_NAME:Secret 的名称。
  • SECRET_VERSION:Secret 版本。

MirrorMaker Source 连接器的工作原理

MirrorMaker 源连接器会从源集群中的一个或多个 Kafka 主题拉取数据,并将这些数据以及 ACL 复制到目标集群中的主题。

以下详细介绍了 MirrorMaker Source 连接器如何复制数据:

  • 连接器会使用源集群中指定 Kafka 主题的消息。使用 topics 配置属性指定要复制的主题,该属性接受以逗号分隔的主题名称或单个 Java 样式的正则表达式。例如 topic-a,topic-b 或 my-prefix-.*。

  • 连接器还可以跳过复制您使用 topics.exclude 属性指定的特定主题;排除项会覆盖包含项。

  • 连接器会将使用的消息写入目标集群。

  • 连接器需要源集群和目标集群的连接详细信息,例如 source.cluster.bootstrap.servers 和 target.cluster.bootstrap.servers。

  • 连接器还需要 source.cluster.alias 和 target.cluster.alias 指定的来源集群和目标集群的别名。默认情况下,复制的主题会自动使用来源别名重命名。例如,来自别名为 primary 的来源且名为 orders 的主题在目标中会变为 primary.orders。如需在目标集群中保留原始主题名称而不使用前缀,请将 replication.policy.class 设置为 org.apache.kafka.connect.mirror.IdentityReplicationPolicy。

  • 与复制的主题关联的 ACL 也会从源集群同步到目标集群。您可以使用 sync.topic.acls.enabled 属性停用此功能。

  • 如果集群需要,则必须在配置中提供用于连接到源集群和目标集群的身份验证详细信息。您必须配置 security.protocol、sasl.mechanism 和 sasl.jaas.config 等属性,并为来源添加 source.cluster. 前缀,为目标添加 target.cluster. 前缀。

  • 连接器依赖于内部主题。您可能需要配置与这些属性相关的属性,例如 offset-syncs.topic.replication.factor。

  • 连接器使用 Kafka 记录转换器 key.converter、value.converter 和 header.converter。对于直接复制,这些通常默认为 org.apache.kafka.connect.converters.ByteArrayConverter,不执行任何转换(直通)。

  • tasks.max 属性控制连接器的并行级别。增加 tasks.max 可能会提高吞吐量,但有效并行性通常受要复制的来源 Kafka 主题中的分区数量限制。

MirrorMaker 2.0 连接器的属性

创建或更新 MirrorMaker 2.0 连接器时,请指定以下属性:

连接器名称

连接器的名称或 ID。如需了解如何命名资源,请参阅 Managed Service for Apache Kafka 资源命名指南。名称不可变。

连接器类型

连接器类型必须是以下类型之一:

源集群

MirrorMaker 2.0 连接器从中读取数据的源 Kafka 集群。

  • Managed Service for Apache Kafka 集群:从下拉菜单中选择一个集群。

  • 自行管理的 Kafka 集群或外部 Kafka 集群:输入引导地址,格式为 hostname:port_number。例如:kafka-test:9092。

主题名称或正则表达式

要复制的主题。指定各个名称(topic1、topic2)或使用正则表达式 (topic.*)。MirrorMaker 2.0 Source 连接器需要此属性。默认值为 .*

使用方群组名称或正则表达式

要复制的使用方群组。指定各个名称(group1、group2)或使用正则表达式 (group.*)。MirrorMaker 2.0 Checkpoint 连接器需要此属性。默认值为 .*。

配置

在此部分中,您可以为 MirrorMaker 2.0 连接器指定其他连接器专用配置属性。

由于 Kafka 主题中的数据可以采用各种格式(例如 Avro、JSON 或原始字节),因此配置的一个关键部分是指定转换器。转换器会将 Kafka 主题中使用的格式的数据转换为 Kafka Connect 的标准化内部格式。

如需详细了解 Kafka Connect 中转换器的作用、支持的转换器类型以及常见配置选项,请参阅转换器。

所有 MirrorMaker 2.0 连接器的一些常见配置包括:

  • source.cluster.alias:来源集群的别名。

  • target.cluster.alias:目标集群的别名。

  • replication.policy.class:用于在目标集群中命名复制的主题的政策类。默认值为 org.apache.kafka.connect.mirror.DefaultReplicationPolicy,它会为复制的主题名称添加源集群别名前缀。如果源集群别名为 primary,则名为 orders 的主题会复制到目标集群中的 primary.orders。如需保留不带前缀的原始主题名称,请将此属性设置为 org.apache.kafka.connect.mirror.IdentityReplicationPolicy。

复制数据时用于排除特定资源的配置:

  • topics.exclude:排除的主题。支持以逗号分隔的主题名称和正则表达式。排除项的优先级高于包含项。 用于 MirrorMaker 2.0 Source 连接器。 默认值为 mm2.*.internal,.*.replica,__.*

  • groups.exclude:排除群组。支持以逗号分隔的群组 ID 和正则表达式。排除项的优先级高于包含项。 用于 MirrorMaker 2.0 检查点连接器。 默认值为 console-consumer-.*,connect-.*,__.*

可用的配置属性取决于具体连接器。查看支持的 MirrorMaker 2.0 连接器版本,了解支持哪些配置示例。请参阅以下文档:

Kafka 记录转换

Kafka Connect 使用 org.apache.kafka.connect.converters.ByteArrayConverter 作为键和值的默认转换器,它提供了一个不进行转换的直通选项。

您可以将 header.converter、key.converter 和 value.converter 配置为使用其他转换器。

任务计数

tasks.max 值用于配置 Kafka Connect 用于运行 MirrorMaker 连接器的最大任务数。它控制连接器的并行级别。 增加任务数量可能会提高吞吐量,但会受到 Kafka 主题分区数量等因素的限制。

创建 MirrorMaker 2.0 来源连接器

在创建连接器之前,请先查看连接器属性文档。

控制台

  1. 在 Google Cloud 控制台中,前往连接集群页面。

    前往“连接集群”

  2. 点击要在其中创建连接器的 Connect 集群。

    系统会显示连接集群详情页面。

  3. 点击创建连接器。

    系统会显示创建 Kafka 连接器页面。

  4. 在连接器名称中,输入一个字符串。

    如需详细了解如何为连接器命名,请参阅 Managed Service for Apache Kafka 资源命名指南。

  5. 对于连接器插件,选择“MirrorMaker 2.0 Source”。

  6. 对于来源集群,请选择以下选项之一:

    • Managed Service for Apache Kafka 集群:从菜单中选择。
    • 自行管理的 Kafka 集群或外部 Kafka 集群:输入格式为 hostname:port_number 的引导地址。
  7. 输入以英文逗号分隔的主题名称或主题正则表达式。

  8. 查看并调整配置,包括所需的安全设置。

    如需详细了解配置和身份验证,请参阅配置。

  9. 选择任务重启政策。如需了解详情,请参阅任务重启政策。

  10. 点击创建。

gcloud

运行 gcloud managed-kafka connectors create 命令:

gcloud managed-kafka connectors create CONNECTOR_ID \
    --connect-cluster=CONNECT_CLUSTER_ID \
    --location=LOCATION \
    --config-file=CONFIG_FILE

替换以下内容:

  • CONNECTOR_ID:连接器的 ID 或名称。
  • CONNECT_CLUSTER_ID:在其中创建连接器的 Connect 集群的 ID。
  • LOCATION:Connect 集群的位置。
  • CONFIG_FILE:本地 YAML 或 JSON 配置文件的路径。

以下是 MirrorMaker 2.0 Source 连接器的配置文件示例:

connector.class: "org.apache.kafka.connect.mirror.MirrorSourceConnector"
source.cluster.alias: "source"
target.cluster.alias: "target"
topics: "TOPIC_LIST"
source.cluster.bootstrap.servers: "SOURCE_CLUSTER_BOOTSTRAP_ADDRESS"
target.cluster.bootstrap.servers: "TARGET_CLUSTER_BOOTSTRAP_ADDRESS"

替换以下内容:

  • TOPIC_LIST:要复制的 Kafka 主题的英文逗号分隔列表。该列表可以包含正则表达式。

  • SOURCE_CLUSTER_BOOTSTRAP_ADDRESS:源 Kafka 集群的引导地址。

  • TARGET_CLUSTER_BOOTSTRAP_ADDRESS:目标 Kafka 集群的引导地址。

如果源集群是自行管理的 Kafka 集群,您可能需要添加其他属性来配置身份验证。如需了解详情,请参阅配置身份验证。

Terraform

您可以使用 Terraform 资源创建连接器。

# A single MirrorMaker 2 Source Connector to replicate from one source to one target.
resource "google_managed_kafka_connector" "default" {
  project         = data.google_project.default.project_id
  connector_id    = "mm2-source-to-target-connector-id"
  connect_cluster = google_managed_kafka_connect_cluster.default.connect_cluster_id
  location        = "us-central1"

  configs = {
    "connector.class"      = "org.apache.kafka.connect.mirror.MirrorSourceConnector"
    "name"                 = "mm2-source-to-target-connector-id"
    "tasks.max"            = "3"
    "source.cluster.alias" = "source"
    "target.cluster.alias" = "target"
    "topics"               = ".*" # Replicate all topics from the source
    # The value for bootstrap.servers is a comma-separated list of hostname:port pairs
    # for one or more Kafka brokers in the source/target cluster.
    "source.cluster.bootstrap.servers" = "source_cluster_dns"
    "target.cluster.bootstrap.servers" = "target_cluster_dns"
    # You can define an exclusion policy for topics as follows:
    # To exclude internal MirrorMaker 2 topics, internal topics and replicated topics,.
    "topics.exclude" = "mm2.*\\.internal,.*\\.replica,__.*"
  }

  provider = google-beta
}

如需了解如何应用或移除 Terraform 配置,请参阅基本 Terraform 命令。

Go

在试用此示例之前,请按照 安装客户端库中的 Go 设置说明进行操作。如需了解详情,请参阅 Managed Service for Apache Kafka Go API 参考文档。

如需向 Managed Service for Apache Kafka 进行身份验证,请设置应用默认凭据(ADC)。 如需了解详情,请参阅为本地开发环境设置 ADC。

import (
	"context"
	"fmt"
	"io"

	managedkafka "cloud.google.com/go/managedkafka/apiv1"
	"cloud.google.com/go/managedkafka/apiv1/managedkafkapb"
	"google.golang.org/api/option"
)

// createMirrorMaker2SourceConnector creates a MirrorMaker 2.0 Source connector.
func createMirrorMaker2SourceConnector(w io.Writer, projectID, region, connectClusterID, connectorID, sourceBootstrapServers, targetBootstrapServers, tasksMax, sourceClusterAlias, targetClusterAlias, topics, topicsExclude string, opts ...option.ClientOption) error {
	// TODO(developer): Update with your config values. Here is a sample configuration:
	// projectID := "my-project-id"
	// region := "us-central1"
	// connectClusterID := "my-connect-cluster"
	// connectorID := "mm2-source-to-target-connector-id"
	// sourceBootstrapServers := "source_cluster_dns"
	// targetBootstrapServers := "target_cluster_dns"
	// tasksMax := "3"
	// sourceClusterAlias := "source"
	// targetClusterAlias := "target"
	// topics := ".*"
	// topicsExclude := "mm2.*.internal,.*.replica,__.*"
	ctx := context.Background()
	client, err := managedkafka.NewManagedKafkaConnectClient(ctx, opts...)
	if err != nil {
		return fmt.Errorf("managedkafka.NewManagedKafkaConnectClient got err: %w", err)
	}
	defer client.Close()

	parent := fmt.Sprintf("projects/%s/locations/%s/connectClusters/%s", projectID, region, connectClusterID)

	config := map[string]string{
		"connector.class":      "org.apache.kafka.connect.mirror.MirrorSourceConnector",
		"name":                 connectorID,
		"tasks.max":            tasksMax,
		"source.cluster.alias": sourceClusterAlias,
		"target.cluster.alias": targetClusterAlias, // This is usually the primary cluster.
		// Replicate all topics from the source
		"topics": topics,
		// The value for bootstrap.servers is a hostname:port pair for the Kafka broker in
		// the source/target cluster.
		// For example: "kafka-broker:9092"
		"source.cluster.bootstrap.servers": sourceBootstrapServers,
		"target.cluster.bootstrap.servers": targetBootstrapServers,
		// You can define an exclusion policy for topics as follows:
		// To exclude internal MirrorMaker 2 topics, internal topics and replicated topics.
		// topicsExclude := "mm2.*.internal,.*.replica,__.*"
		"topics.exclude": topicsExclude,
	}

	connector := &managedkafkapb.Connector{
		Name:    fmt.Sprintf("%s/connectors/%s", parent, connectorID),
		Configs: config,
	}

	req := &managedkafkapb.CreateConnectorRequest{
		Parent:      parent,
		ConnectorId: connectorID,
		Connector:   connector,
	}

	resp, err := client.CreateConnector(ctx, req)
	if err != nil {
		return fmt.Errorf("client.CreateConnector got err: %w", err)
	}
	fmt.Fprintf(w, "Created MirrorMaker 2.0 Source connector: %s\n", resp.Name)
	return nil
}

Java

在试用此示例之前,请按照 安装客户端库中的 Java 设置说明进行操作。如需了解详情,请参阅 Managed Service for Apache Kafka Java API 参考文档。

如需向 Managed Service for Apache Kafka 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅 为本地开发环境设置 ADC。


import com.google.api.gax.rpc.ApiException;
import com.google.cloud.managedkafka.v1.ConnectClusterName;
import com.google.cloud.managedkafka.v1.Connector;
import com.google.cloud.managedkafka.v1.ConnectorName;
import com.google.cloud.managedkafka.v1.CreateConnectorRequest;
import com.google.cloud.managedkafka.v1.ManagedKafkaConnectClient;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class CreateMirrorMaker2SourceConnector {

  public static void main(String[] args) throws Exception {
    // TODO(developer): Replace these variables before running the example.
    String projectId = "my-project-id";
    String region = "my-region"; // e.g. us-east1
    String maxTasks = "3";
    String connectClusterId = "my-connect-cluster";
    String connectorId = "my-mirrormaker2-connector";
    String sourceClusterBootstrapServers = "my-source-cluster:9092";
    String targetClusterBootstrapServers = "my-target-cluster:9092";
    String sourceClusterAlias = "source";
    String targetClusterAlias = "target"; // This is usually the primary cluster.
    String connectorClass = "org.apache.kafka.connect.mirror.MirrorSourceConnector";
    String topics = ".*";
    // You can define an exclusion policy for topics as follows:
    // To exclude internal MirrorMaker 2 topics, internal topics and replicated topics.
    String topicsExclude = "mm2.*.internal,.*.replica,__.*";
    createMirrorMaker2SourceConnector(
        projectId,
        region,
        maxTasks,
        connectClusterId,
        connectorId,
        sourceClusterBootstrapServers,
        targetClusterBootstrapServers,
        sourceClusterAlias,
        targetClusterAlias,
        connectorClass,
        topics,
        topicsExclude);
  }

  public static void createMirrorMaker2SourceConnector(
      String projectId,
      String region,
      String maxTasks,
      String connectClusterId,
      String connectorId,
      String sourceClusterBootstrapServers,
      String targetClusterBootstrapServers,
      String sourceClusterAlias,
      String targetClusterAlias,
      String connectorClass,
      String topics,
      String topicsExclude)
      throws Exception {

    // Build the connector configuration
    Map<String, String> configMap = new HashMap<>();
    configMap.put("tasks.max", maxTasks);
    configMap.put("connector.class", connectorClass);
    configMap.put("name", connectorId);
    configMap.put("source.cluster.alias", sourceClusterAlias);
    configMap.put("target.cluster.alias", targetClusterAlias);
    configMap.put("topics", topics);
    configMap.put("topics.exclude", topicsExclude);
    configMap.put("source.cluster.bootstrap.servers", sourceClusterBootstrapServers);
    configMap.put("target.cluster.bootstrap.servers", targetClusterBootstrapServers);

    Connector connector = Connector.newBuilder()
        .setName(
            ConnectorName.of(projectId, region, connectClusterId, connectorId).toString())
        .putAllConfigs(configMap)
        .build();

    try (ManagedKafkaConnectClient managedKafkaConnectClient = ManagedKafkaConnectClient.create()) {
      CreateConnectorRequest request = CreateConnectorRequest.newBuilder()
          .setParent(ConnectClusterName.of(projectId, region, connectClusterId).toString())
          .setConnectorId(connectorId)
          .setConnector(connector)
          .build();

      // This operation is being handled synchronously.
      Connector response = managedKafkaConnectClient.createConnector(request);
      System.out.printf("Created MirrorMaker2 Source connector: %s\n", response.getName());
    } catch (IOException | ApiException e) {
      System.err.printf("managedKafkaConnectClient.createConnector got err: %s\n", e.getMessage());
    }
  }
}

Python

在试用此示例之前,请按照 安装客户端库中的 Python 设置说明进行操作。如需了解详情,请参阅 Managed Service for Apache Kafka Python API 参考文档。

如需向 Managed Service for Apache Kafka 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置 ADC。

from google.api_core.exceptions import GoogleAPICallError
from google.cloud.managedkafka_v1.services.managed_kafka_connect import (
    ManagedKafkaConnectClient,
)
from google.cloud.managedkafka_v1.types import Connector, CreateConnectorRequest

connect_client = ManagedKafkaConnectClient()
parent = connect_client.connect_cluster_path(project_id, region, connect_cluster_id)

configs = {
    "connector.class": "org.apache.kafka.connect.mirror.MirrorSourceConnector",
    "name": connector_id,
    "tasks.max": tasks_max,
    "source.cluster.alias": source_cluster_alias,
    "target.cluster.alias": target_cluster_alias,  # This is usually the primary cluster.
    # Replicate all topics from the source
    "topics": topics,
    # The value for bootstrap.servers is a hostname:port pair for the Kafka broker in
    # the source/target cluster.
    # For example: "kafka-broker:9092"
    "source.cluster.bootstrap.servers": source_bootstrap_servers,
    "target.cluster.bootstrap.servers": target_bootstrap_servers,
    # You can define an exclusion policy for topics as follows:
    # To exclude internal MirrorMaker 2 topics, internal topics and replicated topics.
    "topics.exclude": topics_exclude,
}

connector = Connector()
# The name of the connector.
connector.name = connector_id
connector.configs = configs

request = CreateConnectorRequest(
    parent=parent,
    connector_id=connector_id,
    connector=connector,
)

try:
    operation = connect_client.create_connector(request=request)
    print(f"Waiting for operation {operation.operation.name} to complete...")
    response = operation.result()
    print("Created Connector:", response)
except GoogleAPICallError as e:
    print(f"The operation failed with error: {e}")

接下来怎么做?

Apache Kafka® 是 Apache Software Foundation 或其关联公司在美国和/或其他国家/地区的注册商标。