AWS DynamoDB 的常用操作

AWS 提供的 NoSQL 数据库有 DynamoDB, DocumentDB(即 MongoDB), 和 Keyspaces(即 Cassandra)。还有一个神秘的早已消失于 AWS 控制台之外的 SimpleDB,它只能通过 API 才能使用。因为 AWS 有意要把它藏起来,不愿被新用户看到它,希望用 DynamoDB 替代它,关于用 aws cli 如何体验 AWS SimpleDB 可见本文后面部分。

DynamoDB 所设计的读写容量参数的概念,AWS 为其标榜是为保证一致性与明确的性能表现,实际上不如说是一个赚钱的计量单位,为了钱反而是把一个简单的事情弄复杂了。当需要全局索引时,必须为全局索引设定读写容量,连索引的钱也不放过。本文只为体验对 DynamoDB 的常用操作,不管吞吐量的问题,所以不用关心读写容量的问题。

DynamoDB 后端用 SSD 存储,不像 Elasticache 是把数据放在内存当,对了 Elasticache 也是 AWS 提供了 NoSQL 服务。DynamoDB 每条记录(Item) 的大小限制为 400K. 阅读全文 >>

创建 AWS EC2 实例时 userdata 的一些知识

我们在初始一个 AWS EC2 实例时,可以通过 user data 让 EC2 第一次启动后做些事情,可以放置 shell script 或 cloud-init 指令。在控制台设置 user data 可用明文文本,由 awscli 创建时可使用一个文件,或者通过 API 用 base64 编码的内容。

下面是 user data 被执行时需知晓的一些知识

  1. 是脚本时必须以 #! 开始,俗称 Shebang, 如 #!/bin/bash
  2. user data是以 root 身份执行,所以不要用 sudo, 当然创建的目录或文件的 owner 也是 root,需要 ec2-user 用户访问的话需要 chmod 修改文件权限,或者直接用 chown ec2-user:ec2-user -R abc 修改文件的所有者()
  3. 脚本不能交互,有交互时必须想办法跳过用户输入,如 apt install -y xzy, 带个  -y 标记
  4. 如果脚本中需访问 AWS 资源,权限由 Instance Profile 所指定的 IAM role 决定
  5. user data 中的脚本会被存储在  /var/lib/cloud/instances/<instance-id>/user-data.txt 文件中,因此也可以从这里验证 user data 是否设置正确。或者在 EC2 实例上访问 http://169.254.169.254/latest/user-data 也能看到 user data 的内容。并且在 EC2 实例初始化后不被删除,所以以此实例为基础来创建一个新的 AMI 需把它删除了
  6. user data 的大小限制为 16 KB, 指 base64 编码前的大小
  7. cloud-init 的输出日志在 /var/log/cloud-init-output.log, 它会捕获 cloud-init 控制台的输出内容

阅读全文 >>

摆脱 Docker Desktop 即将到来的收费

最近本人在 Mac OS X 下安装的 Docker Desktop 经常蹦出要升级的窗口,Skip this update PRO 不能用,Install & Restart  也总是失败,所以每次只能 SNOOZE。原来觉得事有蹊跷,后来才知道这或许与近日吵得沸沸扬扬的 Docker Desktop 收费计划有关。Docker Desktop 是专门为 Windows 和 Mac OS X 定制的,Mac OS X 和 Windows  都是开发人员用的平台。Linux 下可以不用 Docker Desktop,Linux 下 Docker 之间甚至一层窗户纸都没有,实际部署的 Docker 服务也是运行在 Linux 下的。

墙里边微信正在谋划着对聊天记录的云存储收年费,外边是 Docker 在向大公司和开发人员下手,这难道是美国加州版的共同富裕? 阅读全文 >>

DynamoDB Stream 应用及触发 Lambda 函数

DynamoDB Stream 的实质就是一个依附于表的流,对表的增删改像关系型数据的触发器一样,以日志形式按顺记录到该流中。我们可以用 API 去读取其中的记录,或用来触发一个 Lambda。DynamoDB Stream 非常类似于 Kinesis 的 Stream, 它们都是有 Shard 的概念,但是 DynamoDB Stream 的 Shard 数目是不太确定的。而且还能用 KCL(Kinesis Client Library) 来操作 DynamoDB Stream。

DynamoDB Stream 和 Kinesis Stream 有几个通用的 API 操作,像 list_streams(), describe_stream(), get_shard_iterator() 和 get_records() 函数。同时呢,在设置 Lambda 的触发器时,选择 DynamoDB Stream 与 Kinesis Stream 时可配置的参数几乎是一样的,有 Batch size, Batch window, Starting position 以及重试策略。而且也是一个 Shard 只能同时启动一个 Lambda 实例,由于 DynamoDB Stream 的 Shard 数目不太确定,所以它能同时启动几个 Lambda 实例也不确定的。

另外, 一个 DynamoDB Stream 只能最多被两个 Consumer 消费,而可用来消费 Kinesis Stream 的 Consumer 数目是不受限的。DynamoDB Stream 中的记录保存时间为 24 小时, Kinesis Stream 中记录保存时间也是可配置的。我们创建一个 DynamoDB 表时还能启用 Amazon Kinesis data stream details, 即把对 DynamoDB 的操作记录直接发送到 Kinesis Stream 中去,这样就能操作熟悉的 Kinesis Stream,Shard 数目可设定,坏处就是 Kinesis Stream 较费钱。 阅读全文 >>

如何调试 ECR Docker 镜像

常常因为在 AWS 上部署的 ECS 或 EKS 服务,甚至是使用了 ECR 镜像的 Lambda 服务这样或那样的原因无法启动,或其他莫名的异常,这时候最好能直接调试 ECR 上的 Docker 镜像,比调试用于打包 Docker 的源代码更接近真实环境。

要调试 Docker 镜像需要先从 ECR 中下载到 AWS 服务用的镜像,下面以运行 Java 的 Docker 为例,同时用 IntelliJ IDEA 关联源代码进行远程调试。

从 ECR 下载 Docker 镜像部分可参考 推送 Docker 镜像到 Amazon ECR 仓库, 那篇文章写作之时可能与现在略有不同。具体需直接进到 ECR 的页面,如 https://console.aws.amazon.com/ecr/repositories/private/<aws_account_id>/<ecr_name>?region=<region>,点击 View push commands 可看到用 AWS CLI 如何登陆 ECR,现在看到的在 macOS/Linux 下的命令是(假设 AWS AccountId 是 123456789, region 是 us-east-1)

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com

假如 ECR 仓库名是 test-java, 那么要下载 tag 1.0.1 的命令是 阅读全文 >>

Python 子进程与子进程池的应用

去年记录过一篇如何使用 Python 的线程,线程池的日志 Python 多线程编程, 需用到 threading.Thread, concurrent.futures.ThreadPoolExecutor。本文可以当作是上一文 Python 多线程编程的姊妹篇。

Python 的多线程受到 GIL(Global Interpreter Lock) 的限制,GIL 是一把加到了 Python 的解释器的锁,使得在任意时刻只允许一个 Python  进程使用 Python 解释器,也就是任意时刻,Python 只有一个线程在运行。

GIL 严重影响了计算密集型(CPU-bound) 的多线程程序,此时的多线程与单线程性能没什么差异,也发挥不了多核的威力。但对 I/O 密集型(I/O-bound) 影响不大,因为 CPU 多数时候是在等待。

为了突破 GIL 的 CPU 密集型程序的限制,可以使用非 CPython 解释器,如 Jython, IronPython 或 PyPy, 更为现实的做法就是使用子进程来替代线程去承担较为繁重的计算任务,因为 GIL 是加在进程上的,所以新的进程有独立的 GIL. 阅读全文 >>

Python 调用动态库时 Segmentation fault (core dumped) 问题

这几天一直纠缠在如何调用动态库的问题上,先是 Go 语言,而后迁移到 Python 语言。在测试 Python 调用动态库时,出现过 "Segmentation fault (core dumped)" 的问题,本文记录下怎么去寻找线索,找到并解决问题的。

出现 "Segmentation fault (core dumped)" 的原因是多方面的,比如在 C/C++ 语言中

  1. 内存访问越界(数组越界,strcpy, strcat, sprintf, strcmp 等字符串函数读写越界)
  2. 多线程使用了线程不安全的函数
  3. 多线程读写的数据未加锁保护
  4. 非法指针(NULL 指针,随意的指针类型转换
  5. 堆栈溢出(如大的分配在栈上的局部变量)

用 Python 来调用动态库很大的可能性会是内存访问越界 阅读全文 >>

Python 调用 C 动态库(Linux)

Go 调用 C 写的动态库完整例子(Linux版) 弄完了 Go 语言如何调用动态库,又开始琢磨起 Python 怎么调用动态库,首先仍然是以前一篇中的 C 实现为例,C 函数为原型为 char * Add(char* src, int n), 由于用符号直接定位函数,所以无需 C 的头文件。本文仍然是以 Linux 平台为例,GCC 编译为动态库 so 文件。并实验了两个例子,一个为基本的类型,char* 和  int, 再一个就是在 C 中使用到了结构体指针和无类型指针(void*) 时,如何在 Python 进行调用。

测试环境为:

  1. Linux Ubuntu 20.04
  2. gcc: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
  3. Python: 3.8.10

阅读全文 >>

Go 运行期加载 C 动态库(Linux版)

前面写的一篇 Go 调用 C 写的动态库完整例子(Linux版),是在告诉编译器用 /* #cgo ...*/ 的方式去加载动态库 libadd.so,这让代码丧失了一定的灵活性,比如同样的函数由多个动态库提供了不同的实现。这就需要做到在 Go 程序中可根据不同的输入条件选择不同的动态库实现,大概是

if 条件1 {
    loadLibrary("libadd1.so")
    调用其中的实现函数 add
else if 条件 2 {
    loadLibrary("libadd2.so")
    调用其中的实现函数 add
else {
    loadLibrary("libaddx.so")
    调用其中的实现函数  add

当然上面那样写是不行的,首先每一个动态库应该在程序运行期间只加载一次,定位的函数应该要缓存起来复用。 阅读全文 >>

Go 语言使用 Go Modules(go.mod) 来管理依赖

前几日系统性差不多读完了一本讲解 Go 语言的书籍,记录下几篇笔记,现在终于能够开始看看专题性的知识了。首先就是关于 Go 如何管理依赖的问题,Java 经历了最早逐个下载  jar  包,到现在用 maven 来描述项目依赖,及进行项目的构建。而 Go 的起点还是要高一些,从一开始就有  go get, go install 命令来从中心库下载依赖。但管理多版本依赖是个问题,也没有明确的方式来怎么描述一个项目的依赖。

因此也就产生过一些第三方的 Go 依赖管理

  1. Glide(使用 glide.yaml 文件描述依赖)
  2. Godep (长时间没维护的项目)
  3. govendor (不再维护了,在 Go modules 出来后也不被推荐使用)
  4. 还有其他的 GPM, gb, gom, gvt 等都相继退出了历史的舞台

也就是说 Go Modules 才是王道,在理解 Go Modules 之前也许有必要了解一下传统 GOPATH 管理依赖的方式,其他第三方的管理工具用不着去学习了,早已成了云烟。

Go 语言首次在  1.11 版本中引进了 Go Modules, 在这之前或者没有启用 GO111MODULE 的话,我们用 go get 或 go install 来下载依赖,编译器会从 GOPATH 和 vendor 文件夹中查找包。 阅读全文 >>