NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

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:051865

评论

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

在 Windows 平台下安装与配置 MySQL 5.7.36

小魏写代码

大模型开发:从数据挖掘到智能应用

百度开发者中心

人工智能 大数据 大模型

云API错误码的设计规则

EquatorCoco

Python 数据库 API

区块链游戏解说:什么是 SecondLive

Footprint Analytics

区块链 加密货币 代币 NFT

聚道云软件连接器:打通金蝶云星空与招商银行CBS,提升企业财务和银行业务效率

聚道云软件连接器

案例分享

flutter+go构建的即时通讯app,ChatCraft

编程的平行世界

golang #开源项目 fluter

VUE3子表格嵌套分页查询互相干扰的问题解决

EquatorCoco

前端 前端开发 Vue3

人工智能:测试开发的新宠儿

测试人

软件测试

一次编写,多次利用:提高 API 构建效率的技巧

Apifox

前端 后端 开发工具 Apifox API

小程序技术实践:如何快速开发适配鸿蒙的App

Geek_2305a8

2024谷歌SEO入门指南

九凌网络

新来的一个同事,把SpringBoot参数校验玩的那叫一个优雅

快乐非自愿限量之名

Java 前端 springboot

秒级响应,显著增效:明日控股携手奇点云,打造大宗贸易的数据中台标杆

奇点云

数据中台 数据资产 奇点云 明日控股

鸿蒙5.0发布时间已定!移动开发加速器往何处寻找?

Geek_2305a8

【大模型】快速体验百度智能云千帆AppBuilder搭建知识库与小助手

阿Q说代码

知识库 千帆大模型平台 AppBuilder 小助手

NFTScan 与 OneID 达成合作伙伴,支持多类型 DID 搜索!

NFT Research

NFT NFTScan

什么是DDOS流量攻击,DDoS防护安全方案

德迅云安全杨德俊

传统开发与低代码/无代码开发的区别

这我可不懂

软件开发 低代码 JNPF

左耳听风 - 关于招聘「读书打卡 day 20」

Java 工程师蔡姬

读书笔记 程序员 个人成长 招聘 职业发展

SpringBoot实现动态数据源配置

不在线第一只蜗牛

Java 后端 springboot

iOS App审核状态和审核时间管理指南

基于大模型的低代码平台架构

百度开发者中心

人工智能 低代码 大模型

软件测试学习笔记丨JMeter_实现分组并发

测试人

软件测试

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