Serverless 在編程教育中的實踐

提及Serverless這個詞,我想你們應該都不陌生,那麼Serverless這個詞究竟是什麼意思?Serverless到底能解決什麼問題?可能不少朋友尚未深入的體會和體感,這篇文章我就和你們一塊兒聊聊Serverless。前端

什麼是Serverless

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

Serverfull時代

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

DevOps時代

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

Serverless時代

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

Serverless在編程教育中的應用

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

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

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

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

例如小象學院的編程課界面:架構

結合上述這些特色和訴求,不難看出,構建這樣一套在線編程系統的核心在於有一個支持多種編程語言的、健壯高可用的代碼運行環境。併發

那麼咱們先來看看傳統的實現架構:

從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中有兩個概念,一個是服務,一個是函數。一個服務包含多個函數:

這裏拿Java微服務架構來對應,能夠理解爲,FC中的服務是Java中的一個類,FC中的函數是Java類中的一個方法:

可是Java類中的方法當然只能是Java代碼,而FC中的函數能夠設置不一樣語言的Runtime來運行不一樣的編程語言:

這個結構理解清楚以後,咱們來看看如何調用FC的函數,這裏會引出一個觸發器的概念。咱們最常使用的HTTP請求協議其實就是一種類型的觸發器,在FC裏稱爲HTTP觸發器,除了HTTP觸發器之外,還提供了OSS(對象存儲)觸發器、SLS(日誌服務)觸發器、定時觸發器、MNS觸發器、CDN觸發器等。

從上圖能夠大概理解,咱們能夠經過多種途徑調用FC中的函數。舉例兩個場景,好比每當我在指定的OSS Bucket的某個目錄下上傳一張圖片後,就能夠觸發FC中的函數,函數的邏輯是將剛剛上傳的圖片下載下來,而後對圖片作處理,而後再上傳回OSS。再好比向MNS的某個隊列發送一條消息,而後觸發FC中的函數來處理針對這條消息的邏輯。

最後咱們再來看看FC的高可用。每個函數在運行代碼時底層確定仍是IaaS資源,但咱們只須要給每一個函數設置運行代碼時須要的內存數便可,最小128M,最大3G,對使用者而言,不須要考慮多少核數,也不須要知道代碼運行在什麼樣的服務器上,不須要關心啓動了多少個函數實例,也不須要關心彈性擴縮的問題等,這些都由FC來處理。

從上圖能夠看到,高可用有兩種策略:

給函數設置併發實例數,假如設置爲3,那麼有三個請求進來時,該函數只啓一個實例,可是會啓三個線程來運行邏輯。
線程數達到上限後,會再拉起一個函數實例。

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

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

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

Python編程語言示例

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

建立服務和函數

打開函數計算(FC)控制檯,選擇對應的Region,選擇左側服務/函數,而後新建服務:

輸出服務名稱,建立服務。

進入新建立的服務,而後建立函數,選擇HTTP函數,便可配置HTTP觸發器的函數:

設置函數的各個參數:

幾個須要的注意的參數這裏作以說明:

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

代碼解析

函數建立好,進入函數,能夠看到概述、代碼執行、觸發器、日誌查詢等頁籤,咱們先看觸發器,會看到這個函數自動建立了一個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的精髓。

 

原文連接

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

相關文章
相關標籤/搜索