QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

Serverless 架构下还需要评估函数资源吗?

  • 2020-08-09
  • 本文字数:5664 字

    阅读完需:约 19 分钟

Serverless架构下还需要评估函数资源吗?

Serverless 布道师在讲述 Serverless 架构和云主机区别的时候,常会有这样的描述:


传统业务开发想要上线,需要先评估资源使用,并根据资源评估结果购买云主机,之后还要根据业务发展不断对主机等资源进行升级维护。而 Serverless 架构不需要这样复杂的流程,将函数部署到线上后,一切后端服务交给运营商来处理,哪怕是瞬时高并发,也有云厂商来自动扩缩。


但在实际生产生活中,Serverless 真的可以做到无需对资源评估吗?还是说在 Serverless 架构下,资源评估的内容或对象发生了变化,或者进行了简化?

探索 Serverless 下的资源评估

以国内某云厂商为例,在其云函数中,我们创建一个云函数之后,设置页面会出现可设置的选项:



这两个设置范围分别是从 64M-1536M 和 1-900S,这样的配置其实就涉及到资源评估了。


首先是超时时间,一个项目、函数或 Action 都有执行时间,如果超过某个时间没执行完就可以评估其为发生了“意外”,可以被“干掉“了,这个就是超时时间。例如一个获取用户信息的请求,在 10S 内没有返回,证明其不能满足业务需求,那么,我们就可以把超时设置为 10S。当一个运行速度很慢的业务,至少要 50S 才能执行完,那么这个值设置的时候就要大于 50,否则程序可能因为超时被强行停止。


然后是内存,内存是一个有趣的东西,可能衍生两个关联点。


关联点 1: 程序本身需要一定的内存,这个内存要大于程序本身的内存,以 Python 语言为例:


# -*- coding: utf8 -*-import jieba

def main_handler(event, context): jieba.load_userdict("./jieba/dict.txt") seg_list = jieba.cut("我来到北京清华大学", cut_all=True) print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=False) print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他来到了网易杭研大厦") # 默认是精确模式 print(", ".join(seg_list))
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造") # 搜索引擎模式 print(", ".join(seg_list))

复制代码


对程序代码的说明:为了让结果更加直观,差距更加大,所以这里每次都重新导入了自带 dict,这个操作本身就是相对浪费时间和内存的。在实际使用中 jieba 自带缓存,并且无需手动导入本身的 dict


当导入一个自定义的 dict 到 jieba 中,如果此时函数内存设置的默认 128M 内存限制+3S 超时限制就会这样:



此时可以看到,由于在导入自定义 dict 时内存消耗过大, 默认的 128 不足以满足需求,所以需要将其修改成最大:



这时又再次提醒超时,还需要修改超时时间为适当的数值(此处设定为 10S):



总而言之,在关注程序本身的前提下,要将内存设置在一个合理范围内,这个范围是>=程序本身需要的内存数值。


关联点 2: 计费相关,在云函数的文档中,我们可以看到:


云函数 SCF 按照实际使用付费,采用后付费小时结,以 为单位进行结算。

SCF 账单由以下三部分组成,每部分根据自身统计结果和计算方式进行费用计算,结果以 为单位,并保留小数点后两位。

资源使用费用

调用次数费用

外网出流量费用


调用次数和出网流量都是与程序或者使用量相关的,无需格外关注,但在资源使用费用有一些注意点:


资源使用量 = 函数配置内存 × 运行时长

用户资源使用量,由函数配置内存,乘以函数运行时的计费时长得出。其中配置内存转换为 GB

单位,计费时长由毫秒(ms)转换为秒(s)单位,因此,资源使用量的计算单位为 GBs (GB-秒)。

例如,配置为 256MB 的函数,单次运行了 1760 ms,计费时长为 1760

ms,则单次运行的资源使用量为(256/1024)×(1760/1000) = 0.44 GBs。

针对函数的每次运行,均会计算资源使用量,并按小时汇总求和,作为该小时的资源使用量。


这里有一个非常重要的公式,那就是函数配置内存*运行时长。函数配置内存就是我们为程序选择的内存大小,运行时长就是运行程序之后得到的结果:



以这个程序为例,使用的是 1536MB,则使用量为(1536/1024) * (3200/1000) = 4.8GBs


当然,如果此时是 250MB,那程序也可以运行:



此时的资源使用量为(256/1024) * (3400/1000) = 0.85GBs


对比上一次,程序执行时间增加了 0.2S,但是资源使用量降低了将近 6 倍!


产品单价是:



虽然 GBs 的单价很低,但是业务量上来之后,这个数字也是要值得注意的。刚才的只是一个单次请求,如果每天有 1000 单次请求,那费用就会出现差距(仅计算资源使用量费用,而不计算调用次数/外网流量):


1536MB: 4.810000.00011108 = 0.5 元


256MB:0.8510000.00011108 = 0.09442 元


如果不是 1000 次调用,而是 10 万次调用,则就是 50 元和 9 元的区别,随着流量越大,差距越大。


当然,多数情况函数执行时间不会这么久,以下面函数为例:



计费时间均是 100ms,每日调用量在 6000 次左右:



如果按照 64M 内存来计算,单资源费用只要 76 元一年,而如果内存都设置为 1536,则一年要 1824 元!这个费用相当于:



所以,超时时间需要对代码和业务场景进行评估来进行设置,可能关系到程序运行的稳定和功能的完整性;内存则不仅仅在程序使用层面有着不同的需求,在费用成本等方面也占有极大的比重,所以内存设置需要对程序进行一个评估。那么问题来了,内存设置多大比较划算?同样是之前的代码,在本地进行简单的脚本编写:


from tencentcloud.common import credentialfrom tencentcloud.common.profile.client_profile import ClientProfilefrom tencentcloud.common.profile.http_profile import HttpProfilefrom tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKExceptionfrom tencentcloud.scf.v20180416 import scf_client, models
import jsonimport numpyimport matplotlib.pyplot as plt
try: cred = credential.Credential("", "") httpProfile = HttpProfile() httpProfile.endpoint = "scf.tencentcloudapi.com"
clientProfile = ClientProfile() clientProfile.httpProfile = httpProfile client = scf_client.ScfClient(cred, "ap-shanghai", clientProfile)
req = models.InvokeRequest() params = '{"FunctionName":"hello_world_2"}' req.from_json_string(params)
billTimeList = [] timeList = [] for i in range(1, 50): print("times: ", i) resp = json.loads(client.Invoke(req).to_json_string()) billTimeList.append(resp['Result']['BillDuration']) timeList.append(resp['Result']['Duration'])
print("计费最大时间", int(max(billTimeList))) print("计费最小时间", int(min(billTimeList))) print("计费平均时间", int(numpy.mean(billTimeList)))
print("运行最大时间", int(max(timeList))) print("运行最小时间", int(min(timeList))) print("运行平均时间", int(numpy.mean(timeList)))
plt.figure() plt.subplot(4, 1, 1) x_data = range(0, len(billTimeList)) plt.plot(x_data, billTimeList) plt.subplot(4, 1, 2) plt.hist(billTimeList, bins=20) plt.subplot(4, 1, 3) x_data = range(0, len(timeList)) plt.plot(x_data, timeList) plt.subplot(4, 1, 4) plt.hist(timeList, bins=20) plt.show()
except TencentCloudSDKException as err: print(err)
复制代码


运行之后会输出一个简单的图像:



从上到下分别是不同次数计费时间图、计费时间分布图以及不同次数运行时间图和运行时间分布图。256M 起步,1536M 终止,步长 128M,每个内存大小串行靠用 50 次,统计表:



注:为了让统计结果更加清晰,差异性比较明显,在程序代码中进行了部分无用操作用来增加程序执行时间。正常使用 jieba 的速度基本都是毫秒级的:



通过表统计可以看到在满足程序内存消耗的前提下,内存大小对程序执行时间的影响并不是很大,反而是对计费影响很大。


当然上面是两个重要指标,除此之外还有一个参数需要用户来评估:函数并发量,在项目上线之后,需要对项目可能产生的并发量进行评估,当评估的并发量超过默认的并发量,要及时联系售后同学或者提交工单进行最大并发量数值的提升。


除了上面的简单评估,有兴趣的同学也可以进行多进程/多线程与函数执行时间/内存关系的评估,但是考虑到很多时候云函数的业务都不涉及到多进程/多线程,所以这里不仅行单独测试。

应该如何设置函数的内存与超时时间

上一部分主要说了 Serverless 架构与资源评估:性能与成本探索​。探索之后,就不得不引出一个新的问题:在使用 Serverless 架构时如何来设置运行内存和超时时间呢?


评估方法有很多,我的做法是先将函数上线,选择一个稍大的内存执行一次。



得到上图结果,再函数设置为 128M 或者 256M,超时时间设置成 3S,接着运行一段时间,例如接口每天触发次数大约为 4000+次:



将函数日志写成脚本,重新做统计:


import json, time, numpy, base64import matplotlib.pyplot as pltfrom matplotlib import font_managerfrom tencentcloud.common import credentialfrom tencentcloud.common.profile.client_profile import ClientProfilefrom tencentcloud.common.profile.http_profile import HttpProfilefrom tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKExceptionfrom tencentcloud.scf.v20180416 import scf_client, models
secretId = ""secretKey = ""region = "ap-guangzhou"namespace = "default"functionName = "course"
font = font_manager.FontProperties(fname="./01.ttf")
try: cred = credential.Credential(secretId, secretKey) httpProfile = HttpProfile() httpProfile.endpoint = "scf.tencentcloudapi.com"
clientProfile = ClientProfile() clientProfile.httpProfile = httpProfile client = scf_client.ScfClient(cred, region, clientProfile)
req = models.GetFunctionLogsRequest()
strTimeNow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time()))) strTimeLast = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time()) - 86400)) params = { "FunctionName": functionName, "Limit": 500, "StartTime": strTimeLast, "EndTime": strTimeNow, "Namespace": namespace } req.from_json_string(json.dumps(params))
resp = client.GetFunctionLogs(req)
durationList = [] memUsageList = []
for eveItem in json.loads(resp.to_json_string())["Data"]: durationList.append(eveItem['Duration']) memUsageList.append(eveItem['MemUsage'] / 1024 / 1024)
durationDict = { "min": min(durationList), # 运行最小时间 "max": max(durationList), # 运行最大时间 "mean": numpy.mean(durationList) # 运行平均时间 } memUsageDict = { "min": min(memUsageList), # 内存最小使用 "max": max(memUsageList), # 内存最大使用 "mean": numpy.mean(memUsageList) # 内存平均使用 }
plt.figure(figsize=(10, 15)) plt.subplot(4, 1, 1) plt.title('运行次数与运行时间图', fontproperties=font) x_data = range(0, len(durationList)) plt.plot(x_data, durationList) plt.subplot(4, 1, 2) plt.title('运行时间直方分布图', fontproperties=font) plt.hist(durationList, bins=20) plt.subplot(4, 1, 3) plt.title('运行次数与内存使用图', fontproperties=font) x_data = range(0, len(memUsageList)) plt.plot(x_data, memUsageList) plt.subplot(4, 1, 4) plt.title('内存使用直方分布图', fontproperties=font) plt.hist(memUsageList, bins=20)
# with open("/tmp/result.png", "rb") as f: # base64_data = base64.b64encode(f.read())
print("-" * 10 + "运行时间相关数据" + "-" * 10) print("运行最小时间:\t", durationDict["min"], "ms") print("运行最大时间:\t", durationDict["max"], "ms") print("运行平均时间:\t", durationDict["mean"], "ms")
print("\n")
print("-" * 10 + "内存使用相关数据" + "-" * 10) print("内存最小使用:\t", memUsageDict["min"], "MB") print("内存最大使用:\t", memUsageDict["max"], "MB") print("内存平均使用:\t", memUsageDict["mean"], "MB")
print("\n")
plt.show(dpi=200)


except TencentCloudSDKException as err: print(err)
复制代码


运行结果:


----------运行时间相关数据----------运行最小时间:   1 ms运行最大时间:   291 ms运行平均时间:   63.45 ms

----------内存使用相关数据----------内存最小使用: 21.20703125 MB内存最大使用: 29.66015625 MB内存平均使用: 24.8478125 MB
复制代码



通过上图可以看出,近 500 次,每次函数的时间消耗和内存使用。时间消耗基本在 1S 以下,所以此处超时时间设置成 1S 是合理的,而内存使用基本是 64M 以下,所以此时内存设置成 64M 就可以。当然,通过这个图可以看出云函数在执行时可能会有一定的波动,所以无论是内存使用还是超时时间,都可能会出现一定的波动,可以根据自身的业务需求来做一些舍弃,将资源使用量压到最低,节约成本。

总结

综上所述,Serverless 架构也是需要资源评估的,而且资源评估同样是和成本直接挂钩的,只不过这个资源评估的对象逐渐发生了变化,相对之前的评估维度、难度而言,都是大幅度缩小或者降低的。


上线函数之前,进行资源评估的做法基本是分为两步走:


  • 简单运行两次,评估一下基础资源使用量,然后设置一个稍微偏高的值;

  • 函数运行一段时间,得到一定的样本值,在进行数据可视化和基本的数据分析,得到一个相对稳定权威的数据;


2020-08-09 22:052171

评论

发布
暂无评论
发现更多内容

springboot整合activiti自带在线流程编辑器,整个程序源码,请假审批

金陵老街

大前端 工作流 ERP Activiti spring-boot 2.5.4

如何短时间突击 Java 通过面试?

Java架构师迁哥

PyFlink 开发环境利器:Zeppelin Notebook

阿里云大数据AI技术

为什么Kafka的数据不写了?

BUG侦探

DNS gopack Linux操作系统

英特尔的碳中和版图

科技新消息

新思科技软件组成分析解决方案获得Forrester Wave认可

InfoQ_434670063458

新斯科技 Forrester 软件组成分析

安卓工控主板通信接口有哪些呢?

双赞工控

安卓主板 工控主板

LeetCode刷题14-简单-最长公共前缀

ベ布小禅

8月日更

谈谈Go的固定时长定时器和周期性时长定时器

Regan Yue

协程 Go 语言 8月日更

如何搭建SpringcloudAlibaba基础环境(一)

程序员半支烟

Java 微服务

关于数据库应用的一些思考

石云升

数据库 8月日更

网络攻防学习笔记 Day116

穿过生命散发芬芳

网络安全 8月日更

filecoin挖矿教程?filecoin挖矿收益如何?

区块链 分布式存储 IPFS filecoin挖矿 filecoin收益

如何用3分钟搭建一个属于自己的网站?

百度开发者中心

最佳实践 开发者 方法论

服务全球用户,EMQ X Cloud 新增欧洲部署支持

EMQ映云科技

服务器 mqtt 部署与维护 Cloud emq

如何认知新技术?区块链技术和应用

百度开发者中心

区块链 最佳实践 方法论

PyFlink 开发环境利器:Zeppelin Notebook

Apache Flink

Zeppelin notebook pyflink 开发环境利器

SmartNews:基于 Flink 加速 Hive 日表生产的实践

Apache Flink

flink hive airflow

neo4j 安装与基本操控

escray

学习 neo4j 8月日更

如何利用FL Studio编曲--入门级

懒得勤快

深入了解现代web浏览器(第一部分)

GKNick

Filecoin价格今日行情:filecoin价格还能怎么走?

区块链 分布式存储 IPFS filecoin价格 filecoin行情

为联邦学习互信提供“技术解”,星云Clustar荣获FTL-IJCAI 21 最佳应用论文奖

星云Clustar

联邦学习 论文 IJCAI 纵向联邦学习 联邦学习互信

快手基于 Flink 构建实时数仓场景化实践

Apache Flink

flink 解决方案 实时数仓架构 快手

Linux内核内存管理:地址转换和MMU

Linux服务器开发

操作系统 内存管理 Linux内核 内核开发 地址转换

阿里大牛肝出的443页TCP/IP协议趣谈笔记,竟然在GitHub标星27k+

Java 编程 架构 面试 架构师

中国做ipfs公司排名?挖ipfs排名前三的公司是?ipfs矿机哪家最好?

中国做ipfs公司排名 ipfs矿机哪家最好 挖ipfs排名前三的公司是

技术盘点:Unity SDK 开发中有哪些大坑?

环信

【架构实战营】--第一次作业

Geek_d18264

架构实战营

久等了!【Innovation 2021】网易应用创新开发者大赛正式开赛!

网易云信

开源 架构 开发者 网易 语言 & 开发

开发者实践丨盲水印插件:用户端的实时视频溯源保护

声网

开发者实践 RTE大赛

Serverless架构下还需要评估函数资源吗?_服务革新_刘宇_InfoQ精选文章