1. 业务场景
最近因为微服务迁移的需要需要将现有微服务中的 DynamoDB Table 和 S3 Bucket 做一个迁移, 新老资源结构不变只需要从一个位置迁移到另外一个位置. 因为是在线系统所以在设计方案时需要考虑以下两点:
- 实时数据迁移
- 历史数据迁移
实时数据迁移优先上线, 历史数据迁移可以慢慢同步, 等到历史数据同步结束, 新的微服务就可以使用新资源, 在同步时需要注意数据准确性和完整性.
2. 方案设计
2.1 历史数据同步
历史数据同步相对比较简单, 最笨的办法可以通过写程序来实现, 通过调研相关的技术文档详细的设计方法如下:
2.1.1 DynamoDB Table 历史数据同步: 编程
DynamoDB 在国内市场似乎用的比较少, 资料和工具都比较少, 经过调研决定使用编程方式解决, 单表同步逻辑如下:
1 | import { |
上面的代码使用 Typescript 编写, 使用 DynamoDB 的 SDK, 使用 Scan 方法循环查询表直到所有的数据查找完; 另外在权限部分使用fromIni
获取本地配置的 AWS Profile 权限.
2.1.2 S3 Bucket 历史数据同步: CLI
AWS CLI 提供的 sync
非常适合做文件迁移, 并且支持增量, 所以 S3 Bucket 的历史数据同步直接使用 Shell 脚本 + AWS CLI 即可, 代码片段如下:
1 |
|
2.2 实时数据同步
实时数据同步是数据迁移方案不停服务无缝迁移的核心, 经过查看 AWS 的相关服务决定使用 DynamoDB Stream 和 S3 Event 来实现.
2.2.1 DynamoDB Table 实时数据同步: DynamoDB Stream
要实现 DynamoDB Table 实时数据同步, 首先要开启 Table Stream, 然后监听 Table 事件, 通过 Lambda 来实现具体的同步逻辑, 这里以 Serverless 框架为例来做代码演示.
首先开启 Table 的 Stream 功能, 并配置事件处理函数, 可以在serverless.ts
中设置:
1 | import { AWS } from '@serverless/typescript' |
该serverless.ts
不完整, 但是核心的资源创建和函数配置是完整的, 实际情况按照自己业务进行调整, 需要关注的有以下几点:
- Dynamodb Table
ExampleTable
是数据源, 同步到ExampleTableTarget
, 其中ExampleTable
设置了StreamSpecification
配置 StreamSpecification
的StreamViewType
配置的是NEW_AND_OLD_IMAGES
, 表示新老数据修改删除都会通知- Stream 事件的监听处理函数是:
functions.tableStream
中定义的dist/lambda.dataSync
函数
Stream 事件的处理函数如下:
1 | import { S3Event } from 'aws-lambda' |
开启 DynamoDB Table 的 Stream 事件后通过 Lambda 函数处理实现表的实时数据同步, 这个同步方法与表无关, 任何表都可以通过该处理函数.
2.2.2 S3 Bucket 实时数据同步: Bucket Event
同 DynamoDB 类似, S3 也有 Bucket 的事件, 这个事件不需要特别开启, 我们直接创建处理函数即可, 至于资源的创建可以查看 2.2.1 中 serverless.ts 中的配置:
- ExampleS3 是数据源, ExampleS3Target 是同步的目标
- 处理函数是:
functions.s3Sync
中定义的dist/lambda.s3Sync
Bucket 事件处理函数和新代码如下:
1 | import AWS from "aws-sdk"; |
这个处理函数需要关注一下ObjectCreated
的逻辑处理, 代码中没有事用copyObject
而是使用 getObject
和upload
这是为了保证准确性, 因为copyObject
不支持对现有的文件进行修改, 所以这里实际是先把原数据下载然后再覆盖目标文件.
3. 总结
本文通过 DynamoDB Table 的 Stream / S3 Bucket Event / Lambda, 实现针对热(实时)数据的同步, 通过 AWS SDK 和 CLI 实现了冷(历史)数据的同步. 其中热数据同步的方法比较有价值, 可以稍微做一些扩展及可以实现很多高级功能, 比如将老表数据拆分到多个新的子表.
在实现过程中需要注意的是: Stream 事件处理函数可以设置多个表的事件监听, 而 Bucket 事件处理有限制, 同一个 Bucket 同一个事件只能被分配给一个处理函数, 所以如果现有代码中已经针对 Bucket Event 有业务处理时需要格外注意.
4. FQA
同一个 Bucket 事件不能分享给不同目标
原始报错: Configurations on the same bucket cannot share a common event type.
要解决这个问题需要检查现有 Bucket 的属性, 检查 Event notifications 看是否已经有相同的事件被分配了其他函数, AWS 不支持相同的 Bucket 事件分配给不同的函数, 所以:
- 不同的函数使用尽量具体的事件
- 合并事件处理逻辑到同一个函数