Apache Pulsar 连接 Apache Cassandra 实战:零代码 Pulsar IO Sink 快速入门指南

  • 消息队列
  • 后端
  • 流处理

【免费下载链接】pulsar

Apache Pulsar - distributed pub-sub messaging system

项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar
点击查看 免费下载

本指南以 Apache Pulsar 的 IO 框架为主线,通过完整实操演示如何在不编写一行业务代码的前提下,将 Pulsar 集群中的消息实时写入 Apache Cassandra 数据库。你将掌握 Pulsar 二进制发行包与内置连接器(Builtin Connectors)的安装、Standalone 模式的启动与健康检查,以及使用 pulsar-admin CLI 创建、查看、验证和删除 Cassandra Sink 连接器的完整流程,并理解 Sink 连接器在 Pulsar Functions 运行时上的底层工作原理。

Pulsar IO 与 Sink 连接器概述

Pulsar IO 连接器(Connector)是 Pulsar 与外部系统集成的核心机制,分为两种类型:

  • Source(源):将数据从外部系统送入 Pulsar,例如 Kafka、RabbitMQ、Twitter Firehose 等;
  • Sink(汇):将数据从 Pulsar 导出到外部系统,例如 Cassandra、Aerospike、Kinesis、Elasticsearch 等 SQL/NoSQL 数据库。

本教程的主角是 Cassandra Sink 连接器:它订阅 Pulsar 中的一个 topic,把每一条消息作为一行写入 Cassandra 的指定 keyspace 和表中,全程无需编写任何代码。Pulsar IO 连接器在架构上是 Pulsar Functions 的一种运行形态——从源码看,Cassandra Sink 实现类 CassandraStringSink 直接实现了 Pulsar Functions API 中的 Sink<byte[]> 接口(见 CassandraStringSink.java),因此你之后可以复用 Functions CLI 来监控和管理它。

背景提示:自 2.1.0-incubating 起,Pulsar 将内置连接器拆分为独立的二进制发行包发布。本指南的所有命令在单机 Standalone 模式下可直接运行,在多节点集群中同样适用,无需任何改动。

安装 Pulsar 二进制发行包

本教程的全部命令都基于 Pulsar 二进制发行包的根目录 执行。获取发行包有几种方式:

  • 从 Apache 镜像站下载 apache-pulsar-<version>-bin.tar.gz;
  • 访问 Pulsar 官方 Downloads 页面或 Releases 页面获取最新版本链接;
  • 使用 wget 下载(将 <binary_release_url> 替换为实际下载地址):
$ wget pulsar:binary_release_url

下载完成后解压并进入目录:

$ tar xvfz apache-pulsar-<version>-bin.tar.gz
$ cd apache-pulsar-<version>

安装内置连接器(Builtin Connectors)

从 2.1.0-incubating 版本开始,Pulsar 将全部内置连接器放在一个独立的二进制发行包中,需要用以下任一方式单独获取:

  • 从 Apache 镜像下载 apache-pulsar-io-connectors-<version>-bin.tar.gz;
  • 从 Pulsar Downloads 页面或 Releases 页面获取;
  • 使用 wget:
$ wget pulsar:connector_release_url

解压并复制 connectors 目录到 Pulsar 根目录下:

$ tar xvfz /path/to/apache-pulsar-io-connectors-<version>-bin.tar.gz
// 解压后在 pulsar 目录下会得到名为 apache-pulsar-io-connectors-<version> 的目录
// 将其中的 connectors 复制出来
$ cp -r apache-pulsar-io-connectors-<version>/connectors connectors

$ ls connectors
pulsar-io-aerospike-<version>.nar
pulsar-io-cassandra-<version>.nar
pulsar-io-kafka-<version>.nar
pulsar-io-kinesis-<version>.nar
pulsar-io-rabbitmq-<version>.nar
pulsar-io-twitter-<version>.nar
...

每个连接器都是一个 NAR 归档(.nar 后缀),其中打包了连接器实现及其全部依赖。以 Cassandra 连接器为例,其 Maven 模块 pulsar-io/cassandra/pom.xml 使用 nifi-nar-maven-plugin 构建,并依赖 pulsar-io-core(IO 核心 API)与 cassandra-driver-core(DataStax Cassandra 驱动)。Pulsar 的 Functions Worker 在启动时通过 ConnectorUtils.searchForConnectors 扫描 connectors 目录下的所有 *.nar 文件,读取每个 NAR 内的 pulsar-io.yaml 服务描述文件,即可自动发现并注册全部内置连接器。

注意:pulsar-admin sink create 命令中的 --sink-type 参数值(例如 cassandra)由连接器 NAR 内 pulsar-io.yaml 中设置的 name 字段决定,而非随意指定。

启动 Pulsar 服务并完成健康检查

在 Pulsar 根目录下启动单机版 Pulsar:

bin/pulsar standalone

Standalone 模式会按顺序启动 Pulsar 的全部组件(BookKeeper、Broker、ZooKeeper、Functions Worker 等)。启动完成后,通过以下四个检查点确认服务就绪:

1. 检查 Pulsar 二进制协议端口(6650):

telnet localhost 6650

2. 检查 Pulsar Functions 集群状态:

curl -s http://localhost:8080/admin/v2/functions/cluster

示例输出:

[{"workerId":"c-standalone-fw-localhost-6750","workerHostname":"localhost","port":6750}]

3. 确认 public tenant 与 default namespace 存在:

curl -s http://localhost:8080/admin/v2/namespaces/public

示例输出:

["public/default","public/functions"]

4. 确认所有内置连接器均已被发现:

curl -s http://localhost:8080/admin/v2/functions/connectors

示例输出:

[{"name":"aerospike","description":"Aerospike database sink","sinkClass":"org.apache.pulsar.io.aerospike.AerospikeStringSink"},{"name":"cassandra","description":"Writes data into Cassandra","sinkClass":"org.apache.pulsar.io.cassandra.CassandraStringSink"},{"name":"kafka","description":"Kafka source and sink connector","sourceClass":"org.apache.pulsar.io.kafka.KafkaStringSource","sinkClass":"org.apache.pulsar.io.kafka.KafkaStringSink"},{"name":"kinesis","description":"Kinesis sink connector","sinkClass":"org.apache.pulsar.io.kinesis.KinesisSink"},{"name":"rabbitmq","description":"RabbitMQ source connector","sourceClass":"org.apache.pulsar.io.rabbitmq.RabbitMQSource"},{"name":"twitter","description":"Ingest data from Twitter firehose","sourceClass":"org.apache.pulsar.io.twitter.TwitterFireHose"}]

响应中的 sinkClass 字段明确列出了 cassandra 对应的实现类 org.apache.pulsar.io.cassandra.CassandraStringSink,该类的 @Connector(name = "cassandra", type = IOType.SINK) 注解(见 CassandraStringSink.java)正是连接器被自动发现与注册的依据。

若启动过程报错,可以在运行 pulsar standalone 的终端看到异常堆栈,也可以查看 Pulsar 目录下 logs 目录中的日志文件定位问题。

在 Docker 中搭建单节点 Cassandra

接下来的步骤需要本机安装 Docker。使用官方 cassandra 镜像启动一个单节点 Cassandra 集群:

docker run -d --rm --name=cassandra -p 9042:9042 cassandra

-p 9042:9042 将容器内的 CQL 原生协议端口映射到宿主机,便于 Pulsar Sink 从宿主机网络访问。

继续之前,用三种方式确认 Cassandra 已就绪:

1. 确认 Docker 进程在运行:

docker ps

2. 检查 Cassandra 日志,确认进程正常启动:

docker logs cassandra

3. 使用 nodetool 检查集群状态:

docker exec cassandra nodetool status

示例输出:

Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
UN  172.17.0.2  103.67 KiB  256          100.0%            af0e4b2f-84e0-4f0b-bb14-bd5f9070ff26  rack1

UN(Up/Normal)表示节点状态正常,可继续后续步骤。

创建 keyspace 与表

使用 cqlsh 连接 Cassandra:

$ docker exec -ti cassandra cqlsh localhost
Connected to Test Cluster at localhost:9042.
[cqlsh 5.0.1 | Cassandra 3.11.2 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
cqlsh>

以下命令均在 cqlsh 提示符中执行。

创建 keyspace:

cqlsh> CREATE KEYSPACE pulsar_test_keyspace WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};

创建表:

cqlsh> USE pulsar_test_keyspace;
cqlsh:pulsar_test_keyspace> CREATE TABLE pulsar_test_table (key text PRIMARY KEY, col text);

编写 Cassandra Sink 配置文件

Cassandra Sink 连接器会从 Pulsar topic 读取消息并写入 Cassandra 表。运行时它需要一个 YAML 配置文件,告知 Pulsar IO 如何定位 Cassandra 集群、使用哪个 keyspace 和表来落库。

新建文件 examples/cassandra-sink.yml,填入以下内容:

configs:
    roots: "localhost:9042"
    keyspace: "pulsar_test_keyspace"
    columnFamily: "pulsar_test_table"
    keyname: "key"
    columnName: "col"

配置项说明(与源码一一对应)

下表字段与 CassandraSinkConfig.java 中定义的属性完全一致,全部为必填项:

配置项类型必填说明
rootsString是逗号分隔的 Cassandra 主机列表,格式为 host:port,用于建立连接。
keyspaceString是用于写入 Pulsar 消息的 keyspace,必须在启动 Sink 前预先创建。
keynameString是Cassandra 列族中用于存储 Pulsar 消息 key 的列名;若消息没有关联 key,则使用消息 value 作为 key。
columnFamilyString是Cassandra 列族(表)名称,必须在启动 Sink 前预先创建。
columnNameString是列族中用于存储 Pulsar 消息 value 的列名。

从实现层面看,Sink 启动时调用 CassandraSinkConfig.load(config) 解析配置,并校验上述五个字段是否为空,任一缺失都会抛出 IllegalArgumentException("Required property not set.")(见 CassandraAbstractSink.java)。roots 支持以逗号分隔的多个主机,内部通过 host:port 解析后逐个添加为 contact point。

提交 Cassandra Sink 连接器

Pulsar 提供了 pulsar-admin CLI 用于运行和管理 IO 连接器(完整的命令参考见 reference-pulsar-admin.md)。执行以下命令,以 cassandra 类型和刚才编写的配置文件提交一个名为 cassandra-test-sink 的 Sink:

bin/pulsar-admin sink create \
    --tenant public \
    --namespace default \
    --name cassandra-test-sink \
    --sink-type cassandra \
    --sinkConfigFile examples/cassandra-sink.yml \
    --inputs test_cassandra

命令执行成功后,Pulsar 会创建一个名为 cassandra-test-sink 的 Sink 连接器。它以 Pulsar Function 的形态运行:订阅输入 topic test_cassandra,将消息写入 Cassandra 表 pulsar_test_table。

查看与检查 Sink 运行状态

由于 IO 连接器本质上运行在 Pulsar Functions 运行时上,因此可以复用 Functions CLI 进行查看和管理。

获取 Sink 配置信息

bin/pulsar-admin functions get \
    --tenant public \
    --namespace default \
    --name cassandra-test-sink

示例输出:

{
  "tenant": "public",
  "namespace": "default",
  "name": "cassandra-test-sink",
  "className": "org.apache.pulsar.functions.api.utils.IdentityFunction",
  "autoAck": true,
  "parallelism": 1,
  "source": {
    "topicsToSerDeClassName": {
      "test_cassandra": ""
    }
  },
  "sink": {
    "configs": "{\"roots\":\"cassandra\",\"keyspace\":\"pulsar_test_keyspace\",\"columnFamily\":\"pulsar_test_table\",\"keyname\":\"key\",\"columnName\":\"col\"}",
    "builtin": "cassandra"
  },
  "resources": {}
}

可以看到,sink.builtin 字段标记为 cassandra,sink.configs 中携带了我们在 YAML 中声明的全部连接参数,source.topicsToSerDeClassName 则列出了输入 topic test_cassandra。

检查 Sink 运行状态

bin/pulsar-admin functions getstatus \
    --tenant public \
    --namespace default \
    --name cassandra-test-sink

示例输出:

{
  "functionStatusList": [
    {
      "running": true,
      "instanceId": "0",
      "metrics": {
        "metrics": {
          "__total_processed__": {},
          "__total_successfully_processed__": {},
          "__total_system_exceptions__": {},
          "__total_user_exceptions__": {},
          "__total_serialization_exceptions__": {},
          "__avg_latency_ms__": {}
        }
      },
      "workerId": "c-standalone-fw-localhost-6750"
    }
  ]
}

running: true 表示 Sink 实例(instanceId: 0)已正常运行在 Functions Worker(c-standalone-fw-localhost-6750)上。

生产消息并验证数据落库

向 Sink 的输入 topic test_cassandra 生产 10 条消息:

for i in {0..9}; do bin/pulsar-client produce -m "key-$i" -n 1 test_cassandra; done

再次查看 Sink 状态,应能看到 10 条消息已被成功处理:

bin/pulsar-admin functions getstatus \
    --tenant public \
    --namespace default \
    --name cassandra-test-sink

示例输出:

{
  "functionStatusList": [
    {
      "running": true,
      "numProcessed": "11",
      "numSuccessfullyProcessed": "11",
      "lastInvocationTime": "1532031040117",
      "instanceId": "0",
      "metrics": {
        "metrics": {
          "__total_processed__": {
            "count": 5.0,
            "sum": 5.0,
            "max": 5.0
          },
          "__total_successfully_processed__": {
            "count": 5.0,
            "sum": 5.0,
            "max": 5.0
          },
          "__total_system_exceptions__": {},
          "__total_user_exceptions__": {},
          "__total_serialization_exceptions__": {},
          "__avg_latency_ms__": {}
        }
      },
      "workerId": "c-standalone-fw-localhost-6750"
    }
  ]
}

numProcessed 与 numSuccessfullyProcessed 均为 11,说明 10 条消息加上启动初始化均已成功处理,无系统异常与用户异常。

最后进入 cqlsh 验证 Cassandra 中的数据:

docker exec -ti cassandra cqlsh localhost

查询 pulsar_test_table 表中的数据:

cqlsh> use pulsar_test_keyspace;
cqlsh:pulsar_test_keyspace> select * from pulsar_test_table;

 key    | col
--------+--------
  key-5 |  key-5
  key-0 |  key-0
  key-9 |  key-9
  key-2 |  key-2
  key-1 |  key-1
  key-3 |  key-3
  key-6 |  key-6
  key-7 |  key-7
  key-4 |  key-4
  key-8 |  key-8

10 条消息(key-0 到 key-9)全部写入成功。这是因为 CassandraStringSink 将输入消息视为字符串,并生成 key 与 value 完全相同的键值对:若消息自带 key 则使用消息 key,否则用消息 value 充当 key。写入路径上,CassandraAbstractSink.write 通过预编译的 INSERT INTO <columnFamily> (<keyname>, <columnName>) VALUES (?, ?) 语句异步执行插入,成功回调 record.ack() 确认消息,失败则回调 record.fail() 触发重试——这也是 Pulsar IO Sink 侧 at-least-once 语义的实现基础。

删除 Cassandra Sink

验证完毕后,可以清理掉该 Sink 连接器:

bin/pulsar-admin sink delete \
    --tenant public \
    --namespace default \
    --name cassandra-test-sink

延伸阅读

  • 消息队列
  • 后端
  • 流处理

【免费下载链接】pulsar

Apache Pulsar - distributed pub-sub messaging system

项目地址: https://gitcode.com/gh_mirrors/pulsar28/pulsar
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值