作者|高远
编辑|江柳
Serverless 是一种新兴的无服务器架构,使用它,开发者只需专注于代码,无需关心运维、资源交付或者部署。本文将从代码的角度,通过改造一个 Python 应用来帮助读者从侧面理解 Serverless,让应用继承 Serverless 架构的优点。
现有资源
一个成熟的基因对比算法(Python 实现,运行一次的时间花费为 2 秒)
2020 个基因样本文件(每个文件的大小为 2M,可以直接作为算法的输入)
一台 8 核心云主机
基因检测服务
使用上面的资源来对比两个人的基因样本并 print 对比结果(如:有直系血缘关系的概率)。
首先,构造目录结构如下:
relations.py 代码如下:
使用方法如下:
流程比较简单,从本地磁盘读取两个代表基因序列的文件,经过算法计算,最后返回结果。
我们接到了如下业务需求
首先收集唾液样本经过专业仪器分析后,然后生成样本文件并上传到我们的主机上,一共 2020 个样本文件,最后我们需要运行上面的算法
才可完成需求,我们计算一下总花费的时间:
串行需要花费 22 小时才能算完,太慢了,不过我们的机器是 8 核心的,开 8 个进程一起算:
也要快 3 个小时,还是太慢,假设 8 核算力已经到极限了,接下来如何优化呢?
一种 Serverless 产品:UGC
UGC 允许将计算密集型算法封装为 Docker Image (后文统称为「算法镜像」),只需将算法镜像 push 到指定的算法仓库中,UGC 会将算法镜像预先 pull 到一部分计算节点上,当你使用以下两种形式:
特别构造的 HTTP 请求发送到 UGC 的 API 服务时,「任务调度器」会帮你挑选已经 pull 成功算法镜像的节点,并将请求调度过去,然后启动此算法镜像「容器」将此请求的 HTTP body 以标准输入 stdin 的形式传到容器中,经过算法计算,再把算法的标准输出 stdout 和标准错误 stderr 打成一个 tar 包,以 HTTP body 的形式返回给你,你只需要把返回的 body 当做 tar 包来解压即可得到本次算法运行的结果。
讲了这么多,这个产品使你可以把密集的计算放到了数万的计算节点上,而不是我们小小的 8 核心机器,有数万核心可供使用,那么如何使用如此海量的计算资源呢,程序需要小小的改造一下。
针对此 Serverless 架构的改造
改造分为两部分:
改造算法中元数据从「文件输入」改为「标准输入」,输出改为「标准输出」
开发客户端构造 HTTP 请求,并提高并发
改造算法输入输出
改造输入为 stdin
这样把内容通过管道交给 relation.py 的 stdin,然后在 relation.py 中通过以下方式拿到:
将算法的输出数据写入 stdout
到此就改造完了,很快吧。
客户端与并发
刚才我们改造了算法镜像的逻辑(任务的执行),现在我们来看一下任务的提交:构造 HTTP 请求并读取返回结果。
它也支持异步请求。之前提到,此 Serverless 产品会将算法的标准输出打成 tar 包放到 HTTP body 中返回给客户端,所以我们准备此解包函数:
解开 tar 包,并将结果写入 result.txt 文件。
假设我们 2200 个样本文件的绝对路径列表可以通过 get_sample_list 方法拿到
计算 2000 个样本与 20 个样本的笛卡尔积,我们可以直接使用 itertools.product
结合上面的代码段,我们封装一个方法:
因为构造 HTTP 请求提交是 I/O 密集型而非计算密集型,所以我们使用协程池处理是非常高效的:
只是提交任务 200 并发很轻松。
全部改造完成,我们来简单分析一下:
之前是 8 个进程跑计算密集型算法,现在我们把计算密集型算法放到了 Serverless 产品中,因为客户端是 I/O 密集型的,单机使用协程可以开很高的并发,我们不贪心,按 200 并发来算:
进阶阅读:上面这种情况下带宽反而有可能成为瓶颈,我们可以使用 gzip 来压缩 HTTP body,这是一个计算比较密集的操作,为了 8 核心算力的高效利用,可以将样本数据分为 8 份,启动 8 个进程,进程中再使用协程去提交任务就好了。
也就是说进行一组检测只需要 400 秒,从之前的 7 天提高到 400 秒,成果斐然,图表更直观:
而且算力瓶颈还远未达到,任务提交的并发数还可以提升,再给我们一台机器提交任务,便可以缩短到 200 秒,4 台 100 秒,8 台 50 秒...
最重要的是改造后的架构还继承了 Serverless 架构的优点:
免运维:因为你没有服务器了...
高可用:Serverless 服务一般依托云计算的强大基础设施,任何模块都不会只有单点,都尽可能做到跨可用区,或者跨交换机容灾,而且本次使用的服务有一个有趣的机制:同一个任务,你提交一次,会被多个节点执行,如果一个计算节点挂了,其他节点还可以正常返回,哪个先执行完,先返回哪个。
按需付费:文中说每个算法执行一次花费单核心 CPU 时间 2 秒,我们直接算一下花费
每核时 0.09 元(即单核心 CPU 时间 1 小时,计费 9 分钱)。
发布简单:因为使用 Docker 作为载体,所以它是语言无关的,而且发布也很快,代码写好直接上传镜像就好了,至于灰度,客户端 imageName 指定不同版本即可区分不同代码了。
细说云计算
领取专属 10元无门槛券
私享最新 技术干货