Serverless: 2020年函数计算的冷启动怎么样了

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自从Serverless架构被提出,函数计算这个名词变得越发的火热,甚至在很多时候有人会认为Serverless就是函数计算。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作为Serverless架构中的一个重要组成部分,云函数确实值得,也应该备受关注,无论是吐槽他的调试能力,还是抱怨他的冷启动,亦或者对他的弹性伸缩表示怀疑,但是我们不得不承认,更多人正在越来越关注Serverless,也越来越关注Serverless中的FaaS部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"经常看到有人在吐槽函数计算的冷启动问题,时至今日,不知道各平台的冷启动是什么样子的。本文将会通过相对客观的数据来进行基本的验证。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"冷启动验证"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先说到冷启动,就要先说明什么是冷启动,开发者提交代码之后你不知道他调不调用,函数第一次调用会有一个函数冷启动,把网络的环境全部打通,这个函数才能提供服务。如果没有优化好冷启动优化这部分,可能对于一些比较关键的产品首次启动会产生超时,体验非常不好,以前开发者本地运行函数的时候,并不会关注本地函数执行多少毫秒和微妙,但是在云函数场景下就不一样了,云函数有一个部署的过程;无论是公有云的平台上还是开源方案上,冷启动都是值得不断探讨话题和优化的方向。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cd/cdf94f429ebf4c8fe60494899335a838.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在《Serverless: Cold Start War》这篇文章中,作者对AWS Lambda,Azure Function以及Google Cloud Function等三个工业级的Serverless架构产品的冷启动测试。作者将函数启动划分成四个部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ad/ad3c73b4fc23f310ce705d31ebac08f4.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后作者通过对多种语言的“Hello World”与是否有依赖等进行搭配,进行测试,测试结果:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/79/796121f9312b0a383274a0915b5ef383.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过《Understanding AWS Lambda Performance—How Much Do Cold Starts Really Matter?》与《Serverless: Cold Start War》这两个文章的分析和结果,我们可以看到冷启动问题确实存在,而且不同厂商,不同语言,不同测试方法得到的冷启动数据都是有所差异的。这也充分说明,各个厂商也在通过一些规则和策略努力降低冷启动率。除此之外文章《Understanding Serverless Cold Start》、《Everything you need to know about cold starts in AWS Lambda》、《Keeping Functions Warm》、《I'm afraid you're thinking about AWS Lambda cold starts all wrong》等也均对冷启动现象等进行描述和深入的探讨,并且提出了一些业务侧应对函数冷启动的解决方案和策略。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再说回来,时至今日,主流云厂商都已经开始开发探索Serverless架构,那么各个云厂商的冷启动已经\"热\"到了什么程度?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于我是国内的开发者,所以我将测试分为两部分,一部分是国内云厂商(腾讯云、阿里云、华为云),另一部分是国外云厂商(AWS、谷歌)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于在实际生产过程中,冷启动的诞生往往是和API网关共同体现,也就是说,实际上让用户感知相对明显,或者比较常见感知到冷启动出现的情况,通常是函数与网关结合,做了一个接口/服务,访问该服务的时候,放大/体现了冷启动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,我的做法很简单和暴力,通过函数与API网关触发器的结合,针对不同厂商来创建一个API服务,通过本地机器来对该服务进行访问,在客户端判断其耗时。当然,这种做法诚然不能精确的表现出冷启动的具体数据,因为网络因素也将会是影响其准确性的一个重要因素,但是至少可以大概的对比出,不同云厂商的冷启动优化情况,以及服务稳定情况。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"国内的云厂商,在测试的时候更多使用的是国内区,国外云厂商则是国外区,这样就会出现一个额外的问题,国内外云厂商的数据不具有对比性,毕竟网络因素占了很大的一部分,所以本次对比将会是国内和国内对比,国外和国外对比。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客户端程序:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"import time, json\nimport urllib.request\nimport matplotlib.pyplot as plt\nimport numpy\nfrom multiprocessing import Process, Manager\n\n\n# 测试地址\n# qcloud\nurl = \"https://service-px5f98f4-1256773370.gz.apigw.tencentcs.com/release/scf_demo\"\n# # huaweicloud\n# url = \"https://2937587fe6ce4e6eb22d521d1d9b811c.apig.cn-east-2.huaweicloudapis.com/demo\"\n# # aliyun\n# url = \"https://50155512.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/guide-hello_world/mydemo/\"\n# # aws\n# url = \"https://di6vbxf2lk.execute-api.us-east-1.amazonaws.com/default/mydemo\"\n# # GoogleCloud\n# url = \"https://us-central1-meta-imagery-277209.cloudfunctions.net/mydemo\"\n\n# 此时\ntimes = 200\n\n# 串行处理\nserialColdStart = []\nserialHotStart = []\n\nfor i in range(0,times):\n timeStart = time.time()\n responseAttr = urllib.request.urlopen(url)\n endTime = time.time()\n\n response = json.loads(responseAttr.read().decode(\"utf-8\"))\n\n if response['isNew']:\n serialColdStart.append(endTime-timeStart)\n else:\n serialHotStart.append(endTime-timeStart)\n\n\n# 并行处理\ndef worker(url, return_list):\n timeStart = time.time()\n responseAttr = urllib.request.urlopen(url)\n endTime = time.time()\n return_list.append({\n \"duration\": endTime-timeStart,\n \"response\": json.loads(responseAttr.read().decode(\"utf-8\"))\n })\n\nmanager = Manager()\nreturn_list = manager.list()\njobs = []\nfor i in range(times):\n p = Process(target=worker, args=(url ,return_list))\n jobs.append(p)\n p.start()\n\nfor proc in jobs:\n proc.join()\n\nparallelColdStart = []\nparallelHotStart = []\nfor eveData in return_list:\n if eveData['response']['isNew']:\n parallelColdStart.append(eveData['duration'])\n else:\n parallelHotStart.append(eveData['duration'])\n\n\n# 数据汇总\nprint(\"-\"*10, \"串行测试\", \"-\"*10)\nprint(\"总触发次数:\", len(serialColdStart) + len(serialHotStart))\nprint(\"冷启动次数:\", len(serialColdStart))\nprint(\"热启动次数:\", len(serialHotStart))\nprint(\"最大耗时量:\", max(serialColdStart + serialHotStart))\nprint(\"最小耗时量:\", min(serialColdStart + serialHotStart))\nprint(\"平均耗时量:\", numpy.mean(serialColdStart + serialHotStart))\n\nprint(\"-\"*10, \"并行测试\", \"-\"*10)\nprint(\"总触发次数:\", len(parallelColdStart) + len(parallelHotStart))\nprint(\"冷启动次数:\", len(parallelColdStart))\nprint(\"热启动次数:\", len(parallelHotStart))\nprint(\"最大耗时量:\", max(parallelColdStart + parallelHotStart))\nprint(\"最小耗时量:\", min(parallelColdStart + parallelHotStart))\nprint(\"平均耗时量:\", numpy.mean(parallelColdStart + parallelHotStart))\n\nplt.figure(figsize=(15,10))\nplt.subplot(4, 2, 1)\nplt.title('(Serial) Cold Start Time')\nplt.plot(range(0, len(serialColdStart)), serialColdStart)\nplt.subplot(4, 2, 3)\nplt.title('(Serial) Cold Start Time')\nplt.hist(serialColdStart, bins=20)\nplt.subplot(4, 2, 5)\nplt.title('(Serial) Hot Start Time')\nplt.plot(range(0, len(serialHotStart)), serialHotStart)\nplt.subplot(4, 2, 7)\nplt.title('(Serial) Hot Start Time')\nplt.hist(serialHotStart, bins=20)\nplt.subplot(4, 2, 2)\nplt.title('(Parallel) Hot Start Time')\nplt.plot(range(0, len(parallelColdStart)), parallelColdStart)\nplt.subplot(4, 2, 4)\nplt.title('(Parallel) Cold Start Time')\nplt.hist(parallelColdStart, bins=20)\nplt.subplot(4, 2, 6)\nplt.title('(Parallel) Hot Start Time')\nplt.plot(range(0, len(parallelHotStart)), parallelHotStart)\nplt.subplot(4, 2, 8)\nplt.title('(Parallel) Hot Start Time')\nplt.hist(parallelHotStart, bins=20)\nplt.show()import time, json\nimport urllib.request\nimport matplotlib.pyplot as plt\nimport numpy\nfrom multiprocessing import Process, Manager\n\n\n# 测试地址\n# qcloud\nurl = \"https://service-px5f98f4-1256773370.gz.apigw.tencentcs.com/release/scf_demo\"\n# # huaweicloud\n# url = \"https://2937587fe6ce4e6eb22d521d1d9b811c.apig.cn-east-2.huaweicloudapis.com/demo\"\n# # aliyun\n# url = \"https://50155512.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/guide-hello_world/mydemo/\"\n# # aws\n# url = \"https://di6vbxf2lk.execute-api.us-east-1.amazonaws.com/default/mydemo\"\n# # GoogleCloud\n# url = \"https://us-central1-meta-imagery-277209.cloudfunctions.net/mydemo\"\n\n# 此时\ntimes = 200\n\n# 串行处理\nserialColdStart = []\nserialHotStart = []\n\nfor i in range(0,times):\n timeStart = time.time()\n responseAttr = urllib.request.urlopen(url)\n endTime = time.time()\n\n response = json.loads(responseAttr.read().decode(\"utf-8\"))\n\n if response['isNew']:\n serialColdStart.append(endTime-timeStart)\n else:\n serialHotStart.append(endTime-timeStart)\n\n\n# 并行处理\ndef worker(url, return_list):\n timeStart = time.time()\n responseAttr = urllib.request.urlopen(url)\n endTime = time.time()\n return_list.append({\n \"duration\": endTime-timeStart,\n \"response\": json.loads(responseAttr.read().decode(\"utf-8\"))\n })\n\nmanager = Manager()\nreturn_list = manager.list()\njobs = []\nfor i in range(times):\n p = Process(target=worker, args=(url ,return_list))\n jobs.append(p)\n p.start()\n\nfor proc in jobs:\n proc.join()\n\nparallelColdStart = []\nparallelHotStart = []\nfor eveData in return_list:\n if eveData['response']['isNew']:\n parallelColdStart.append(eveData['duration'])\n else:\n parallelHotStart.append(eveData['duration'])\n\n\n# 数据汇总\nprint(\"-\"*10, \"串行测试\", \"-\"*10)\nprint(\"总触发次数:\", len(serialColdStart) + len(serialHotStart))\nprint(\"冷启动次数:\", len(serialColdStart))\nprint(\"热启动次数:\", len(serialHotStart))\nprint(\"最大耗时量:\", max(serialColdStart + serialHotStart))\nprint(\"最小耗时量:\", min(serialColdStart + serialHotStart))\nprint(\"平均耗时量:\", numpy.mean(serialColdStart + serialHotStart))\n\nprint(\"-\"*10, \"并行测试\", \"-\"*10)\nprint(\"总触发次数:\", len(parallelColdStart) + len(parallelHotStart))\nprint(\"冷启动次数:\", len(parallelColdStart))\nprint(\"热启动次数:\", len(parallelHotStart))\nprint(\"最大耗时量:\", max(parallelColdStart + parallelHotStart))\nprint(\"最小耗时量:\", min(parallelColdStart + parallelHotStart))\nprint(\"平均耗时量:\", numpy.mean(parallelColdStart + parallelHotStart))\n\nplt.figure(figsize=(15,10))\nplt.subplot(4, 2, 1)\nplt.title('(Serial) Cold Start Time')\nplt.plot(range(0, len(serialColdStart)), serialColdStart)\nplt.subplot(4, 2, 3)\nplt.title('(Serial) Cold Start Time')\nplt.hist(serialColdStart, bins=20)\nplt.subplot(4, 2, 5)\nplt.title('(Serial) Hot Start Time')\nplt.plot(range(0, len(serialHotStart)), serialHotStart)\nplt.subplot(4, 2, 7)\nplt.title('(Serial) Hot Start Time')\nplt.hist(serialHotStart, bins=20)\nplt.subplot(4, 2, 2)\nplt.title('(Parallel) Hot Start Time')\nplt.plot(range(0, len(parallelColdStart)), parallelColdStart)\nplt.subplot(4, 2, 4)\nplt.title('(Parallel) Cold Start Time')\nplt.hist(parallelColdStart, bins=20)\nplt.subplot(4, 2, 6)\nplt.title('(Parallel) Hot Start Time')\nplt.plot(range(0, len(parallelHotStart)), parallelHotStart)\nplt.subplot(4, 2, 8)\nplt.title('(Parallel) Hot Start Time')\nplt.hist(parallelHotStart, bins=20)\nplt.show()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"国内云厂商"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"腾讯云"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"测试代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# -*- coding: utf8 -*-\nimport json\nimport time\nimport uuid\n\nrequestId = None\ncontaineId = str(uuid.uuid1())\ncreateTime = time.time()\n\ndef main_handler(event, context):\n time.sleep(1)\n tempId = str(uuid.uuid1())\n timeStart = time.time()\n global requestId\n if not requestId:\n requestId = tempId\n\n response = {\n \"isNew\": True,\n \"oldRequestId\": requestId,\n \"newRequestId\": tempId,\n \"duration\": time.time() - timeStart,\n \"containeId\": containeId,\n \"createTime\": createTime\n }\n\n response[\"isNew\"] = True if requestId == tempId else False\n return response\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输出数据:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---------- 串行测试 ----------\n总触发次数: 200\n冷启动次数: 8\n热启动次数: 192\n最大耗时量: 2.3507158756256104\n最小耗时量: 1.0568928718566895\n平均耗时量: 1.134293702840805\n---------- 并行测试 ----------\n总触发次数: 186\n冷启动次数: 170\n热启动次数: 16\n最大耗时量: 14.849930047988892\n最小耗时量: 1.092796802520752\n平均耗时量: 7.125524929774705\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1fe35cabd6ef6591ed2f8db97970811d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"阿里云"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"测试代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# -*- coding: utf8 -*-\nimport json\nimport time\nimport uuid\n\nrequestId = None\ncontaineId = str(uuid.uuid1())\ncreateTime = time.time()\n\nclass AppClass:\n \"\"\"Produce the same output, but using a class\n \"\"\"\n def __init__(self, environ, start_response, response):\n self.environ = environ\n self.start = start_response\n self.response = response\n def __iter__(self):\n status = '200'\n response_headers = [('Content-type', 'text/html;charset=utf-8')]\n self.start(status, response_headers)\n yield self.response.encode(\"utf-8\")\n\ndef handler(environ, start_response):\n time.sleep(1)\n tempId = str(uuid.uuid1())\n timeStart = time.time()\n global requestId\n if not requestId:\n requestId = tempId\n\n response = {\n \"isNew\": True,\n \"oldRequestId\": requestId,\n \"newRequestId\": tempId,\n \"duration\": time.time() - timeStart,\n \"containeId\": containeId,\n \"createTime\": createTime\n }\n\n response[\"isNew\"] = True if requestId == tempId else False\n return AppClass(environ, start_response, json.dumps(response))\n "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输出数据:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---------- 串行测试 ----------\n总触发次数: 200\n冷启动次数: 1\n热启动次数: 199\n最大耗时量: 1.8273499011993408\n最小耗时量: 1.1592700481414795\n平均耗时量: 1.221251163482666\n---------- 并行测试 ----------\n总触发次数: 200\n冷启动次数: 163\n热启动次数: 37\n最大耗时量: 3.184391975402832\n最小耗时量: 1.1983528137207031\n平均耗时量: 2.3849029302597047"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/369ff1ec54a68fc47b17f738f0b1784f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"华为云"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"测试代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# -*- coding: utf8 -*-\nimport json\nimport time\nimport uuid\nrequestId = None\ncontaineId = str(uuid.uuid1())\ncreateTime = time.time()\n\ndef handler(event, context):\n time.sleep(1)\n tempId = str(uuid.uuid1())\n timeStart = time.time()\n global requestId\n if not requestId:\n requestId = tempId\n\n response = {\n \"isNew\": True,\n \"oldRequestId\": requestId,\n \"newRequestId\": tempId,\n \"duration\": time.time() - timeStart,\n \"containeId\": containeId,\n \"createTime\": createTime\n }\n\n response[\"isNew\"] = True if requestId == tempId else False\n return json.dumps({\n 'statusCode': 200,\n 'isBase64Encoded': False,\n 'headers': {\n \"Content-type\": \"text/html; charset=utf-8\"\n },\n 'body': json.dumps(response),\n })\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输出数据:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---------- 串行测试 ----------\n总触发次数: 200\n冷启动次数: 1\n热启动次数: 199\n最大耗时量: 2.4535348415374756\n最小耗时量: 1.202908992767334\n平均耗时量: 1.4574852859973908\n---------- 并行测试 ----------\n总触发次数: 200\n冷启动次数: 72\n热启动次数: 128\n最大耗时量: 3.8169281482696533\n最小耗时量: 1.232532024383545\n平均耗时量: 2.3244904506206514"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d8/d82b8ba31f93342a6b27c28313444896.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"国外云厂商"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"AWS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"测试代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# -*- coding: utf8 -*-\nimport json\nimport time\nimport uuid\n\nrequestId = None\ncontaineId = str(uuid.uuid1())\ncreateTime = time.time()\n\ndef lambda_handler(event, context):\n time.sleep(1)\n tempId = str(uuid.uuid1())\n timeStart = time.time()\n global requestId\n if not requestId:\n requestId = tempId\n\n response = {\n \"isNew\": True,\n \"oldRequestId\": requestId,\n \"newRequestId\": tempId,\n \"duration\": time.time() - timeStart,\n \"containeId\": containeId,\n \"createTime\": createTime\n }\n\n response[\"isNew\"] = True if requestId == tempId else False\n return {\n 'statusCode': 200,\n 'body': json.dumps(response)\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输出数据:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---------- 串行测试 ----------\n总触发次数: 200\n冷启动次数: 1\n热启动次数: 199\n最大耗时量: 6.628237009048462\n最小耗时量: 1.917238712310791\n平均耗时量: 2.1634005284309388\n---------- 并行测试 ----------\n总触发次数: 200\n冷启动次数: 176\n热启动次数: 24\n最大耗时量: 6.071150779724121\n最小耗时量: 1.9705779552459717\n平均耗时量: 2.370948977470398"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c4/c418a6ae370a1f53b97d050b5012f9d1.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Google Cloud"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"测试代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"# -*- coding: utf8 -*-\nimport json\nimport time\nimport uuid\n\nrequestId = None\ncontaineId = str(uuid.uuid1())\ncreateTime = time.time()\n\ndef main_handler(event):\n time.sleep(1)\n tempId = str(uuid.uuid1())\n timeStart = time.time()\n global requestId\n if not requestId:\n requestId = tempId\n\n response = {\n \"isNew\": True,\n \"oldRequestId\": requestId,\n \"newRequestId\": tempId,\n \"duration\": time.time() - timeStart,\n \"containeId\": containeId,\n \"createTime\": createTime\n }\n\n response[\"isNew\"] = True if requestId == tempId else False\n return json.dumps(response)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输出数据:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"---------- 串行测试 ----------\n总触发次数: 200\n冷启动次数: 1\n热启动次数: 199\n最大耗时量: 4.707853078842163\n最小耗时量: 1.226269006729126\n平均耗时量: 1.3416448163986205\n---------- 并行测试 ----------\n总触发次数: 200\n冷启动次数: 198\n热启动次数: 2\n最大耗时量: 7.694962024688721\n最小耗时量: 1.296091079711914\n平均耗时量: 5.523866602182388"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e6/e6307866c20a23e4b9d6641f2f09880c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"测试结果"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过对上面的数据进行基本分析,可以作图:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/35/353f99196caa60701eb3788f84141b27.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过这个表格可以看到,由于我是在国内测试的aws和google cloud,而且还是国外区域,所以网络因素对其影响蛮大的。抛弃掉这个因素进行分析可以看到,在串行的时候,腾讯云的冷启动率比较大,容器复用率相对比较低,串行环境下,国内厂商最小的时间消耗基本一致,平均的时间消耗也基本一致。国外厂商中,由于受到网络环境的影响,导致数据可供参考的价值不大,但是可以确定的是,无论是AWS还是GoogleCloud的串行测试过程中,容器复用率还是蛮高的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实相对对来说,在串行环境下,除了腾讯云的容器复用率比较低之外,整体来看云厂商之间区别不大。重点还是在并行测试结果上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"并行测试中,可以看到,全部厂商中,只有腾讯云出现了失败次数,失败报错是:"},{"type":"codeinline","content":[{"type":"text","text":"HTTP Error 504: Gateway Time-out"}]},{"type":"text","text":",我为了确定这个不是我的问题,我进行了多次运行,结果腾讯云每次都会有十几个失败的请求,可以基本判断是API网关/云函数的不稳定,造成了504的错误。除此之外,冷启动造成的最大耗时,腾讯云高居榜首,是阿里云和华为云的4倍之多,就算包括了测试环境访问国外网络因素,腾讯云的冷启动最大耗时也是AWS和GoogleCloud的2倍之多,这个结果并不理想。在最小耗时部分,基本上是和串行一致,原因很简单,因为我的程序中是休眠1S,而冷启动很多都高于1S,这就导致可能出现了复用情况,通过冷启动次数也可以确定这个推断。由于腾讯云的冷启动最大耗时实在太大了,导致其平均耗时也比较高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函数冷启动问题确实是在项目中常见的一个现象,我做了一个微信公众号,后台绑定了两个函数,冷启动可怕到每次遇到冷启动,公众号的后台服务都会被微信判定为\"故障,无法提供服务\",在实际项目中,冷启动不能说是地球毁灭,但是也应该是一场大灾难。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我始终认为,一个项目对开发者再友好,提供再多的功能/能力都是建立起来的高楼,如果这个高楼的地基不稳定,那么这高楼注定坍塌。与其更多的用户侧体验优化,真不如用心做一下核心能力,否则冷启动14S,真的可以吓退开发者。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后以我的公众号做结:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9f/9fe9560430fb77cf123d44bbc8cb1f2b.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章