說起 Serverless 這個詞,我想大家應該都不陌生,那麼 Serverless 這個詞到底是什麼意思?Serverless 到底能解決什麼問題?可能很多朋友還沒有深刻的體會和體感。這篇文章我就和大家一起聊聊 Serverless 。
什麼是 Serverless
我們先將 Serverless 這個詞拆開來看。Server,大家都知道是服務器的意思,說明 Serverless 解決的問題範圍在服務端。Less,大家肯定也知道它的意思是較少的。那麼 Serverless 連起來,再稍加修飾,那就是較少的關心服務器的意思。
Serverfull 時代
我們都知道,在研發側都會有研發人員和運維人員兩個角色,要開發一個新系統的時候,研發人員根據產品經理的 PRD 開始寫代碼開發功能,當功能開發、測試完之後,要發佈到服務器。這個時候開始由運維人員規劃服務器規格、服務器數量、每個服務部署的節點數量、服務器的擴縮容策略和機制、發佈服務過程、服務優雅上下線機制等等。這種模式是研發和運維隔離,服務端運維都由專門的運維人員處理,而且很多時候是靠純人力處理,也就是 Serverfull 時代。
DevOps 時代
互聯網公司裡最辛苦的是誰?我相信大多數都是運維同學。白天做各種網絡規劃、環境規劃、數據庫規劃等等,晚上熬夜發佈新版本,做上線保障,而且很多事情是重複性的工作。然後慢慢就有了賦能研發這樣的聲音,運維同學幫助研發同學做一套運維控制檯,可以讓研發同學在運維控制檯上自行發佈服務、查看日誌、查詢數據。這樣一來,運維同學主要維護這套運維控制檯系統,並且不斷完善功能,輕鬆了不少。這就是研發兼運維的 DevOps 時代。
Serverless 時代
漸漸的,研發同學和運維同學的關注點都在運維控制檯了,運維控制檯的功能越來越強大,比如根據運維側的需求增加了自動彈性擴縮、性能監控的功能,根據研發側的需求增加了自動化發佈的流水線功能。因為有了這套系統,代碼質量檢測、單元測試、打包編譯、部署、集成測試、灰度發佈、彈性擴縮、性能監控、應用防護這一系列服務端的工作基本上不需要人工參與處理了。這就是 NoOps,Serverless 時代。
Serverless 在編程教育中的應用
2020 年註定是不平凡的一年,疫情期間,多少家企業如割韭菜般倒下,又有多少家企業如雨後春筍般茁壯成長,比如在線教育行業。
沒錯,在線教育行業是這次疫情的最大受益者,在在線教育在這個行業裡,有一個細分市場是在線編程教育,尤其是少兒編程教育和麵向非專業人士的編程教育,比如編程貓、斑馬AI、小象學院等。這些企業的在線編程系統都有一些共同的特點和訴求:
- 屏幕一側寫代碼,執行代碼,另一側顯示運行結果。
- 根據題目編寫的代碼都是代碼塊,每道題的代碼量不會很大。
- 運行代碼的速度要快。
- 支持多種編程語言。
- 能支撐不可預計的流量洪峰衝擊。
例如小象學院的編程課界面:
結合上述這些特點和訴求,不難看出,構建這樣一套在線編程系統的核心在於有一個支持多種編程語言的、健壯高可用的代碼運行環境。
那麼我們先來看看傳統的實現架構:
從 HighLevel 的架構來看,前端只需要將代碼片段和編程語言的標識傳給 Server 端即可,然後等待響應展示結果。所以整個 Server 端要負責對不同語言的代碼進行分類、預處理然後傳給不同編程語言的 Runtime 。這種架構有以下幾個比較核心的問題。
工作量大,靈活性差
首先是研發和運維工作量的問題,當市場有新的需求,或者洞察到新業務模式時需要增加編程語言,此時研發側需要增加編程代碼分類和預處理的邏輯,另外需要構建對應編程語言的 Runtime 。在運維側需要規劃支撐新語言的服務器規格以及數量,還有整體的 CICD 流程等。所以支持新的編程語言這個需求要落地,需要研發、運維花費不少的時間來實現,再加上黑/白盒測試和 CICD 流程測試的時間,對市場需求的支撐不能快速的響應,靈活性相對較差。
高可用自己兜底
其次整個在線編程系統的穩定性是重中之重。所以所有 Server 端服務的高可用架構都需要自己搭建,用以保證流量高峰場景和穩態場景下的系統穩定。高可用一方面是代碼邏輯編寫的是否優雅和完善,另一方面是部署服務的集群,無論是 ECS 集群還是 K8s 集群,都需要研發和運維同學一起規劃,那麼對於對編程語言進行分類和預處理的服務來講,尚能給定一個節點數,但是對於不同語言的 Runtime 服務來講,市場需求隨時會變,所以不好具體衡量每個服務的節點數。另外很重要的一點是所以服務的擴容,縮容機制都需要運維同學來實時手動操作,即便是通過腳本實現自動化,那麼ECS彈起的速度也是遠達不到業務預期的。
成本控制粒度粗
再次是整個 IaaS 資源的成本控制,我們都知道這種在線教育是有明顯的流量潮汐的,比如上午 10 點到 12 點,下午 3 點到 5 點,晚上 8 點到 10 點這幾個時段是流量比較大的時候,其他時間端流量比較小,而且夜晚更是沒什麼流量。所以在這種情況下,傳統的部署架構無法做到 IaaS 資源和流量的貼合。舉個例子,加入為了應對流量高峰時期,需要 20 臺 ECS 搭建集群來承載流量衝擊,此時每臺 ECS 的資源使用率可能在 70% 以上,利用率較高,但是在流量小的時候和夜晚,每臺 ECS 的資源使用率可能就是百分之十幾甚至更低,這就是一種資源浪費。
Serverless 架構
那麼我們來看看如何使用 Serverless 架構來實現同樣的功能,並且解決上述幾個問題。在選擇 Serverless 產品時,在國內自然而然優先想到的就是阿里雲的產品。阿里雲有兩款 Serverless 架構的產品 Serverless 應用引擎和函數計算,這裡我們使用函數計算來實現編程教育的場景。
函數計算(FunctionCompute)是事件驅動的全託管計算服務,簡稱FC。使用函數計算,我們無需採購與管理服務器等基礎設施,只需編寫並上傳代碼。函數計算為您準備好計算資源,彈性地、可靠地運行任務,並提供日誌查詢、性能監控和報警等功能。
這裡不對FC的含義做過多贅述,只舉一個例子。FC 中有兩個概念,一個是服務,一個是函數。一個服務包含多個函數:
這裡拿 Java 微服務架構來對應,可以理解為,FC 中的服務是Java中的一個類,FC 中的函數是 Java 類中的一個方法:
但是 Java 類中的方法固然只能是 Java 代碼,而 FC 中的函數可以設置不同語言的 Runtime 來運行不同的編程語言:
這個結構理解清楚之後,我們來看看如何調用 FC 的函數,這裡會引出一個觸發器的概念。我們最常使用的 HTTP 請求協議其實就是一種類型的觸發器,在 FC 裡稱為 HTTP 觸發器,除了 HTTP 觸發器以外,還提供了 OSS (對象存儲)觸發器、SLS(日誌服務)觸發器、定時觸發器、MNS 觸發器、 CDN 觸發器等。
從上圖可以大概理解,我們可以通過多種途徑調用 FC 中的函數。舉例兩個場景,比如每當我在指定的 OSSBucket 的某個目錄下上傳一張圖片後,就可以觸發 FC 中的函數,函數的邏輯是將剛剛上傳的圖片下載下來,然後對圖片做處理,然後再上傳回 OSS 。再比如向 MNS 的某個隊列發送一條消息,然後觸發FC中的函數來處理針對這條消息的邏輯。
最後我們再來看看 FC 的高可用。每一個函數在運行代碼時底層肯定還是 IaaS 資源,但我們只需要給每個函數設置運行代碼時需要的內存數即可,最小 128M ,最大 3G ,對使用者而言,不需要考慮多少核數,也不需要知道代碼運行在什麼樣的服務器上,不需要關心啟動了多少個函數實例,也不需要關心彈性擴縮的問題等,這些都由 FC 來處理。
從上圖可以看到,高可用有兩種策略:
- 給函數設置併發實例數,假如設置為 3 ,那麼有三個請求進來時,該函數只啟一個實例,但是會啟三個線程來運行邏輯。
- 線程數達到上限後,會再拉起一個函數實例。
大家看到這裡,可能已經大概對基於 FC 實現在線編程教育系統的架構有了一個大概的輪廓。
上圖是基於 FC 實現的在線編程教育系統的架構圖,在這個架構下來看看上述那三個核心問題怎麼解:
- 工作量和靈活性:我們只需要關注在如何執行代碼的業務邏輯上,如果要加新語言,只需要創建一個對應語言 Runtime 的 FC 函數即可。
- 高可用:多線程運行業務邏輯和多實例運行業務邏輯兩層高可用保障,並且函數實例的擴縮完全都是 FC 自動處理,不需要研發和運維同學做任何配置。
- 成本優化:當沒有請求的時候,函數實例是不會被拉起的,此時也不會計費,所以在流量低谷期或者夜間時,整個 FC 的成本消耗是非常低的。可以做到函數實例個數、計費粒度和流量完美的貼合。
Python 編程語言示例
下面以運行 Python 代碼為例來看看如何用 FC 實現 Python 在線編程 Demo 。
創建服務和函數
打開函數計算(FC)控制檯,選擇對應的 Region ,選擇左側服務/函數,然後新建服務:
https://fc.console.aliyun.com/fc/overview/cn-hangzhou
輸出服務名稱,創建服務。
進入新創建的服務,然後創建函數,選擇HTTP函數,即可配置HTTP觸發器的函數:
設置函數的各個參數:
幾個需要的注意的參數這裡做以說明:
- 運行環境:這個很好理解,這裡選擇 Python3 。
- 函數實例類型:這裡有彈性實例和性能實例兩種,前者最大支持 2C 3G 規格的實例,後者支持更大的規格,最大到 8C 16G 。
- 函數入口:詳細參見文檔-HTTP 觸發器認證方式:anonymous 為不需要鑑權, function 是需要鑑權的:
https://help.aliyun.com/document_detail/74756.html?spm=a2c4g.11186623.6.572.195359cdselnzR
代碼解析
函數創建好,進入函數,可以看到概述、代碼執行、觸發器、日誌查詢等頁籤,我們先看觸發器,會看到這個函數自動創建了一個 HTTP 觸發器,有調用該函數對應的 HTTP 路徑:
然後我們選擇代碼執行,直接在線寫入我們的代碼:
具體代碼如下:
# -*- coding: utf-8 -*-
import logging
import urllib.parse
import time
import subprocess
def handler(environ, start_response):
context = environ['fc.context']
request_uri = environ['fc.request_uri']
for k, v in environ.items():
if k.startswith('HTTP_'):
pass
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
# 獲取用戶傳入的code
request_body = environ['wsgi.input'].read(request_body_size)
codeStr = urllib.parse.unquote(request_body.decode("GBK"))
# 因為body裡的對象裡有code和input兩個屬性,這裡分別獲取用戶code和用戶輸入
codeArr = codeStr.split('&')
code = codeArr[0][5:]
inputStr = codeArr[1][6:]
# 將用戶code保存為py文件,放/tmp目錄下,以時間戳為文件名
fileName = '/tmp/' + str(int(time.time())) + '.py'
f = open(fileName, "w")
# 這裡預置引入了time庫
f.write('import time \r\n')
f = open(fileName, "a")
f.write(code)
f.close()
# 創建子進程,執行剛才保存的用戶code py文件
p = subprocess.Popen("python " + fileName, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, encoding='utf-8')
# 通過標準輸入傳入用戶的input輸入
if inputStr != '' :
p.stdin.write(inputStr + "\n")
p.stdin.flush()
# 通過標準輸出獲取代碼執行結果
r = p.stdout.read()
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [r.encode('UTF-8')]
整個代碼思路如下:
- 從前端傳入代碼片段,格式是字符串。
- 在 FC 函數中獲取到傳入的代碼字符串,截取 code 內容和 input 的內容。因為這裡簡單實現了 Python 中 input 交互的能力。
- 將代碼保存為一個 Python 文件,以時間戳為文件名,保存在 FC 函數的 /tmp 目錄下。(每個FC函數都有獨立的 /tmp 目錄,可以存放臨時文件)
- 然後在文件中追加了引入 time 庫的代碼,應對 sleep 這種交互場景。
- 通過 subprocess 創建子進程,以 Shell 的方式通過 Python 命令執行保存在 /tmp 目錄下的 Python 文件。如果有用戶輸入的信息,則通過標準輸入輸出寫入子進程。
- 最後讀取執行結果返回給前端。
前端代碼
前端我使用 VUE 寫了簡單的頁面,這裡解析兩個簡單的方法:
頁面加載時初始化 HTTP 請求對象,調用的 HTTP 路徑就是方才函數的 HTTP 觸發器的路徑。
這個方法就是調用 FC 中的 PythonRuntime 函數,將前端頁面的代碼片段傳給該函數。這裡處理 input 交互的思路是,掃描整個代碼片段,以包含 input 代碼為標識將整個代碼段分成多段。沒有包含 input 代碼的直接送給 FC 函數執行,包含 input 代碼的,請求用戶的輸入,然後代碼片段帶著用戶輸入的信息一起送給 FC 函數執行。
演示如下:
結束語
這篇文章洋洋灑灑給大家介紹了 Serverless ,阿里雲的 Serverless 產品函數計算(FC)以及基於函數計算(FC)實現的在線編程系統的Demo。大家應該有所體感,基於函數計算(FC)實現在線編程系統時,研發同學只需要專注在如何執行由前端傳入的代碼即可,整個 Server 端的各個環節都不需要研發同學和運維同學去關心,基本體現了 Serverless 的精髓。
基於 Serverless 還有很多其他的應用場景,之後我會一一分享給大家,大家如果有任何疑問也可以掃碼加入釘釘群(群號:35712134)來尋找答案,我們不見不散!
作者:計緣
來源:阿里巴巴中間件 微信公眾號
原文鏈接:https://mp.weixin.qq.com/s/007Wy1AdQh21Q3nsBPk5lw