Serverless在編程教育中的實踐

簡介: 基於函數計算(FC)實現在線編程系統時,研發同學只需要專注在如何執行由前端傳入的代碼即可,整個Server端的各個環節都不需要研發同學和運維同學去關心,基本體現了Serverless的精髓。

說起Serverless這個詞,我想大家應該都不陌生,那麼Serverless這個詞到底是什麼意思?Serverless到底能解決什麼問題?可能很多朋友還沒有深刻的體會和體感,這篇文章我就和大家一起聊聊Serverless。

什麼是Serverless

我們先將Serverless這個詞拆開來看。Server,大家都知道是服務器的意思,說明Serverless解決的問題範圍在服務端。Less,大家肯定也知道它的意思是較少的。那麼Serverless連起來,再稍加修飾,那就是較少的關心服務器的意思。

Serverfull時代

我們都知道,在研發側都會有研發人員和運維人員兩個角色,要開發一個新系統的時候,研發人員根據產品經理的PRD開始寫代碼開發功能,當功能開發、測試完之後,要發佈到服務器。這個時候開始由運維人員規劃服務器規格、服務器數量、每個服務部署的節點數量、服務器的擴縮容策略和機制、發佈服務過程、服務優雅上下線機制等等。這種模式是研發和運維隔離,服務端運維都由專門的運維人員處理,而且很多時候是靠純人力處理,也就是Serverfull時代。

DevOps時代

互聯網公司裏最辛苦的是誰?我相信大多數都是運維同學。白天做各種網絡規劃、環境規劃、數據庫規劃等等,晚上熬夜發佈新版本,做上線保障,而且很多事情是重複性的工作。然後慢慢就有了賦能研發這樣的聲音,運維同學幫助研發同學做一套運維控制檯,可以讓研發同學在運維控制檯上自行發佈服務、查看日誌、查詢數據。這樣一來,運維同學主要維護這套運維控制檯系統,並且不斷完善功能,輕鬆了不少。這就是研發兼運維的DevOps時代。

Serverless時代

漸漸的,研發同學和運維同學的關注點都在運維控制檯了,運維控制檯的功能越來越強大,比如根據運維側的需求增加了自動彈性擴縮、性能監控的功能,根據研發側的需求增加了自動化發佈的流水線功能。因爲有了這套系統,代碼質量檢測、單元測試、打包編譯、部署、集成測試、灰度發佈、彈性擴縮、性能監控、應用防護這一系列服務端的工作基本上不需要人工參與處理了。這就是NoOps,Serverless時代。

Serverless在編程教育中的應用

2020年註定是不平凡的一年,疫情期間,多少家企業如割韭菜般倒下,又有多少家企業如雨後春筍般茁壯成長,比如在線教育行業。

沒錯,在線教育行業是這次疫情的最大受益者,在在線教育在這個行業裏,有一個細分市場是在線編程教育,尤其是少兒編程教育和麪向非專業人士的編程教育,比如編程貓、斑馬AI、小象學院等。這些企業的在線編程系統都有一些共同的特點和訴求:

屏幕一側寫代碼,執行代碼,另一側顯示運行結果。
根據題目編寫的代碼都是代碼塊,每道題的代碼量不會很大。

運行代碼的速度要快。
支持多種編程語言。
能支撐不可預計的流量洪峯衝擊。

例如小象學院的編程課界面:
image.png
結合上述這些特點和訴求,不難看出,構建這樣一套在線編程系統的核心在於有一個支持多種編程語言的、健壯高可用的代碼運行環境。

那麼我們先來看看傳統的實現架構:
image.png

從High Level的架構來看,前端只需要將代碼片段和編程語言的標識傳給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 應用引擎和函數計算,這裏我們使用函數計算來實現編程教育的場景。
函數計算(Function Compute)是事件驅動的全託管計算服務,簡稱FC。使用函數計算,我們無需採購與管理服務器等基礎設施,只需編寫並上傳代碼。函數計算爲您準備好計算資源,彈性地、可靠地運行任務,並提供日誌查詢、性能監控和報警等功能。

這裏不對FC的含義做過多贅述,只舉一個例子。FC中有兩個概念,一個是服務,一個是函數。一個服務包含多個函數:
image.png
這裏拿Java微服務架構來對應,可以理解爲,FC中的服務是Java中的一個類,FC中的函數是Java類中的一個方法:
image.png
但是Java類中的方法固然只能是Java代碼,而FC中的函數可以設置不同語言的Runtime來運行不同的編程語言:
image.png
這個結構理解清楚之後,我們來看看如何調用FC的函數,這裏會引出一個觸發器的概念。我們最常使用的HTTP請求協議其實就是一種類型的觸發器,在FC裏稱爲HTTP觸發器,除了HTTP觸發器以外,還提供了OSS(對象存儲)觸發器、SLS(日誌服務)觸發器、定時觸發器、MNS觸發器、CDN觸發器等。
image.png
從上圖可以大概理解,我們可以通過多種途徑調用FC中的函數。舉例兩個場景,比如每當我在指定的OSS Bucket的某個目錄下上傳一張圖片後,就可以觸發FC中的函數,函數的邏輯是將剛剛上傳的圖片下載下來,然後對圖片做處理,然後再上傳回OSS。再比如向MNS的某個隊列發送一條消息,然後觸發FC中的函數來處理針對這條消息的邏輯。







最後我們再來看看FC的高可用。每一個函數在運行代碼時底層肯定還是IaaS資源,但我們只需要給每個函數設置運行代碼時需要的內存數即可,最小128M,最大3G,對使用者而言,不需要考慮多少核數,也不需要知道代碼運行在什麼樣的服務器上,不需要關心啓動了多少個函數實例,也不需要關心彈性擴縮的問題等,這些都由FC來處理。
image.png
從上圖可以看到,高可用有兩種策略:

給函數設置併發實例數,假如設置爲3,那麼有三個請求進來時,該函數只啓一個實例,但是會啓三個線程來運行邏輯。

線程數達到上限後,會再拉起一個函數實例。

大家看到這裏,可能已經大概對基於FC實現在線編程教育系統的架構有了一個大概的輪廓。

image.png
上圖是基於FC實現的在線編程教育系統的架構圖,在這個架構下來看看上述那三個核心問題怎麼解:

  • 工作量和靈活性:我們只需要關注在如何執行代碼的業務邏輯上,如果要加新語言,只需要創建一個對應語言Runtime的FC函數即可。
  • 高可用:多線程運行業務邏輯和多實例運行業務邏輯兩層高可用保障,並且函數實例的擴縮完全都是FC自動處理,不需要研發和運維同學做任何配置。
  • 成本優化:當沒有請求的時候,函數實例是不會被拉起的,此時也不會計費,所以在流量低谷期或者夜間時,整個FC的成本消耗是非常低的。可以做到函數實例個數、計費粒度和流量完美的貼合。

image.png

Python編程語言示例

下面以運行Python代碼爲例來看看如何用FC實現Python在線編程Demo。

創建服務和函數

打開函數計算(FC)控制檯,選擇對應的Region,選擇左側服務/函數,然後新建服務:
image.png
輸出服務名稱,創建服務。
image.png
進入新創建的服務,然後創建函數,選擇HTTP函數,即可配置HTTP觸發器的函數:
image.png
設置函數的各個參數:
image.png






幾個需要的注意的參數這裏做以說明:

  • 運行環境:這個很好理解,這裏選擇Python3
  • 函數實例類型:這裏有彈性實例和性能實例兩種,前者最大支持2C3G規格的實例,後者支持更大的規格,最大到8C16G。
  • 函數入口:詳細參見文檔
  • HTTP觸發器認證方式:anonymous爲不需要鑑權,function是需要鑑權的。

代碼解析

函數創建好,進入函數,可以看到概述、代碼執行、觸發器、日誌查詢等頁籤,我們先看觸發器,會看到這個函數自動創建了一個HTTP觸發器,有調用該函數對應的HTTP路徑:
image.png
然後我們選擇代碼執行,直接在線寫入我們的代碼:
image.png
具體代碼如下:



-- 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寫了簡單的頁面,這裏解析兩個簡單的方法:
image.png
頁面加載時初始化HTTP請求對象,調用的HTTP路徑就是方纔函數的HTTP觸發器的路徑。

image.png
這個方法就是調用FC中的PythonRuntime函數,將前端頁面的代碼片段傳給該函數。這裏處理input交互的思路是,掃描整個代碼片段,以包含input代碼爲標識將整個代碼段分成多段。沒有包含input代碼的直接送給FC函數執行,包含input代碼的,請求用戶的輸入,然後代碼片段帶着用戶輸入的信息一起送給FC函數執行。

image.png
演示如下:
image.gif

結束語

這篇文章給大家介紹了Serverless,阿里雲的Serverless產品函數計算(FC)以及基於函數計算(FC)實現的在線編程系統的Demo。大家應該有所體感,基於函數計算(FC)實現在線編程系統時,研發同學只需要專注在如何執行由前端傳入的代碼即可,整個Server端的各個環節都不需要研發同學和運維同學去關心,基本體現了Serverless的精髓。
基於Serverless還有很多其他的應用場景,之後我會一一分享給大家,大家如果有任何疑問也可以加入釘釘羣35712134來尋找答案,我們不見不散!

 

原文鏈接
本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章