Todos los productos
Search
Centro de documentos

DataHub:Compatibilidad con Kafka

Última actualización: Sep 05, 2026

DataHub es totalmente compatible con el protocolo de Apache Kafka. Puede utilizar clientes nativos de Kafka para leer datos de DataHub y escribir datos en él.

Mapeo de Kafka a DataHub

Tipos de temas

Kafka y DataHub tienen mecanismos diferentes para escalar temas. Para garantizar la compatibilidad con el comportamiento de Kafka, debe establecer el modo de escalado en ONLY_EXTEND cuando cree un tema de DataHub. En este modo, solo puede agregar nuevos shards a un tema. Este modo no permite dividir ni fusionar shards, y aún no se admite la eliminación de shards.

Nomenclatura de temas

El nombre de un tema de Kafka se asigna a un proyecto y un tema de DataHub, separados por un punto (.). El mapeo sigue estas reglas:

  • La parte anterior al primer . corresponde al proyecto de DataHub, y la parte posterior corresponde al tema de DataHub. Por ejemplo, test_project.test_topic se asigna al proyecto test_project y al tema test_topic.

  • Si un nombre contiene varios caracteres ., solo el primer . actúa como separador. Todos los caracteres . y - restantes se reemplazan por _.

Particiones

Cada shard activo en DataHub corresponde a una partición en Kafka. Por ejemplo, si un tema tiene cinco shards activos, equivale a un tema de Kafka con cinco particiones. Al escribir datos, puede especificar un ID de partición en el rango [0, 4]. Si no especifica una partición, el cliente de Kafka asigna una automáticamente.

Temas Tuple

Cuando escribe datos desde Kafka hacia un tema Tuple, el esquema del tema debe tener una o dos columnas, ambas de tipo STRING. De lo contrario, la operación de escritura fallará.

  • Si el esquema tiene una sola columna, solo se escribe el valor y se descarta la clave.

  • Si el esquema tiene dos columnas, la primera y la segunda columna corresponden a la clave y al valor, respectivamente.

Además, no escriba datos binarios en un tema Tuple, ya que esto provoca caracteres ilegibles. Para almacenar datos binarios, utilice un tema Blob.

Temas Blob

Cuando escribe datos desde Kafka hacia un tema Blob, el valor del mensaje de Kafka se escribe en el campo Blob. Si la clave del mensaje no es NULL, se escribe como un atributo de DataHub. El nombre del atributo es __kafka_key__ y su valor es la clave del mensaje de Kafka.

Encabezados

Los encabezados de Kafka se asignan a atributos de DataHub. Si el valor de un encabezado es NULL, se ignora y no se escribe como atributo. Se recomienda no utilizar __kafka_key__ como clave de encabezado para evitar conflictos con el nombre de atributo integrado en los temas Blob.

Grupos de consumidores

En DataHub, un ID de suscripción actúa como un grupo de consumidores, pero solo puede suscribirse a un único tema. En cambio, un grupo de consumidores de Kafka puede suscribirse a varios temas simultáneamente. Para ofrecer compatibilidad con el modelo de suscripción de Kafka, DataHub ofrece una función de grupos. Puede crear un grupo dentro de un proyecto y vincularlo a varios temas, lo que le permite suscribirse a todos ellos bajo un mismo grupo.

Un grupo gestiona internamente múltiples suscripciones de DataHub en el servidor. Después de vincular un tema, el grupo crea automáticamente una suscripción, la cual aparece en la lista de suscripciones de la página de detalles del tema. No elimine esta suscripción manualmente. Hacerlo impedirá que el grupo se suscriba al tema y provocará la pérdida de todos los desplazamientos de consumo existentes.

Un solo grupo puede suscribirse a un máximo de 50 temas. Para suscribirse a más temas, envíe un ticket de soporte.

Parámetros de Kafka

C=Consumidor, P=Productor, S=Streams

Parámetro

C/P/S

Valor

Obligatorio

Descripción

bootstrap.servers

*

Consulte la sección de endpoints de Kafka.

Sí

security.protocol

*

SASL_SSL

Sí

Para garantizar la seguridad de los datos, las conexiones de Kafka a DataHub utilizan cifrado SSL de forma predeterminada.

sasl.mechanism

*

PLAIN

Sí

El mecanismo de autenticación para credenciales AccessKey. Solo se admite PLAIN.

compression.type

P

LZ4

No

Especifica el tipo de compresión para los mensajes. Actualmente, solo se admite LZ4.

group.id

C

project.topic:subId

o bien

project.group

Sí

Si utiliza el formato project.topic:subId, el ID debe coincidir con el tema suscrito. De lo contrario, no se podrán leer los datos. Se recomienda utilizar el formato project.group.

partition.assignment.strategy

C

org.apache.kafka.clients.consumer.RangeAssignor

No

La estrategia de asignación de particiones predeterminada en Kafka es RangeAssignor. DataHub actualmente solo admite esta estrategia. No modifique este parámetro.

session.timeout.ms

C/S

[60000, 180000]

No

El valor predeterminado en Kafka es 10,000 ms. Sin embargo, dado que DataHub requiere un mínimo de 60,000 ms, este valor se ajusta automáticamente a 60,000 ms.

heartbeat.interval.ms

C/S

Recomendado: 2/3 de session.timeout.ms

No

El valor predeterminado de Kafka es 3,000 ms. Dado que session.timeout.ms se ajusta a 60,000 ms, se recomienda establecer explícitamente este valor en 40000 para evitar solicitudes frecuentes de latido.

application.id

S

project.topic:subId

o bien

project.group

Sí

Si utiliza el formato project.topic:subId, el ID debe coincidir con el tema suscrito. De lo contrario, no se podrán leer los datos. Se recomienda utilizar el formato project.group.

Esta tabla enumera los parámetros clave que deben revisarse al utilizar un cliente de Kafka con DataHub. Otros parámetros del lado del cliente, como retries y batch.size, se comportan igual que en Kafka nativo. Los parámetros del lado del servidor no modifican el comportamiento real de DataHub. Por ejemplo, independientemente del valor de acks, DataHub devuelve una confirmación únicamente después de que los datos se han escrito por completo.

Endpoints de Kafka

Región

ID de región

Endpoint público

Endpoint de ECS (red clásica)

Endpoint de ECS (VPC)

China (Hangzhou)

cn-hangzhou

dh-cn-hangzhou.aliyuncs.com:9092

dh-cn-hangzhou.aliyun-inc.com:9093

dh-cn-hangzhou-int-vpc.aliyuncs.com:9094

China (Shanghai)

cn-shanghai

dh-cn-shanghai.aliyuncs.com:9092

dh-cn-shanghai.aliyun-inc.com:9093

dh-cn-shanghai-int-vpc.aliyuncs.com:9094

China (Beijing)

cn-beijing

dh-cn-beijing.aliyuncs.com:9092

dh-cn-beijing.aliyun-inc.com:9093

dh-cn-beijing-int-vpc.aliyuncs.com:9094

China (Zhangjiakou)

cn-zhangjiakou

dh-cn-zhangjiakou.aliyuncs.com:9092

dh-cn-zhangjiakou.aliyun-inc.com:9093

dh-cn-zhangjiakou-int-vpc.aliyuncs.com:9094

China (Shenzhen)

cn-shenzhen

dh-cn-shenzhen.aliyuncs.com:9092

dh-cn-shenzhen.aliyun-inc.com:9093

dh-cn-shenzhen-int-vpc.aliyuncs.com:9094

Singapore

ap-southeast-1

dh-ap-southeast-1.aliyuncs.com:9092

dh-ap-southeast-1.aliyun-inc.com:9093

dh-ap-southeast-1-int-vpc.aliyuncs.com:9094

Malaysia (Kuala Lumpur)

ap-southeast-3

dh-ap-southeast-3.aliyuncs.com:9092

dh-ap-southeast-3.aliyun-inc.com:9093

dh-ap-southeast-3-int-vpc.aliyuncs.com:9094

Germany (Frankfurt)

eu-central-1

dh-eu-central-1.aliyuncs.com:9092

dh-eu-central-1.aliyun-inc.com:9093

dh-eu-central-1-int-vpc.aliyuncs.com:9094

China East 2 Finance

cn-shanghai-finance-1

dh-cn-shanghai-finance-1.aliyuncs.com:9092

dh-cn-shanghai-finance-1.aliyun-inc.com:9093

dh-cn-shanghai-finance-1-int-vpc.aliyuncs.com:9094

China (Hong Kong)

cn-hongkong

dh-cn-hongkong.aliyuncs.com:9092

dh-cn-hongkong.aliyun-inc.com:9093

dh-cn-hongkong-int-vpc.aliyuncs.com:9094

Crear un tema

  1. Crear un tema en la consola

    Al crear un tema, habilite el modo de expansión de shards (Shard Expand Mode).

  2. Crear un tema mediante el SDK

    No es posible crear temas mediante la API de Kafka. Debe utilizar el SDK de DataHub y establecer ExpandMode en ONLY_EXTEND. La versión de dependencia de Maven requerida es la 2.19.0 o posterior.

    Se recomienda utilizar variables de entorno para configurar su AccessKey ID y AccessKey Secret, y evitar codificarlos directamente en el código de su proyecto. El par de AccessKey de una cuenta de Alibaba Cloud tiene permisos para todas las operaciones de la API. Para mayor seguridad, utilice un par de AccessKey de un usuario de RAM para el acceso a la API o las operaciones diarias, con el fin de reducir el riesgo de fuga de credenciales.

    datahub.endpoint=<yourEndpoint>
    datahub.accessId=<yourAccessKeyId>
    datahub.accessKey=<yourAccessKeySecret>
    <dependency>
      <groupId>com.aliyun.datahub</groupId>
      <artifactId>aliyun-sdk-datahub</artifactId>
      <version>2.19.0-public</version>
    </dependency>
    @Value("${datahub.endpoint}")
    String endpoint ;
    @Value("${datahub.accessId}")
    String accessId;
    @Value("${datahub.accessKey}")
    String accessKey;
    public class CreateTopic {
        public static void main(String[] args) {
            DatahubClient datahubClient = DatahubClientBuilder.newBuilder()
                    .setDatahubConfig(
                            new DatahubConfig(endpoint,
                                    new AliyunAccount(accessId, accessKey)))
                    .build();
    
            int shardCount = 1;
            int lifeCycle = 7;
    
            try {
                datahubClient.createTopic("test_project", "test_topic", shardCount, lifeCycle, RecordType.BLOB, "comment", ExpandMode.ONLY_EXTEND);
            } catch (DatahubClientException e) {
                e.printStackTrace();
            }
        }
    }

Crear un grupo

  1. Crear un grupo en la consola

    Haga clic en Create Group y agregue los temas a los que desea suscribirse desde la lista de la derecha. Puede modificar los temas vinculados después de crear el grupo. El grupo crea automáticamente una suscripción, la cual aparece en la página de lista de suscripciones del tema.

  2. Crear un grupo mediante el SDK

    La versión de dependencia de Maven debe ser la 2.21.6-public o posterior.

    <dependency>
      <groupId>com.aliyun.datahub</groupId>
      <artifactId>aliyun-sdk-datahub</artifactId>
      <version>2.21.6-public</version>
    </dependency>
    @Value("${datahub.endpoint}")
    String endpoint ;
    @Value("${datahub.accessId}")
    String accessId;
    @Value("${datahub.accessKey}")
    String accessKey;
    public class CreateGroup {
        public static void main(String[] args) {
            DatahubClient datahubClient = DatahubClientBuilder.newBuilder()
                    .setDatahubConfig(
                            new DatahubConfig(endpoint,
                                    new AliyunAccount(accessId, accessKey)))
                    .build();
    
            List<String> topicList = new ArrayList<>();
            topicList.add("test_project.topic1");
            topicList.add("test_project.topic2");
            topicList.add("test_project.topic3");
    
            try {
                // Create a Kafka group.
                datahubClient.createKafkaGroup("test_project", "test_topic", "test comment");
    
                // Bind the topics to the group for subscription.
                datahubClient.updateTopicsForKafkaGroup("test_project", "test_topic", topicList, UpdateKafkaGroupMode.ADD);
            } catch (DatahubClientException e) {
                e.printStackTrace();
            }
        }
    }

Ejemplo de productor

El archivo kafka_client_producer_jaas.conf

Cree un archivo llamado kafka_client_producer_jaas.conf en cualquier directorio y agregue el siguiente contenido.

KafkaClient {
  org.apache.kafka.common.security.plain.PlainLoginModule required
  username="yourAccessKeyId"
  password="yourAccessKeySecret";
};

Dependencia de Maven

La versión del cliente de Kafka debe ser la 0.10.0.0 o posterior. Se recomienda la versión 2.4.0.

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.4.0</version>
</dependency>

Código de ejemplo

public class ProducerExample {
    static {
        System.setProperty("java.security.auth.login.config", "src/main/resources/kafka_client_producer_jaas.conf");
    }

    public static void main(String[] args) {

        Properties properties = new Properties();
        properties.put("bootstrap.servers", "dh-cn-hangzhou.aliyuncs.com:9092");
        properties.put("security.protocol", "SASL_SSL");
        properties.put("sasl.mechanism", "PLAIN");
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("compression.type", "lz4");

        String KafkaTopicName = "test_project.test_topic";
        Producer<String, String> producer = new KafkaProducer<String, String>(properties);

        try {
            List<Header> headers = new ArrayList<>();
            RecordHeader header1 = new RecordHeader("key1", "value1".getBytes());
            RecordHeader header2 = new RecordHeader("key2", "value2".getBytes());
            headers.add(header1);
            headers.add(header2);

            ProducerRecord<String, String> record = new ProducerRecord<>(KafkaTopicName, 0, "key", "Hello DataHub!", headers);

            // Sync send
            producer.send(record).get();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } finally {
            producer.close();
        }
    }
}

Resultado

Una vez que el código se ejecute correctamente, puede muestrear datos para verificar el resultado.

Ejemplo de consumidor

Para obtener información sobre cómo generar el archivo kafka_client_producer_jaas.conf y agregar la dependencia de Maven, consulte el ejemplo del productor.

Cuando se une un nuevo consumidor, la asignación de shards tarda entre 10 y 20 segundos. Una vez completada la asignación, el consumidor puede comenzar a consumir datos.

Código de ejemplo

Uso de un grupo de Kafka (recomendado)

package com.aliyun.datahub.kafka.demo;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class ConsumerExample2 {

    static {
        System.setProperty("java.security.auth.login.config", "src/main/resources/kafka_client_producer_jaas.conf");
    }

    public static void main(String[] args) {

        Properties properties = new Properties();
        properties.put("bootstrap.servers", "dh-cn-hangzhou.aliyuncs.com:9092");
        properties.put("security.protocol", "SASL_SSL");
        properties.put("sasl.mechanism", "PLAIN");
        // Set group.id to the project.group format.
        properties.put("group.id", "test_project.test_kafka_group");
        properties.put("auto.offset.reset", "earliest");
        properties.put("session.timeout.ms", "60000");
        properties.put("heartbeat.interval.ms", "40000");
        properties.put("ssl.endpoint.identification.algorithm", "");
        properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);

        List<String> topicList = new ArrayList<>();
        topicList.add("test_project.test_topic1");
        topicList.add("test_project.test_topic2");
        topicList.add("test_project.test_topic3");
        // By using a Kafka group, you can subscribe to multiple topics.
        kafkaConsumer.subscribe(topicList);

        while (true) {
            ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofSeconds(5));

            for (ConsumerRecord<String, String> record : records) {
                System.out.println(record.toString());
            }
        }
    }
}

Uso de project.topic:subId

package com.aliyun.datahub.kafka.demo;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
public class ConsumerExample {

    static {
        System.setProperty("java.security.auth.login.config", "src/main/resources/kafka_client_producer_jaas.conf");
    }

    public static void main(String[] args) {

        Properties properties = new Properties();
        properties.put("bootstrap.servers", "dh-cn-hangzhou.aliyuncs.com:9092");
        properties.put("security.protocol", "SASL_SSL");
        properties.put("sasl.mechanism", "PLAIN");
        // Set group.id to the project.topic:subId format.
        properties.put("group.id", "test_project.test_topic:1611039998153N71KM");
        properties.put("auto.offset.reset", "earliest");
        properties.put("session.timeout.ms", "60000");
        properties.put("heartbeat.interval.ms", "40000");
        properties.put("ssl.endpoint.identification.algorithm", "");
        properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);

        // When using the project.topic:subId format, you can only subscribe to a single topic.
        kafkaConsumer.subscribe(Collections.singletonList("test_project.test_topic"));

        while (true) {
            ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofSeconds(5));

            for (ConsumerRecord<String, String> record : records) {
                System.out.println(record.toString());
            }
        }
    }
}

Resultado

Después de que el código se ejecute correctamente, podrá ver los datos consumidos en su terminal.

ConsumerRecord(topic = test_project.test_topic, partition = 0, leaderEpoch = 0, offset = 0, LogAppendTime = 1611040892661, serialized key size = 3, serialized value size = 14, headers = RecordHeaders(headers = [RecordHeader(key = key1, value = [118, 97, 108, 117, 101, 49]), RecordHeader(key = key2, value = [118, 97, 108, 117, 101, 50])], isReadOnly = false), key = key, value = Hello DataHub!)

En este ejemplo, todos los registros de datos devueltos en una sola solicitud tienen el mismo LogAppendTime, que corresponde a la marca de tiempo más reciente entre todos los registros de ese lote.

Ejemplo de Streams

Dependencia de Maven

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-streams</artifactId>
    <version>2.4.0</version>
</dependency>

Código de ejemplo

Este ejemplo lee datos de un tema de entrada dentro de test_project, convierte las cadenas de clave y valor a minúsculas, y escribe el resultado en un tema de salida.

public class StreamExample {

    static {
        System.setProperty("java.security.auth.login.config", "src/main/resources/kafka_client_producer_jaas.conf");
    }

    public static void main(final String[] args) {
        final String input = "test_project.input";
        final String output = "test_project.output";
        final Properties properties = new Properties();
        properties.put("bootstrap.servers", "dh-cn-hangzhou.aliyuncs.com:9092");
        properties.put("application.id", "test_project.input:1611293595417QH0WL");
        properties.put("security.protocol", "SASL_SSL");
        properties.put("sasl.mechanism", "PLAIN");
        properties.put("session.timeout.ms", "60000");
        properties.put("heartbeat.interval.ms", "40000");
        properties.put("auto.offset.reset", "earliest");

        final StreamsBuilder builder = new StreamsBuilder();
        TestMapper testMapper = new TestMapper();
        builder.stream(input, Consumed.with(Serdes.String(), Serdes.String()))
                .map(testMapper)
                .to(output, Produced.with(Serdes.String(), Serdes.String()));

        final KafkaStreams streams = new KafkaStreams(builder.build(), properties);
        final CountDownLatch latch = new CountDownLatch(1);

        Runtime.getRuntime().addShutdownHook(new Thread("streams-shutdown-hook") {
            @Override
            public void run() {
                streams.close();
                latch.countDown();
            }
        });

        try {
            streams.start();
            latch.await();
        } catch (final Throwable e) {
            System.exit(1);
        }
        System.exit(0);
    }

    static class TestMapper implements KeyValueMapper<String, String, KeyValue<String, String>> {

        @Override
        public KeyValue<String, String> apply(String s, String s2) {
            return new KeyValue<>(StringUtils.lowerCase(s), StringUtils.lowerCase(s2));
        }
    }
}

Resultado

Después de iniciar la tarea de Streams, la asignación de shards tarda aproximadamente un minuto. Posteriormente, podrá ver el número de tareas actuales en la consola. El número de tareas coincide con el número de shards del tema de entrada. En este ejemplo, el tema de entrada tiene tres shards.

currently assigned active tasks: [0_0, 0_1, 0_2]
currently assigned standby tasks: []
revoked active tasks: []
  revoked standby tasks: []

Una vez asignados los shards, puede escribir datos de prueba como (AAAA,BBBB),(CCCC,DDDD),(EEEE,FFFF) en el tema de entrada. Luego, muestree datos del tema de salida para verificar que se escribieron correctamente.

Notas de uso

  • No se admiten transacciones ni idempotencia.

  • Los clientes de Kafka no pueden crear temas automáticamente en DataHub. Debe crear el tema antes de escribir datos en él.

  • Al utilizar un ID de suscripción (project.topic:subid) como group.id, un consumidor solo puede suscribirse a un tema. Para suscribirse a varios temas, utilice un grupo de DataHub.

  • La marca de tiempo de los datos leídos por un consumidor siempre es el LogAppendTime, que indica cuándo se escribieron los datos en DataHub. Todos los registros en una sola solicitud de obtención comparten la misma marca de tiempo: la más reciente de ese lote. Esto significa que la marca de tiempo de lectura podría ser posterior a la hora real de escritura.

  • Una aplicación de Streams admite solo un tema de entrada, pero puede tener varios temas de salida.

  • Solo se admiten tareas de Streams sin estado.

  • Las versiones compatibles de Kafka van desde la 0.10.0 hasta la 2.4.0.

Preguntas frecuentes

Desconexión al escribir datos

Selector - [Producer clientId=producer-1] Connection with dh-cn-shenzhen.aliyuncs.com disconnected
java.io.EOFException
    at org.apache.kafka.common.network.SslTransportLayer.read(SslTransportLayer.java:573)
    ...

Las solicitudes de metadatos de Kafka y las solicitudes de escritura de datos utilizan conexiones diferentes.

El cliente establece primero una conexión para obtener metadatos. Luego utiliza la información del broker devuelta para establecer una segunda conexión destinada a la escritura de datos. Todas las solicitudes posteriores se envían a través de esta segunda conexión.

La primera conexión, ahora inactiva, se cierra automáticamente por el servidor tras un tiempo de espera. Esto puede generar un error de desconexión en los registros. Puede ignorar este error si los datos se están escribiendo correctamente.

Fallo al iniciar el cliente de Kafka

Caused by: org.apache.kafka.common.errors.SslAuthenticationException: SSL handshake failed
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 100.67.134.161 found

Agregue la siguiente propiedad a su configuración: properties.put("ssl.endpoint.identification.algorithm", "");.

DisconnectException durante el consumo

[INFO][Consumer clientId=client-id, groupId=consumer-project.topic:subid] Error sending fetch request (sessionId=INVALID, epoch=INITIAL) to node 1: {}.
org.apache.kafka.common.errors.DisconnectException

El cliente de Kafka debe mantener una conexión TCP persistente con el servidor. Esta excepción suele deberse a inestabilidad en la red. El cliente cuenta con una lógica de reintento integrada, por lo que este error generalmente no afecta el consumo.