基於 Serverless 架構的編程學習小工具

以前我作過一個在線編程的軟件,目前用戶量大概有幾十萬,經過這個 App 不只僅能夠進行代碼的編寫、運行還能夠進行編程的學習。本身一直對 Serverless 架構情有獨鍾,剛好趕到個人這個 App 學習板塊被不少人吐槽難用,索性就對這個學習板塊進行重構,而且打算在重構的時候,直接將這個學習板塊搬上 Serverless 架構。前端

本文做者 Anycodespython

基於 Serverless 架構重構是出於兩個方面考慮 —— 一是 Serverless 架構能讓我的開發者的運維工做變得簡單,尤爲是不用操心服務器,也不用關心流量洪峯(固然,對於個人我的項目而言,也沒太多的洪峯),二是 Serverless 架構的按量付費,極大節約了成本。mysql

總體設計

數據庫設計

這個部分在以前是若干個大模塊,如今統一整理到一個模塊中進行項目重構,因此這裏繼續複用以前的數據庫:git

在這個數據庫中,四個模塊分別是:新聞文章、開發文檔、基礎教程以及圖書資源。其中開發文檔包括大分類,子列表以及正文等內容,這裏表關聯並無使用外鍵,而是直接用的 ID 進行表之間的關聯。github

說實話,這個數據庫設計的並非很好,緣由是由於初次構建這個數據部分,絕大部分數據都是在其餘站點採集而來,當時因爲模塊快速上線,便直接按照原有格式存儲,因此能夠認爲這個數據庫中有不少表的字段實際上是無效的,或者針對這個項目是未被使用的。web

後端設計

後端將會總體部署到一個函數上,功能總體結構:sql

總體功能就是雲函數 SCF 綁定 API 網關觸發器,用戶訪問 API 網關指定的地址,觸發雲函數,而後函數在入口處進行功能拆分,請求不一樣的方法得到對應的數據。數據庫

這裏要額外說明一下,後端總體接口部署在一個函數的緣由,是由於我這個模塊的使用量並非很是頻繁,因此部署到一個函數上也不會出現超過最大實例的限制,若是超出限制是能夠申請擴容的;express

其次,全部的接口都是對數據庫增刪改查,放入到一個函數中,在必定程度上能夠保證容器的活性,下降部分冷啓動帶來的問題,同時容器的複用,也能夠在必定程度上下降後臺數據庫連接池的壓力;除此以外,全部的接口功能,都是隻須要最少的內存(64M)便可完整運行,不會由於個別接口的預估內存較大,進而影響影響總體的成本。編程

因此這裏評估以後,是能夠將多個接口,放入到一個函數中,對外提供對應的服務。

前端設計

前端設計,預計在學習資源部分須要有 8 個頁面,主要就是科技類新聞、教程、文檔、圖書等相關功能,經過墨刀繪製的原型圖以下:

前端項目開發將會採用 Vue.js,而且將其部署到對象存儲中,經過騰訊雲對象存儲的靜態網站功能對外提供服務。

項目開發

後端函數開發

後端函數開發主要包括三部分

  • 部分資源的初始化,部分資源初始化,須要在函數外進行,這樣能夠保證複用實例的時候不會再次創建連接,防止數據庫鏈接池出現問題:
def getConnection(dbName):
    conn = pymysql.connect(host="",
                           user="root",
                           password="",
                           port=3306,
                           db=dbName,
                           charset='utf8',
                           cursorclass=pymysql.cursors.DictCursor,
                           )
    conn.autocommit(1)
    return conn


connectionArticle = getConnection("anycodes_article")
  • 數據庫查詢操做

這一部分主要就是針對不一樣接口查詢數據庫,例如獲取文章分類:

def getArticleCategory():
    connectionArticle.ping(reconnect=True)
    cursor = connectionArticle.cursor()
    search_stmt = ('SELECT * FROM `category` ORDER BY `sort`')
    cursor.execute(search_stmt, ())
    data = cursor.fetchall()
    cursor.close()
    result = {}
    for eve_data in data:
        if eve_data['pre_name'] not in result:
            result[eve_data['pre_name']] = []
        result[eve_data['pre_name']].append({
            "id": eve_data["sort"],
            "name": eve_data["name"]
        })
    return result

例如獲取文章列表:

def getArticleList(cid):
    connectionArticle.ping(reconnect=True)
    cursor = connectionArticle.cursor()
    search_stmt = ('SELECT * FROM `article` WHERE `category` = %s ORDER BY `sort`')
    cursor.execute(search_stmt, (cid,))
    data = cursor.fetchall()
    cursor.close()
    result = [{
                "id": eve_data["aid"],
                "title": eve_data["title"]
            } for eve_data in data]
    return result
  • 最後一部分就是函數的入口,函數入口部分就是作功能分發和接口識別:
def main_handler(event, context):
    try:
        result_data = {
            "error": False
        }
        req_type = event["pathParameters"]["type"]
        if req_type == "get_book_list":
            result_data["data"] = getBookList()
        elif req_type == "get_book_info":
            result_data["data"] = getBookContent(event["queryString"]["id"])
        elif req_type == "get_daily_content":
            result_data["data"] = getDailyContent(event["queryString"]["id"])
        elif req_type == "get_daily_list":
            result_data["data"] = getDailyList(event["queryString"]["category"])
        elif req_type == "get_dictionary_result":
            result_data["data"] = getDictionaryResult(event["queryString"]["word"])
        elif req_type == "get_dev_content":
            result_data["data"] = getDevContent(event["queryString"]["id"])
        elif req_type == "get_dev_section":
            result_data["data"] = getDevSection(event["queryString"]["id"])
        elif req_type == "get_dev_chapter":
            result_data["data"] = getDevChapter(event["queryString"]["id"])
        elif req_type == "get_dev_list":
            result_data["data"] = getDevList()
        elif req_type == "get_article_content":
            result_data["data"] = getArticle(event["queryString"]["id"])
        elif req_type == "get_article_list":
            result_data["data"] = getArticleList(event["queryString"]["id"])
        elif req_type == "get_article_category":
            result_data["data"] = getArticleCategory()
        return result_data
    except Exception as e:
        print(e)
        return {"error": True}

函數部分完成以後,能夠配置 API 網關部分:

在整個後端接口開發過程當中,其實並無遇到什麼太大的問題,由於這個學習功能的模塊基本上就是對數據庫進行查詢的操做,因此相對來講很是順利。

效果預覽

總體預覽結果:一共包括十幾個頁面,這裏取其中8個主要的頁面進行效果展現:

整個頁面基本上是還原了設計稿的樣子,而且和原有項目進行了部分的整合,不管是列表頁面仍是圖書頁面等,數據加載速度表現良好。

經過 PostMan 進行基本測試:

對接口進行 1000 次訪問測試:

能夠看到,接口表現良好,並未出現失敗的狀況,對該測試結果進行耗時的可視化:

其中最大的時間消耗是 219 毫秒,最小是 27 毫秒,平均值 35 毫秒,能夠看到總體的效果仍是很是不錯。

這樣一個項目開發完成,上線以後,前端部分被放到對象存儲 COS 中,後端業務被放到雲函數 SCF 中,觸發器使用的是 API 網關,在監控層面,函數計算有着比較不錯的監控緯度:

同時函數併發,彈性伸縮等問題都由雲廠商來解決,能夠這樣說,自從這個組件部署到了 Serverless 架構上,我所作的操做就是若是業務代碼有問題,進行簡單修復和簡單維護。講真,整個效果仍是不錯的。

經過按量付費,能夠看到我後端服務產生的費用:

因爲雲函數沒辦法看到單個資源的費用,因此整個函數我有幾十個,一共花費的費用也遠遠比服務器的一個月便宜不少。

固然雖說在計算服務這裏總體費用只有幾元錢相對來講很是便宜,可是其還有 API 網關的費用和對象存儲的費用,例如 API 網關費用:

一樣,我這裏的 API 網關也是有不少服務的,不只僅是 Anycodes 這樣一個服務產生的,可是總體加一塊兒 2 月份只有 1 元錢,相對來講也是蠻低的。

總結

經過我的項目中的一個子模塊重構過程,將該項目部署到 Serverless 架構上:

  • 在開發過程當中,我以爲是蠻方便的,一方面本身不須要在服務器中安裝各種軟件,也不須要搭建 web 服務,不須要對 web 服務進行優化,作的只是讀取數據庫,按照必定的格式進行 return,至於 web 服務等相關模塊交給 API 網關來實現,整個一個後端開發大概耗時大約是一個多小時;前端開發是比較耗時的,由於我我的不是專業作前端的,因此不管是佈局仍是邏輯開發,都是有點障礙的,可是也只用了 2 天時間;因此這個模塊從開發到上線只用了 2 天時間;

  • 項目在部署的時候很是流暢,基於 Serverless Framework 的開發者工具一鍵部署,後期更新維護,只須要從新部署便可,線上也是無縫切換,不會出現更新服務形成的服務中斷,也不用爲更新服務可能形成服務中斷而作額外的操做,總體後期更新過程快速且簡單易用;

  • 資源消耗部分就是使用按量付費,經過一個月的觀察,整個資源消耗是蠻低的,總體性能保證的同時,成本也逐漸的被壓低,對於我的開發者來講,確實是一個福音。

經過這樣一個簡單上 Serverless 架構的過程,也讓我對 Serverless 架構有了更深刻的瞭解和認識,做爲一種新技術或者說新的架構,Serverless 的成長還須要一段時間。可是我相信,他的成長,會很快速。

Serverless Framework 30 天試用計劃

咱們誠邀您來體驗最便捷的 Serverless 開發和部署方式。在試用期內,相關聯的產品及服務均提供免費資源和專業的技術支持,幫助您的業務快速、便捷地實現 Serverless!

詳情可查閱:Serverless Framework 試用計劃

One More Thing

3 秒你能作什麼?喝一口水,看一封郵件,仍是 —— 部署一個完整的 Serverless 應用?

複製連接至 PC 瀏覽器訪問:https://serverless.cloud.tencent.com/deploy/express

3 秒極速部署,當即體驗史上最快的 Serverless HTTP 實戰開發!

傳送門:

歡迎訪問:Serverless 中文網,您能夠在 最佳實踐 裏體驗更多關於 Serverless 應用的開發!


推薦閱讀:《Serverless 架構:從原理、設計到項目實戰》

相關文章
相關標籤/搜索