分布式计算有这么一个需求,主进程准备好输入数据,然后根据输入中某个 Items 动态调用若干计算进程,待到所有计算完成后再汇集结果。这一需求移植到 AWS 上就像是下面这样子
但在一个 Lambda 中同步调用其他 Lambda 时就有个费时费钱的问题,虽然我们采用线程池来调用 Lambda2, 由于每个同步调用的耗时不相同, Lambda1 最终要等待最慢的那个调用结束后才能对所有结果进行聚集处理。这就是著名的“长板效应”, Lambda1 多数时候是在无谓的等待当中消耗着你的钱财。
Read More
无论在何处,有多重任务要处理时,并发编程总是要得到考虑的。比如有 IO 等待时的并发或 CPU 密集型时的并行计算,并发通常是指在同一个 CPU 上按时间片轮换执行,并行是任务在不同的 CPU 上执行。能有效使用 CPU 多核的语言可以让线程运行在不同的核上实现并行,如果是启动的子进程能由操作系统运行在其他 CPU 核上。
回到 AWS Lambda 中的 Python 代码,如果是处理 IO 等待,使用多线程并发就行,大致的代码如下:with ThreadPoolExecutor(10) as executor:
以上代码在 AWS Lambda 中是可以运行的。
result = executor.map(task_function, task_inputs)
如果是 CPU 密集型的任务,用 Python 的多线程就要歇菜了,因为存在著名的 Python's GIL 的约束。 这时候就必须要考虑多进程并行的方式,同时应知晓当前选择的 Lambda 运行环境有多少个 CPU 内核,因为如果是单核的话再多进程也无济于事,没必要启动多于核心数的进程。 底下是本人上篇博客测试收集的不同 AWS Lambda 内存选择对应的 CPU 核心数,以及实际可用内存大小的关系表 Read More
有了 FastAPI 之后,用 Python 实现 API 或 Web 都不再考虑 Flask 了。Flask 最早在 13 年前的 2010 年 4 月 1 日发布,实现的是 WSGI; FastAPI 较为年轻,于 4 年前的 2018 年 12 月 5 日发布,支持 ASGI。性能方面普遍是 FastAPI 比 Flask 高,编程方面就各取所好吧,使用 Flask 的时候还是 1.x 的版本,最近用 FastAPI 较多,所以无法对比。
FastAPI 本身没提供启动 Web 服务的代码,不像 Flask 还能通过 Flask 对象 或 flask 命令启动一个开发用途的 Web 服务,而 FastAPI 必须用其他的组件(ASGI server)来启动,比如各种 *corn 或 Daphne- Uvicorn: FastAPI 官方的出品,默认启用访问日志,相关的参数有
--log-config <path>,--access-log/--no-access-log启用或关闭访问日志,默认是开启的。 - Hypercorn: 相关的配置选项有
--access-logformat,--access-logfile - Hypercorn: 它只是实现了 WSGI 规格的服务,所以不兼容 FastAPI, 只能作为 Uvicorn 进程的管理器。Gunicorn 作为 WSGI 服务器有丰富的访问日志配置,但访问日志仍然是由 Uvicorn 输出
Read More- Uvicorn: FastAPI 官方的出品,默认启用访问日志,相关的参数有
前两年用 AWS Lambda 搭配 API Gateway 使用是为了省钱,因为没有请求时不花钱。又由于是 Rest API, 所以实现部分用了 FastAPI 的装饰器,但不实际启动 FastAPI 的 Web 服务,Lambda 的 handler 方法根据 routeKey 手动映射到 FastAPI 的装饰方法。大概实现是def lambda_handler(event: dict, context):
当时也思考着能不能把 Lambda 的请求与 FastAPI 的 Web 服务桥接起来,却又不能真正启动一个 Web 服务,否则 Lambda 调用不能结束。比如说 AWS Lambda 收到请求时快速启动 FastAPI 服务,该服务绑定到 TCP 端口或 Socket 文件都行,然后 Lambda 请求代理到 FastAPI 服务,最后关闭 FastAPI 服务,但是想来都不那么容易实现。 Read More
fastapi_function = locate_fastapi_function(event['routeKey'])
return fastapi_function(<extract parameters from event>)
AWS SNS(Simple Notification Service) 以消息订阅,推送的方式对组件进行解藕。当有新消息发送到 SNS 主题中,SNS 会向当前所有的订阅者发送一个消息(广播),它本身不像 SQS 那样会存储消息,而只是一个纯粹的消息路由。SNS 消息可以订阅到 Amazon Kinesis Data Firehose, SQS, Lambda, Email, Email-JSON, HTTP, HTTPs, Platform application endpoint, 和 SMS。同邮件列表一样,订阅 SNS 消息也是需要确认的,不然 SNS 消息就可能恶意满天飞。
本文试验如何用 HTTP 端点订阅 SNS 消息,订阅确认,以及发送消息到 SNS 主题后消息推送到 HTTP 端点的细节,重点是了解订阅及被推送过来消息时的 HTTP 报文内容。SNS 的 HTTP 端点订阅需要一个公网上的 HTTP URL, 对 SNS 可见,所以我在本地测试时在家中路由器上加一个端口映射,对 Modem 获得的公有 IP 的 8080 端口访问转发到写此文用所用机器的 8080 端口上。
在本机需要在 8080 端口上启动一个 HTTP 服务以迎接 AWS 消息的到来,比如用 python 3 的话,简单运行命令python -m http.server 8080。如果不想在 API 代码中分析 HTTP 报文数据,只需打开 Wireshark(过滤条件 tcp.port=8080 && http) 抓取 8080 上的 HTTP 数据通信即可。在 API 代码中如何处理 HTTP 请求数据不是本文的重点。 Read More
基于 EC2 的 ECS 服务,要看看容器内的状态,一直以来都是先 SSM(Simple System Manager) 或 SSH 进到 EC2 实例,然后再docker exec -it <container-id> sh, 查看容器的控制台日志则用docker logs <container-id> [--follow]. 但是对使用 Farget 的 ECS 服务就无能为力了,因为找不到 SSM 或 SSH 的主体, 只能通过程序日志来大概了解容器内发生的事了。
Amazon 在 2021-03-15 推出了一个新的特性ECS Exec允许我们直接连接 Fargate 或 EC2 中的容器会话,见 Amazon ECS now allows you to run commands in a container running on Amazon EC2 or AWS Fargate. ECS Exec 支持 Container Agent 版本为 1.50.2 及以上的 ECS Optimized AMI 系列,和 Fargate Platform Version 1.4.0(Linux) 或 1.0.0(Windows) 及以上。
ECS Exec 的实现原理是以往在 EC2 实例上启动的 SSM Agent,也在容器内部启动一份,然后命令aws ecs execute-command直指容器本身。参考本人写过的一篇 AWS Session Manager 管理 EC2 实例,连接过程中唯一的不同就是容器中也运行了一个 SSM Agent, 所以这个容器也就无所谓是在 EC2 实例还是在 Fargate 中。
Read More
CSV 文件是纯文本的,对人阅读和编辑来说是最友好的描述表格数据的格式。虽然当前处理大数据时会用到 JSON, avro, parquet 等数据格式,但是在处理平面数据时 CSV 仍然被广泛使用。
S3 Select 能支持 CSV, JSON 和 parquet 格式数据的直接查询。在 AWS s3 控制台选择一个 CSV 文件,从右上的Object actions下拉选项上选择Query with S3 Select就能直接查询该文件的内容,而无须下载后打开文件。
如 S3 Select 查询语句SELECT * from s3object WHERE Name='Tom' LIMIT 5
如果 CSV 带 Header 的话,请勾选上Exclude the first line of CSV data。当然 S3 Select 查看任意的文本文件也行,只是把它当成一个不规则的 CSV 文件来对待。
S3 Select 只能针对单个 S3 文件查询,如果要对一组 CSV 文件同时进行查询的话就要用到 Athena。把相同 Schema 的一系列 CSV 文件放到 S3 的某一个目录中,我们可为它们创建一个 Athena 表,然后查询该 Athena 表就会从对应 S3 目录中扫描所有的 CSV 文件。 Read More
最近注意到一个很有意思的项目 Diagrams, 用 Python 代码来绘制架构或流程图, 以前基本用 Gliffy 来画。继一系列 X as X, 如 PaaS, SaaS, IaaS, CaC(Configuration as Code), IaC(Infrastructure as Code) 等,Diagrams 喊出了 Diagram as Code 的口号。其实,在这之前, Markdown 就做了许多 Diagram as Code 的事情, 也许更准确说是 Diagram as Document。
熟练的程序员大概不喜欢用可视化设计器来生成 GUI 代码,那会让代码变得极不简洁,而是直接写,眼中看到的是代码,头脑中即时产生映像。
Diagrams 就是这样一款写 Python 代码产生架构或流程图的库,它绘制的架构图支持主要的云服务提供商,如- 知名的(本人认为的): AWS, Azure, GCP, IBM, DigitalOcean, AlibabaCloud, OCI(Oracle Cloud Infrastructure, 不是 Open Container Initiative), OpenStack
- 刚了解到的: Google 的 Firebase, Elastic(ElasticSearch 出品方也有自己的平台), Outscale
- 以及应用平台 K8S, Saas 和 OnPrem 中的元素
- 通用元素,编程语言及流程图,还能定制自己的节点图
在 Spring Web Controller 方法中的参数可用 org.springframework.web.bind.annotation 下的各种注解来说明参数值从哪儿获得,比如我们熟知的 @PathVariable, @RequestParam, @RequestHeader, @RequestBody, 还有较少使用的 @ReqeustAttribute, @SessionAttribute, @RequestPart, @MatrixVariable, @ModelAttribute, @AuthenticationPrincipal, @CurrentSecurityContext 等。其实在它们背后工作的是相应的 HandlerMethodArgumentResolver 的子孙们,当然还有 HttpMessageConverter 的各个实现类还默默的对输入数据进入类型转换。
为进一步深入了解 Spring Web 如何获得用户输入,我们先尝试一下不常用的注解,然后实现一个自己的注解参数 @ProductId, 它来从 queryString 或 requestHeader 中获得 productId。写作本文的起因是在上一篇 理解 Spring Boot Security + JWT Token 的简单应用 里, JwtTokenFilter 住 SecurityContextFilter 放一个 Authentication 实例, 在 Controller 方法中便能用 @AuthenticationPrincipal 自动注入 authentication.getPrincipal() 的值。 Read More
Python 3.10 于 2021-10-04 发布,至今已大半年,目前 AWS 的 Lambda 尚未直接支持,但用 Docker 镜像的方式使用 AWS Lambda 是可以使用 Python 3.10。Python 一年一发布的节奏比 Java LTS 还紧密。下一个版本 Python 3.11 预计在 2022-10-03 发布。在学习 Python 3.10 之前先回顾一下 Python 3.7, 3.8, 3.9 的特性(不想关心之前版本的变迁可直接跳跃到下方的 Python 3.10 新特性去)
Python 3.7 所带来的新特性- breakpoint()
- 数据类(@dataclass)
- 类型提示强化和延迟注解求值
- 时间精度的提高
- 保证字典的顺序
- async 和 await 成为关键字
- asyncio.run() 简化事件循环
- 上下文变量(ContextVar) - 可实现 ThreadLocal 和 SLF4J 的 MDC 功能