簡短回憶下計算領域的發展html
早期,出現了……好吧,這有點複雜。很早的時候,出現了機械計算機,後來又有了埃尼阿克 ENIAC(Electronic Numerical Integrator And Computer,很早的電子計算機),可是都沒有規模生產。直到大型機出現後,計算領域才快速發展。python
這些日期是大概的發佈或者流行日期,無需和我爭論時間的準確性。
計算領域的演進趨勢是執行的功能單元愈來愈小。每一次演進一般都意味着運維負擔的減少和運維靈活性的增長。linux
發展前景web
喔,Serverless!可是,serverless 能給咱們帶來什麼好處? 咱們將面臨什麼挑戰呢?數據庫
未執行代碼時無需付費。我認爲,這是個巨大的賣點。當無人訪問你的站點或用你的 API 時,你無需付錢。沒有持續支出的基礎設施成本,僅僅支付你須要的部分。換句話說,這履行了雲計算的承諾:「僅僅支付你真正用的資源」。json
無需維護服務器,也無需考慮服務器安全。服務器的維護和安全將由你的服務提供商來處理(固然,你也能夠架設本身的 serverless 主機,只是這彷佛是在向錯誤的方向前進)。因爲你的執行時間也是受限的,安全補丁也被簡化了,由於徹底不須要重啓。這些都應該由你的服務提供商無縫地處理。flask
無限的可擴展性。這是又一個大的好處。假設你又開發了一個 Pokemon Go, 與其頻繁地把站點下線維護升級,不如用 serverless 來不斷地擴展。固然,這也是個雙刃劍,大量的帳單也會隨之而來。若是你的業務的利潤強依賴於站點上線率的話,serverless 確實能幫上忙。api
強制的微服務架構。這也有兩面性,一方面,微服務彷佛是一種好的構建靈活可擴展的、容錯的架構的方式。另外一方面,若是你的業務沒有按照這種方式設計,你將很難在已有的架構中引入 serverless。安全
可是如今你被限制在他們的平臺上服務器
受限的環境。你只能用服務提供商提供的環境,你想在 Rust 中用 serverless?你可能不會太幸運。
受限的預裝包。你只有提供商預裝的包。可是你或許可以提供你本身的包。
受限的執行時間。你的 Function 只能夠運行這麼長時間。若是你必須處理 1TB 的文件,你可能須要有一個解決辦法或者用其餘方案。
強制的微服務架構。參考上面的描述。
受限的監視和診斷能力。例如,你的代碼在幹什麼? 在 serverless 中,基本不可能在調試器中設置斷點和跟蹤流程。你仍然能夠像往常同樣記錄日誌併發出統計度量,可是這帶來的幫助頗有限,沒法定位在 serverless 環境中發生的難點問題。
競爭領域
自從 2014 年出現 AWS Lambda 之後,serverless 的提供商已經增長了一些。下面是一些主流的服務提供商:
這些平臺都有它們的相對優點和劣勢(例如,Azure 支持 C#,或者緊密集成在其餘提供商的平臺上)。這裏面最大的玩家是 AWS。
經過 AWS 的 Lambda 和 API Gateway 構建你的第一個 API
咱們來試一試 serverless。咱們將用 AWS Lambda 和 API Gateway 來構建一個能返回 Jimmy 所說的「Guru Meditations」的 API。
全部代碼在 GitHub 上能夠找到。
API文檔:
POST / { "status": "success", "meditation": "did u mention banana cognac shower" }
怎樣組織工程文件
文件結構樹:
. ├── LICENSE ├── README.md ├── server │ ├── __init__.py │ ├── meditate.py │ └── swagger.json ├── setup.py ├── tests │ └── test_server │ └── test_meditate.py └── tools ├── deploy.py ├── serve.py ├── serve.sh ├── setup.sh └── zip.sh
AWS 中的信息(想了解這裏究竟在作什麼的詳細信息,可查看源碼tools/deploy.py)。
代碼
Lambda Function 將從一個硬編碼列表中隨機選擇一個並返回 guru meditations,很是簡單:
import logging import random logger = logging.getLogger() logger.setLevel(logging.INFO) def handler(event, context): logger.info(u"received request with id '{}'".format(context.aws_request_id)) meditations = [ "off to a regex/", "the count of machines abides", "you wouldn't fax a bat", "HAZARDOUS CHEMICALS + RKELLY", "your solution requires a blood eagle", "testing is broken because I'm lazy", "did u mention banana cognac shower", ] meditation = random.choice(meditations) return { "status": "success", "meditation": meditation, }
deploy.py 腳本
這個腳本至關長,我無法貼在這裏。它基本只是遍歷上述「AWS 中的信息」下的項目,確保每項都存在。
咱們來部署這個腳本
只需運行 ./tools/deploy.py。
基本完成了。不過彷佛在權限申請上有些問題,因爲 API Gateway 沒有權限去執行你的 Function,因此你的 Lambda Function 將不能執行,報錯應該是「Execution failed due to configuration error: Invalid permissions on Lambda function」。我不知道怎麼用 botocore 添加權限。你能夠經過 AWS console 來解決這個問題,找到你的 API, 進到 "/POST" 端點,進到「integration request」,點擊「Lambda Function」旁邊的編輯圖標,修改它,而後保存。此時將彈出一個窗口提示「You are about to give API Gateway permission to invoke your Lambda function」, 點擊「OK」。
當你完成後,記錄下/tools/deploy.py 打印出的 URL,像下面這樣調用它,而後查看你的新 API 的行爲:
$ curl -X POST https://a1b2c3d4.execute-api.us-east-1.amazonaws.com/prod/ {"status": "success", "meditation": "the count of machines abides"}
本地運行
不幸的是,AWS Lambda 沒有好的方法能在本地運行你的代碼。在這個例子裏,咱們將用一個簡單的 flask 服務器來在本地託管合適的端點,並調用 handler 函數。
from __future__ import absolute_import from flask import Flask, jsonify from server.meditate import handler app = Flask(__name__) @app.route("/", methods=["POST"]) def index(): class FakeContext(object): aws_request_id = "XXX" return jsonify(**handler(None, FakeContext())) app.run(host="0.0.0.0")
你能夠在倉庫中用 /tools/serve.sh 運行它,像這樣調用:
$ curl -X POST http://localhost:5000/ { "meditation": "your solution requires a blood eagle", "status": "success" }
測試
你老是應該測試你的代碼。咱們的測試方法是導入並運行咱們的 handler 函數。這是最基本的 python 測試方法:
from __future__ import absolute_import import unittest from server.meditate import handler class SubmitTestCase(unittest.TestCase): def test_submit(self): class FakeContext(object): aws_request_id = "XXX" response = handler(None, FakeContext()) self.assertEquals(response["status"], "success") self.assertTrue("meditation" in response)
你能夠在倉庫裏經過 nose2 運行這個測試代碼。
更多前景