天天看點

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

什麼是Serverless

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

Serverfull時代

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

DevOps時代

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

Serverless時代

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

Serverless在程式設計教育中的應用

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

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

螢幕一側寫代碼,執行代碼,另一側顯示運作結果。

根據題目編寫的代碼都是代碼塊,每道題的代碼量不會很大。

運作代碼的速度要快。

支援多種程式設計語言。

能支撐不可預計的流量洪峰沖擊。

例如小象學院的程式設計課界面:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

結合上述這些特點和訴求,不難看出,建構這樣一套線上程式設計系統的核心在于有一個支援多種程式設計語言的、健壯高可用的代碼運作環境。

那麼我們先來看看傳統的實作架構:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

Serverless在程式設計教育中的實踐-- coding: utf-8 --

這裡拿Java微服務架構來對應,可以了解為,FC中的服務是Java中的一個類,FC中的函數是Java類中的一個方法:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

但是Java類中的方法固然隻能是Java代碼,而FC中的函數可以設定不同語言的Runtime來運作不同的程式設計語言:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

Serverless在程式設計教育中的實踐-- coding: utf-8 --

從上圖可以大概了解,我們可以通過多種途徑調用FC中的函數。舉例兩個場景,比如每當我在指定的OSS Bucket的某個目錄下上傳一張圖檔後,就可以觸發FC中的函數,函數的邏輯是将剛剛上傳的圖檔下載下傳下來,然後對圖檔做處理,然後再上傳回OSS。再比如向MNS的某個隊列發送一條消息,然後觸發FC中的函數來處理針對這條消息的邏輯。

最後我們再來看看FC的高可用。每一個函數在運作代碼時底層肯定還是IaaS資源,但我們隻需要給每個函數設定運作代碼時需要的記憶體數即可,最小128M,最大3G,對使用者而言,不需要考慮多少核數,也不需要知道代碼運作在什麼樣的伺服器上,不需要關心啟動了多少個函數執行個體,也不需要關心彈性擴縮的問題等,這些都由FC來處理。

Serverless在程式設計教育中的實踐-- coding: utf-8 --

從上圖可以看到,高可用有兩種政策:

給函數設定并發執行個體數,假如設定為3,那麼有三個請求進來時,該函數隻啟一個執行個體,但是會啟三個線程來運作邏輯。

線程數達到上限後,會再拉起一個函數執行個體。

大家看到這裡,可能已經大概對基于FC實作線上程式設計教育系統的架構有了一個大概的輪廓。

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

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

Python程式設計語言示例

下面以運作Python代碼為例來看看如何用FC實作Python線上程式設計Demo。

建立服務和函數

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

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

Serverless在程式設計教育中的實踐-- coding: utf-8 --

設定函數的各個參數:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

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

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

代碼解析

函數建立好,進入函數,可以看到概述、代碼執行、觸發器、日志查詢等頁簽,我們先看觸發器,會看到這個函數自動建立了一個HTTP觸發器,有調用該函數對應的HTTP路徑:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

然後我們選擇代碼執行,直接線上寫入我們的代碼:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

具體代碼如下:

-- 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寫了簡單的頁面,這裡解析兩個簡單的方法:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

頁面加載時初始化HTTP請求對象,調用的HTTP路徑就是方才函數的HTTP觸發器的路徑。

Serverless在程式設計教育中的實踐-- coding: utf-8 --

這個方法就是調用FC中的PythonRuntime函數,将前端頁面的代碼片段傳給該函數。這裡處理input互動的思路是,掃描整個代碼片段,以包含input代碼為辨別将整個代碼段分成多段。沒有包含input代碼的直接送給FC函數執行,包含input代碼的,請求使用者的輸入,然後代碼片段帶着使用者輸入的資訊一起送給FC函數執行。

Serverless在程式設計教育中的實踐-- coding: utf-8 --

示範如下:

Serverless在程式設計教育中的實踐-- coding: utf-8 --

結束語

這篇文章給大家介紹了Serverless,阿裡雲的Serverless産品函數計算(FC)以及基于函數計算(FC)實作的線上程式設計系統的Demo。大家應該有所體感,基于函數計算(FC)實作線上程式設計系統時,研發同學隻需要專注在如何執行由前端傳入的代碼即可,整個Server端的各個環節都不需要研發同學和運維同學去關心,基本展現了Serverless的精髓。

基于Serverless還有很多其他的應用場景,之後我會一一分享給大家,我們不見不散!

【加入客戶交流釘釘群】

阿裡雲專門成立了“網際網路架構更新實戰課”釘釘群,每周邀請一位阿裡雲專家在群内進行行業最佳實踐直播,每天分享行業前沿幹貨,歡迎釘釘掃碼加入。

Serverless在程式設計教育中的實踐-- coding: utf-8 --