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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章