Exemplo do Kafka Connect com MQTT e MongoDB

Exemplo do Kafka Connect com MQTT e MongoDB

1. Visão geral

Ema previous article, tivemos uma introdução rápida ao Kafka Connect, incluindo os diferentes tipos de conectores, recursos básicos do Connect, bem como a API REST.

Neste tutorial, usaremos conectores Kafka para construir um exemplo mais do "mundo real".

Usaremos um conector para coletar dados via MQTT e gravaremos os dados coletados no MongoDB.

2. Configuração usando Docker

UsaremosDocker Compose para configurar a infraestrutura. Isso inclui um broker MQTT como fonte, Zookeeper, um broker Kafka e o Kafka Connect como middleware e, finalmente, uma instância do MongoDB incluindo uma ferramenta GUI como coletor.

2.1. Instalação do conector

Os conectores necessários para o nosso exemplo, uma fonte MQTT e um conector coletor MongoDB, não estão incluídos no Kafka comum ou na Plataforma Confluent.

Como discutimos no artigo anterior, podemos baixar os conectores (MQTT, bem comoMongoDB) do hub Confluent. Depois disso, temos que descompactar os frascos em uma pasta, que montaremos no contêiner Kafka Connect na seção seguinte.

Vamos usar a pasta/tmp/custom/jars para isso. Temos que mover os frascos para lá antes de iniciar a pilha de composição na seção a seguir, pois o Kafka Connect carrega conectores online durante a inicialização.

2.2. Arquivo Docker Compose

Descrevemos nossa configuração como um simples arquivo de composição do Docker, que consiste em seis contêineres:

version: '3.3'

services:
  mosquitto:
    image: eclipse-mosquitto:1.5.5
    hostname: mosquitto
    container_name: mosquitto
    expose:
      - "1883"
    ports:
      - "1883:1883"
  zookeeper:
    image: zookeeper:3.4.9
    restart: unless-stopped
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
        ZOO_MY_ID: 1
        ZOO_PORT: 2181
        ZOO_SERVERS: server.1=zookeeper:2888:3888
    volumes:
      - ./zookeeper/data:/data
      - ./zookeeper/datalog:/datalog
  kafka:
    image: confluentinc/cp-kafka:5.1.0
    hostname: kafka
    container_name: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
      KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
      KAFKA_BROKER_ID: 1
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    volumes:
      - ./kafka/data:/var/lib/kafka/data
    depends_on:
      - zookeeper
  kafka-connect:
    image: confluentinc/cp-kafka-connect:5.1.0
    hostname: kafka-connect
    container_name: kafka-connect
    ports:
      - "8083:8083"
    environment:
      CONNECT_BOOTSTRAP_SERVERS: "kafka:9092"
      CONNECT_REST_ADVERTISED_HOST_NAME: connect
      CONNECT_REST_PORT: 8083
      CONNECT_GROUP_ID: compose-connect-group
      CONNECT_CONFIG_STORAGE_TOPIC: docker-connect-configs
      CONNECT_OFFSET_STORAGE_TOPIC: docker-connect-offsets
      CONNECT_STATUS_STORAGE_TOPIC: docker-connect-status
      CONNECT_KEY_CONVERTER: org.apache.kafka.connect.json.JsonConverter
      CONNECT_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
      CONNECT_INTERNAL_KEY_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
      CONNECT_INTERNAL_VALUE_CONVERTER: "org.apache.kafka.connect.json.JsonConverter"
      CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: "1"
      CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: "1"
      CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: "1"
      CONNECT_PLUGIN_PATH: '/usr/share/java,/etc/kafka-connect/jars'
      CONNECT_CONFLUENT_TOPIC_REPLICATION_FACTOR: 1
    volumes:
      - /tmp/custom/jars:/etc/kafka-connect/jars
    depends_on:
      - zookeeper
      - kafka
      - mosquitto
  mongo-db:
    image: mongo:4.0.5
    hostname: mongo-db
    container_name: mongo-db
    expose:
      - "27017"
    ports:
      - "27017:27017"
    command: --bind_ip_all --smallfiles
    volumes:
      - ./mongo-db:/data
  mongoclient:
    image: mongoclient/mongoclient:2.2.0
    container_name: mongoclient
    hostname: mongoclient
    depends_on:
      - mongo-db
    ports:
      - 3000:3000
    environment:
      MONGO_URL: "mongodb://mongo-db:27017"
      PORT: 3000
    expose:
      - "3000"

O contêinermosquitto fornece um broker MQTT simples baseado no Eclipse Mosquitto.

Os contêinereszookeeperekafka definem um cluster Kafka de nó único.

kafka-connect define nosso aplicativo Connect no modo distribuído.

Por fim,mongo-db define nosso banco de dados coletor, assim como omongoclient baseado na web, que nos ajuda a verificar se os dados enviados chegaram corretamente ao banco de dados.

Podemos iniciar a pilha usando o seguinte comando:

docker-compose up

3. Configuração do Conector

Como o Kafka Connect agora está em funcionamento, agora podemos configurar os conectores.

3.1. Configurar Conector de Origem

Vamos configurar o conector de origem usando a API REST:

curl -d @/connect-mqtt-source.json -H "Content-Type: application/json" -X POST http://localhost:8083/connectors

Nosso arquivoconnect-mqtt-source.json tem a seguinte aparência:

{
    "name": "mqtt-source",
    "config": {
        "connector.class": "io.confluent.connect.mqtt.MqttSourceConnector",
        "tasks.max": 1,
        "mqtt.server.uri": "tcp://mosquitto:1883",
        "mqtt.topics": "example",
        "kafka.topic": "connect-custom",
        "value.converter": "org.apache.kafka.connect.converters.ByteArrayConverter",
        "confluent.topic.bootstrap.servers": "kafka:9092",
        "confluent.topic.replication.factor": 1
    }
}

Existem algumas propriedades que não usamos antes:

  • mqtt.server.uri é o ponto final ao qual nosso conector se conectará

  • mqtt.topics é o tópico MQTT que nosso conector assinará

  • kafka.topic define o tópico Kafka para o qual o conector enviará os dados recebidos

  • value.converter define um conversor que será aplicado à carga recebida. Precisamos doByteArrayConverter, pois o Conector MQTT usa Base64 por padrão, enquanto queremos usar texto simples

  • confluent.topic.bootstrap.servers é exigido pela versão mais recente do conector

  • O mesmo se aplica aconfluent.topic.replication.factor: ele define o fator de replicação para um tópico interno do Confluente - como temos apenas um nó em nosso cluster, temos que definir esse valor para 1

3.2. Conector de fonte de teste

Vamos fazer um teste rápido publicando uma mensagem curta para o corretor MQTT:

docker run \
-it --rm --name mqtt-publisher --network 04_custom_default \
efrecon/mqtt-client \
pub -h mosquitto  -t "example" -m "{\"id\":1234,\"message\":\"This is a test\"}"

E se ouvirmos o tópico,connect-custom:

docker run \
--rm \
confluentinc/cp-kafka:5.1.0 \
kafka-console-consumer --network 04_custom_default --bootstrap-server kafka:9092 --topic connect-custom --from-beginning

então devemos ver nossa mensagem de teste.

3.3. Configuração do conector coletor

Em seguida, precisamos do nosso conector de coletor. Vamos usar novamente a API REST:

curl -d @/connect-mongodb-sink.json -H "Content-Type: application/json" -X POST http://localhost:8083/connectors

Nosso arquivoconnect-mongodb-sink.json tem a seguinte aparência:

{
    "name": "mongodb-sink",
    "config": {
        "connector.class": "at.grahsl.kafka.connect.mongodb.MongoDbSinkConnector",
        "tasks.max": 1,
        "topics": "connect-custom",
        "mongodb.connection.uri": "mongodb://mongo-db/test?retryWrites=true",
        "mongodb.collection": "MyCollection",
        "key.converter": "org.apache.kafka.connect.json.JsonConverter",
        "key.converter.schemas.enable": false,
        "value.converter": "org.apache.kafka.connect.json.JsonConverter",
        "value.converter.schemas.enable": false
    }
}

Temos as seguintes propriedades específicas do MongoDB aqui:

  • mongodb.connection.uri contém a string de conexão para nossa instância do MongoDB

  • mongodb.collection define a coleção

  • Como o conector MongoDB está esperando JSON, temos que definirJsonConverter parakey.converterevalue.converter

  • E também precisamos de JSON sem esquema para MongoDB, então temos que definirkey.converter.schemas.enable evalue.converter.schemas.enable parafalse

3.4. Conector de coletor de teste

Como nosso tópicoconnect-custom já contém mensagens do teste do conector MQTT,the MongoDB connector should have fetched them directly after creation.

Portanto, devemos encontrá-los imediatamente em nosso MongoDB. We can use the web interface for that, by opening the URL http://localhost:3000/Após o login, podemos selecionar nossoMyCollection à esquerda, pressionarExecute, e nossa mensagem de teste deve ser exibida.

3.5. Teste ponta a ponta

Agora, podemos enviar qualquer estrutura JSON usando o cliente MQTT:

{
    "firstName": "John",
    "lastName": "Smith",
    "age": 25,
    "address": {
        "streetAddress": "21 2nd Street",
        "city": "New York",
        "state": "NY",
        "postalCode": "10021"
    },
    "phoneNumber": [{
        "type": "home",
        "number": "212 555-1234"
    }, {
        "type": "fax",
        "number": "646 555-4567"
    }],
    "gender": {
        "type": "male"
    }
}

O MongoDB suporta documentos JSON sem esquema e, como desabilitamos os esquemas para o nosso conversor, qualquer estrutura é imediatamente passada através da nossa cadeia de conectores e armazenada no banco de dados.

Novamente, podemos usar a interface da web emhttp://localhost:3000/.

3.6. Limpar

Assim que terminarmos, podemos limpar nosso experimento e remover os dois conectores:

curl -X DELETE http://localhost:8083/connectors/mqtt-source
curl -X DELETE http://localhost:8083/connectors/mongodb-sink

Depois disso, podemos encerrar a pilha Compose comCtrl + C.

4. Conclusão

Neste tutorial, criamos um exemplo usando o Kafka Connect, para coletar dados via MQTT e gravar os dados coletados no MongoDB.

Como sempre, os arquivos de configuração podem ser encontrados emon GitHub.