天天看點

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

什麼是 Serverless

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

Serverfull 時代

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

DevOps 時代

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

Serverless 時代

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

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

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

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

  • 螢幕一側寫代碼,執行代碼,另一側顯示運作結果。
  • 根據題目編寫的代碼都是代碼塊,每道題的代碼量不會很大。
  • 運作代碼的速度要快。
  • 支援多種程式設計語言。
  • 能支撐不可預計的流量洪峰沖擊。
資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

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

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

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

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

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

Python 程式設計語言示例

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

建立服務和函數

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

https://fc.console.aliyun.com/fc/overview/cn-hangzhou
資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

設定函數的各個參數:

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

  • 運作環境:這個很好了解,這裡選擇 Python3 。
  • 函數執行個體類型:這裡有彈性執行個體和性能執行個體兩種,前者最大支援 2C 3G 規格的執行個體,後者支援更大的規格,最大到 8C 16G 。
  • 函數入口:詳細參見文檔-HTTP 觸發器認證方式:anonymous 為不需要鑒權, function 是需要鑒權的:
https://help.aliyun.com/document_detail/74756.html?spm=a2c4g.11186623.6.572.195359cdselnzR

代碼解析

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

具體代碼如下:

# -*- 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颠覆程式設計教育的創新實踐

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

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

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

示範如下:

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

結束語

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

基于 Serverless 還有很多其他的應用場景,之後我會一一分享給大家,大家如果有任何疑問也可以掃碼加入釘釘群(群号:35712134)來尋找答案,我們不見不散!

資源成本雙優化!看Serverless颠覆程式設計教育的創新實踐

作者:計緣

來源:阿裡巴巴中間件 微信公衆号

原文連結:

https://mp.weixin.qq.com/s/007Wy1AdQh21Q3nsBPk5lw