Serverless架構與資源評估:性能與成本探索

  • 2019 年 12 月 24 日
  • 筆記

在很多的場合中,Serverless的佈道師常常說Serverless架構和雲主機等區別的時候,都會有類似的描述:

傳統業務開發完成想要上線,需要評估資源使用,根據資源評估結果,購買雲主機,並且需要根據業務發展不斷對主機等資源進行升級維護,而Serverless架構,則不需要這樣複雜的流程,只需要將函數部署到線上,一切後端服務交給運營商來處理,哪怕是瞬時高並發,也有雲廠商為您自動擴縮。

但是,在實際生產生活中,Serverless真的是這樣無需對資源評估么?還是說在Serverless架構下,資源評估的內容或者對象發生了變化,或者說進行了簡化呢?

在騰訊云云函數中,我們創建一個雲函數之後,可以看到在設置頁面有幾個設置項是可以進行設置的:

這兩個設置範圍分別是從64M到1536M,和1-900S,涉及到這樣的設置,我覺得就是涉及到了資源評估。

首先先說超時時間,我們一個項目或者說一個函數,一個Action都是有執行時間的,如果超過某個時間沒執行完就可以評估其為發生了「意外」,可以被「幹掉「了,這個就是超時時間,例如我們有一個簡單請求,是獲取用戶信息的請求,我們預估,如果10S中沒有返回證明已經不滿足業務需求,所以這個時候就可以將超時設置為10S,如果說我們有一個業務,運行速度比較慢,至少要50S才能執行完,那麼這個值設置的時候就要大於50,否則程序可能因為超時被強行停止。

然後再說說內存,內存是一個有趣的東西,可能衍生兩個關聯點。

關聯點1: 程序本身需要一定的內存,這個內存要大於程序本身的內存,以Python語言為例:

# encoding=utf-8  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: 計費相關,在雲函數的文檔中,我們可以看到:

計費方式 – 雲函數 – 文檔中心 – 騰訊雲​cloud.tencent.com

雲函數 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倍!

產品單價是:

產品定價 – 雲函數 – 文檔中心 – 騰訊雲​cloud.tencent.com

雖然說GBs的單價很低很低,但是當我們業務量上來之後,這個數字也是要值得注意的,因為我剛才的只是一個單次請求,如果每天有1000此次請求,那:

(僅計算資源使用量費用,而不計算調用次數/外網流量)

1536MB: 4.8*1000*0.00011108 = 0.5元

256MB:0.85*1000*0.00011108 = 0.09442元

如果不是1000次調用,而是10萬次調用,則就是50元和9元的區別,差距很大,隨着流量越大,差距越大。

當然很多時候函數執行時間不會這麼久,以我個人的某個函數為例:

計費時間均是100ms,每日調用量在6000次左右:

如果按照64M內存來看,單資源費用只要76元一年,而如果內存都設置稱為1536,則一年要1824元!這個費用相當於:

所以說,超時時間,是需要對代碼和業務場景進行評估來進行設置,他可能關係到程序運行的穩定和功能的完整性;內存,則不僅僅在程序使用層面有着不同的需求,在費用成本等方面也佔有極大的比重,所以內存設置還是需要對程序進行一個評估,那麼評估問題來了,我設置多大比較划算呢?同樣是之前的代碼,在本地進行簡單的腳本編寫:

from tencentcloud.common import credential  from tencentcloud.common.profile.client_profile import ClientProfile  from tencentcloud.common.profile.http_profile import HttpProfile  from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException  from tencentcloud.scf.v20180416 import scf_client, models    import json  import numpy  import 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架構也是需要資源評估的,而且資源評估同樣和成本是直接掛鈎的,只不過這個資源評估的對象逐漸發生了變化,相對之前的評估唯獨,難度而言,都是大幅度縮小或者降低的。