随着企业继续接受事件驱动架构并应对大数据的机遇,企业在整合Apache Kafka和MongoDB方面取得了巨大成功。 这两种互补的技术为解决这些大规模的挑战提供了动力和灵活性。 今天,MongoDB继续投资于Apache Kafka的MongoDB连接器,发布了1.4版本 在过去的几个月里,我们一直在收集反馈,并学习如何最好地帮助我们的客户在Apache Kafka生态系统中整合MongoDB。 本文重点介绍了这个新版本的一些关键功能。
MongoDB的选择性复制
在许多解决方案中,能够仅仅跟踪已经改变的数据是一个重要的用例。 自从连接器的原始版本以来,变化数据捕获(CDC)已经在水槽上可用。 然而,直到1.4版本,CDC事件的来源只能通过Debezium MongoDB连接器从MongoDB获取。 在最新的版本中,你可以在水槽上指定MongoDB变化流处理程序,以读取和重放使用Apache Kafka的MongoDB连接器从MongoDB获取的MongoDB事件。 这项功能使你能够记录MongoDB中命名空间的插入、更新和删除活动,并在目标MongoDB集群上重放这些活动。 实际上,你有一个轻量级的方法来通过Kafka执行MongoDB数据的基本复制。
让我们深入了解一下,看看引擎盖下正在发生什么。回顾一下,当连接器被用作MongoDB的源时,它在一个特定的命名空间上启动一个变化流。 根据你配置源连接器的方式,文件会被写入基于这个命名空间和管道的Kafka主题,这些文件符合你的标准。 这些文件默认为变化流的事件格式。下面是Kafka主题中的部分消息,它是由以下语句生成的:db.Source.insert({proclaim: "Hello World!"}):
{
"schema": {
"type": "string",
"optional": false
},
"payload": {
"_id": {
"_data": "82600B38...."
},
"operationType": "insert",
"clusterTime": {
"$timestamp": {
"t": 1611348141,
"i": 2
}
},
"fullDocument": {
"_id": {
"$oid": "600b38ad6011ef6265c3acd1"
},
"proclaim": "Hello World!"
},
"ns": {
"db": "Tutorial3",
"coll": "Source"
},
"documentKey": {
"_id": {
"$oid": "600b38ad6011ef6265c3acd1"
}
}
}
}
现在我们的变化流消息已经在Kafka主题中了,我们可以把连接器作为一个sink来读取消息流,并在目标集群中重放它们。 为了设置水槽以消耗这些事件,将 "change.data.capture.handler "设置为新的com.mongodb.kafka.connect.sink.cdc.mongodb.changeStreamHandler属性。
注意,其中一个字段是 "operationType"。 sink连接器将只支持对命名空间的插入、更新和删除操作,不支持创建数据库对象(如用户、命名空间、索引、视图和其他元数据)等操作,这些操作发生在更传统的复制解决方案中。 此外,这项功能并不打算作为全功能复制系统的替代品,因为它不能保证两个集群之间的事务性一致性。 也就是说,如果你想做的只是移动数据,并且可以接受其缺乏一致性,那么你就可以使用新的ChangeStreamHandler的一个简单解决方案。
要了解这个新功能的教程,请查看GitHub中的MongoDB Connector for Apache Kafka Tutorials的教程3。
动态命名空间映射
当我们使用MongoDB连接器作为水槽时,我们会将驻留在Kafka Topic上的数据插入一个集合中。 在1.4版本之前,一旦定义了这种映射,就不可能将话题数据路由到另一个集合。 在这个版本中,我们增加了动态映射命名空间到Kafka主题消息内容的能力。
例如,考虑一个Kafka主题 "Customers.Orders",其中包含以下消息:
{"orderid":1,"country":"ES"}
{"orderid":2,"country":"US"}
我们希望这些消息能根据国家值被放在自己的集合中。 因此,字段为 "orderid "且数值为1的消息将被复制到一个名为 "ES "的集合中。 同样地,字段 "orderid "的值为2的消息将被复制到一个叫做 "US "的集合中。
为了了解我们如何配置这个场景,我们将使用新的namespace.mapper属性定义一个sink,配置的值为 "com.mongodb.kafka.connect.sink.namespace.mapping.FieldPathNamespaceMapper"。 使用这个映射器,我们可以使用一个键或值字段来分别确定数据库和集合。 在我们上面的例子中,让我们使用国家字段的值来定义我们的配置,作为下沉到的集合名称:
'{"name": "mongo-dynamic-sink",
"config": {
"connector.class":"com.mongodb.kafka.connect.MongoSinkConnector",
"topics":"Customers.Orders",
"connection.uri":"mongodb://mongo1:27017,mongo2:27017,mongo3:27017",
"database":"Orders",
"collection":"Other"
"value.converter":"org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable":"false",
"namespace.mapper":"com.mongodb.kafka.connect.sink.namespace.mapping.FieldPathNamespaceMapper",
"namespace.mapper.value.collection.field":"country" }}
没有国家值的消息默认会被写入配置中定义的命名空间,就像没有映射时一样。 然而,如果你想让不符合映射的消息产生一个错误,只需将属性namespace.mapper.error.if.invalid设置为true。 当消息由于缺少字段或字段不是字符串而不能被映射到命名空间时,这将引发一个错误并停止连接器。
如果你想对命名空间有更多的控制,你可以使用接口com.mongodb.kafka.connect.sink.namespace.mapping.NamespaceMapper的新 "getNamespace "方法。 这个方法的实现可以实现更复杂的业务规则,并可以访问SinkRecord或SinkDocument作为逻辑的一部分来确定目标命名空间。
动态主题映射
一旦源连接器被配置好,变化流事件就会从连接器中定义的命名空间流向Kafka Topic。 Kafka Topic的名字是由三个配置参数组成的:topic.prefix、数据库和集合。 例如,如果你有作为你的源连接器配置的一部分:
“topic.prefix”:”Stocks”,
“database”:”Customers”,
“collection”:”Orders”
将被创建的Kafka主题是 "Stocks.Customers.Orders"。 然而,如果你并不总是希望Orders集合中的事件总是流向这个特定的主题呢? 如果你想在运行时决定一个特定的消息应该被路由到哪个主题呢?
在1.4中,你现在可以指定一个命名空间地图,定义一个命名空间应该被写入哪个kafka主题。 例如,考虑下面的地图:
{"Customers": "CustomerTopic",
"Customers.Orders": "Orders"}
这将把所有来自Customers 数据库的变化流文件映射到CustomerTopic.<collectionName> ,除了来自Customers.Orders 命名空间的任何文件映射到Orders 主题。
如果你需要使用复杂的业务逻辑来确定路线,你可以在新的TopicMapper类中实现getTopic方法来处理这个映射逻辑。
还要注意的是,1.4版本除了topic.prefix之外,还引入了一个topic.suffix 配置属性。 使用我们上面的例子,你可以配置:
“topic.prefix”:”Stocks”,
“database”:”Customers”,
“collection”:”Orders”,
topics.suffix:”US”
这将定义要写入的主题为 "Stocks.Customers.Orders.US"