利用 Azure Functions 实现无服务器体系结构
從工具到機器再到計算機,我們一直在尋找能夠自動執行重復工作并讓我們所處理的上下文規范化的方法,以便我們可以將重心放在做出高價值的專業化貢獻上,從而完成任務并解決問題。
與此同時,很顯然,隨著 IT 產業的不斷發展,我們一直都努力在每個系統層級(從 CPU 到服務器場)實現更高密度,以期最大限度地提高我們的系統輸出效率。無服務器體系結構就可以同時滿足這兩種需求。借助這種體系結構,不僅可以最大限度地細化個人工作量以重點完成特定任務,還能最大限度地減少系統浪費。
在無服務器環境中,開發者創建的是解決方案(而不是基礎結構),監視的是執行情況(而不是環境運行狀況)。開銷對象為時間片,而不是大多數情況下都處于空閑狀態的虛擬機 (VM) 場。
在 Microsoft Azure 中,有許多可使用的服務能夠關聯在一起,形成一個完整的解決方案。此解決方案的一個新關鍵組件就是 Azure Functions,可以在完整的解決方案生態系統中提供無服務器計算功能。在本文中,我們將探索無服務器體系結構的意義和 Azure Functions 工具。
利用 Azure Functions 實現無服務器體系結構
無服務器體系結構的定義有許多。雖然是這樣命名的,但無服務器體系結構并不是指不使用服務器運行的代碼。
事實上,服務器仍是非常有必要的,這一點毋庸置疑。你可能會認為它是平臺即服務 (PaaS) 的下一迭代,雖然這已經很接近了,但其實還不是。
那么,它究竟什么是呢? 從根本上講,無服務器體系結構是云服務的下個革命,建立在 PaaS 基礎之上,通過綁定移除了 VM、應用程序框架和外部依賴關系,讓開發者能夠將重心僅放在實現業務邏輯的代碼上。
謹慎的做法是介紹 Azure Functions 和無服務器體系結構的典型屬性。Azure Functions 提供無服務器體系結構的無服務器計算組件。如圖 1?所示,Azure Functions 建立在 Azure App Service 和 WebJobs SDK 的基礎之上,在托管和運行 Azure Function 代碼方面,以及在一些細節方面(如運行時賦值)施展了一些奇思妙想。
圖 1:Azure Functions 體系結構
因此,使用 Azure App Service 的所有好處一樣不少(見設置中服務的正下方)。此外,這還意味著,可以在本地使用 WebJobs SDK,創建本地運行時環境。
Azure Functions 是一個多語通平臺,支持 Node.js、C#、PhP、Python 和 Windows PowerShell 等多種語言,盡管這不是體系結構方面的優勢。運行時和依賴關系由平臺進行處理。
這對于使用混合環境的人員來說絕對是一大優勢,因為偏好設置和技能不同的團隊可以利用同一平臺來構建和傳遞功能。使用 Azure Functions 作為無服務器體系結構的無服務器計算組件有以下多項重要優勢:
縮短上市時間:?由于基礎結構是由平臺進行管理,因此開發者可以將重心放在實現業務邏輯的應用程序代碼上。Azure Functions 可視為將微服務進一步分解成納米服務。這兩種范例都是開發流程的一大福音,因為開發、測試和部署活動可以只側重于一小部分與其他相關(但離散)服務分開管理的功能。
降低總擁有成本:?由于基礎結構或操作系統負責維護,因此開發者可以將重心放在提高業務價值上。此外,DevOps 和維護所需的投資還會大大精簡。
按執行次數付費:?僅按使用周期付費,再加上增加所用計算資源的函數密度,通??梢源蟠蠊澕s成本。
至于你在使用 Azure Functions 生成解決方案時采用的方法,實現全部價值的關鍵在于,只編寫最小邏輯單元來完成一個范疇內的工作,并最大限度地減少依賴關系。使用 Azure Functions 時,最好遵循以下最佳做法:
在本質上,Azure 函數的用途應該單一。將它們看作是精簡的語句,而不是并列復合句。
操作為冪等操作。也就是說,如果后續使用相同的參數調用 API 終結點,生成的系統狀態不變。
快速執行。目標應為收到輸入內容,執行所需操作,然后向下游使用者傳遞結果。對于長時間運行的進程,不妨考慮使用 Azure WebJobs 或在 Azure Service Fabric 中托管服務。
在試圖保持較低的整體復雜性和快速執行的同時,最好最大限度地減少內部依賴關系。添加過多的運行時權重不僅會延長初始加載時間,還會增加系統的復雜性。
通過輸入和輸出綁定進行外部集成。有關高性能網站的一些常見指南是編寫無狀態服務。這樣一來,既可以簡化或加快用于保持、序列化和反序列化運行時狀態的服務,也可以簡化調試工作量,因為你無需發現狀態,也無需嘗試重現狀態,即可了解發生了什么;若為無狀態,只需返回參數值即可。
無服務器體系結構的屬性如下:
無服務器體系結構的工作單元采用事件調用的無狀態函數的形式。
縮放、容量和基礎結構管理以服務的形式提供。
以執行次數為依據的計費模型,只需按代碼運行時間付費。
下面介紹了無服務器體系結構帶來的一些挑戰:
復雜性:?雖然無服務器體系結構為開發者簡化了很多操作,但鑒于平臺的抽象性,仍需要重新審視應用程序的生成方式。將整個應用程序作為一個單元進行管理,比管理一系列專門構建的函數及其之間的依賴關系更為簡單。諸如 Azure API 管理提供的功能便派上用場,可用于創建向外的一致命名空間,從而將所有離散定義和托管的函數關聯在一起。
工具:?用于編寫、調試和測試函數的工具相對較新,目前仍處于開發階段。截至本文撰寫之時,這些工具暫處于預覽階段,但將內置在 Visual Studio 2017 中。此外,管理和監視工具仍在改進階段,但已推出適用于 Azure Functions 的基本界面,可以在其中詳細了解請求、成功消息、錯誤消息和請求響應。將應用監視工具綁定到平臺還有大量工作需要完成,Azure Functions 團隊正在努力提供和改進此類支持。
組織支持:?遷移至無服務器范例對于一些組織來說非常重要。許多組織在遷移至全自動持續集成 (CI)/持續交付 (CD) 管道和微服務體系結構時都面臨挑戰。遷移至無服務器設計更是難上加難,因為這經常要質疑現行標準,并需要為資源提供有關可用性以及如何關聯和管理的指導。
無運行時優化:?在傳統設計中,可以根據工作負載優化執行環境,同時更改 RAM、交換、磁盤和網絡連接等的類型和容量。使用 Azure Functions,只能作出微小更改,如要使用的存儲帳戶。
傳統體系結構與無服務器體系結構
一套典型的系統設計項目包括邏輯設計、技術設計和軟件體系結構。邏輯設計通常定義的是系統的功能和用途,而技術設計則通常定義的是系統概念。
隨著你遷移至無服務器體系結構,將更為關注系統的用途,而不是系統的概念。在許多 IT 商店中,看到的更多是像圖 2?一樣的體系結構圖。
圖 2:傳統技術體系結構
這一類項目對于負責托管、網絡和 DBA 組的人員尤為重要,因為他們需要知道預配和配置什么。
不過,在平臺即服務 (PaaS) 環境中,你將重心放在函數屬性上,而將預配詳細信息交由只需定義 PaaS 服務本身配置的平臺處理。
輸入的配置詳細信息將保存在模板中,并且所有對話都將以功能和集成為重心,無需討論 RAM、CPU 和磁盤的最佳容量。
生成的圖可能更接近于圖 3?所示的常見邏輯圖。此類圖一開始的重心將放在函數部分以及如何關聯這些部分上,而不是放在配置應用程序主機上。
這樣簡化溝通和闡明用途不僅有助于就意向和實現展開技術討論,還必然會讓更多的非技術性辦公室享受系統帶來的價值,因為討論的重心從系統的概念轉變為系統的用途。
圖 3:無服務器體系結構
Azure Functions 演示?
Azure IoT 平臺提供一系列豐富的服務來支持收集和分析數據。我們的示例包括現有的 IoT 實現,以使用基本分析功能來收集車輛遙測并將其存儲在云中。
我們現在要探索如何向解決方案添加新功能,如實時查詢數據以找到距離給定位置最近的車輛。這種功能可用于獨立的汽車共享程序,甚至可用于在停車場找車。
在此示例實現中,必須注意一些重要事項,因為我們出于工具和平臺演示目的要故意采取一些簡便方法。
首先,我們直接使用單分區 DocumentDB。在黃金時段實現中,我們至少會根據預期數據量對 DocumentDB 進行分片,但我們也可能選擇執行其他操作,如添加 Azure Redis 緩存和 Azure 彈性搜索作為優化一些讀取路徑的方法。
其次,由于最新的 DocumentDB API 要求客戶端進行更多處理才能獲取我們要比較的記錄,因此我們采取了簡便方法,只要求獲取前 100 條記錄。若要在大型數據集中查找所需記錄,更為典型的方法是使用分區和搜索功能。
無論如何,仍然可以使用函數來查找可能記錄,然后將它們與給定位置進行比較,返回數據集中距離最近的車輛。
創建函數
截至本文撰寫之時,Visual Studio 工具無法幫助加速開發流程。
為此,我們將先通過門戶界面創建 Azure 函數。在 Azure 門戶中開始使用 Azure Functions 創建函數后,便會看到一個邊欄選項卡,用于為函數設定一些設置。請注意應用服務計劃選擇選項: “使用量計劃”和“應用服務計劃”。
在這兩個選項之間做出選擇看似最簡單,但實際上是要在管理資源這一老式做法和無服務器體系結構的理想目標之間做出選擇。選擇“應用服務計劃”時,必須猜測需要多少處理能力。如果選擇過多,就要為沒有使用的資源付費;而如果選擇過少,就可能會遇到實現問題,最終會對客戶和經濟利益造成影響。
“使用量計劃”是首選,因為這樣一來,系統可以為你縮放資源,而你作為使用者則可以按應用的使用量來支付相應費用。
最后一步就是創建函數本身。將從 WebHook 預先創建的 C# 函數入手。這會將觸發器預先配置為根據是否收到 HTTP 請求來執行函數。
選擇左側菜單上的“整合項”后,有多個觸發器、輸入和輸出配置選項可供選擇。觸發器選擇頁上有一些實用信息,包括 WebHook 綁定信息、如何使用 API 密鑰,以及一些用于調用 WebHook 的示例 C# 和 Node.js 代碼??吹綄υ捒蚝?#xff0c;便可以配置輸入和輸出綁定,如圖 4?所示。
圖 4:配置輸入綁定
雖然可以通過添加庫和適配器來調用外部系統,但系統是通過綁定運行,并提供對 EventHubs 和 DocumentDB 等眾多數據源的一級支持??赏ㄟ^ Azure Functions 提供的綁定基礎結構來提升開發體驗。
綁定可讓實際目標軟件基礎結構(DocumentDB、EventHubs、存儲等)抽象化,讓開發者可以向表示目標的參數添加代碼,同時保持靈活性,因為可以通過更改配置來更改目標。
此時,已將其配置為與源 DocumentDB 通信。借助此配置,可以在函數內直接針對 DocumentDB 客戶端編寫代碼,你無需自行連接。請注意文檔參數名稱 inputDocument。這是傳遞給函數的變量,將用來調用 DocumentDB。
此時,可以看到輸出選項,其中包括許多存儲、隊列和其他外部系統。所有可以通過 UI 選擇和配置的項稍后都可通過 Function App 設置 UI 訪問,并能作為模板的一部分或通過程序設計界面進行配置。只需通過 HTTP 將 JSON 結果返回給調用方即可。由于已為輸出設置“HTTP(res)”(定義了輸出參數),因此將原樣接受。
開發代碼
選擇左側菜單中的“開發”后,便會看到聯機編輯器。這有助于快速執行迭代更改并實時查看日志,提供了用于觸發 WebHook 函數的界面。
適用于 Visual Studio 和 Visual Studio Code 的工具正處于開發階段,將帶來更豐富多彩的體驗。如果現在使用的是 Azure Functions,不妨使用 IDE,Visual Studio 通過服務器資源管理器可輕松連接 Function App。
編輯文件時需要執行以下幾項操作。“代碼”窗口正下方有一個“查看文件”鏈接。在“代碼”窗口右側直接打開文件資源管理器。
首先,需要有 DocumentDB 客戶端。有許多庫可通過使用頂部的 #r 指令自動引用。
有關這些庫的列表以及其他許多開發者信息,請參閱聯機開發者參考信息 (bit.ly/2gaWT9x)。
例如,需要訪問 DocumetDB 客戶端對象,因為有對 DocumentDB 的一級支持。只需在文件頂部為相應的程序集添加 #r 指令即可。如果所需的庫不包含在內,請指出一個要維護并發布到 NuGet 的庫,它就會被添加到 project.json 文件(使用 Node.js 編寫的 package.json)中。
此時,可以編輯 run.csx 文件,其中包含所有代碼。為此,直接在適用于 Azure Functions 的聯機 IDE 中編輯該文件,如圖 5?所示。
圖 5:編輯函數代碼
從模板代碼入手,先將你自己的自定義外部庫添加到函數中,因為其中包含半正矢函數代碼。若有不是太大的函數專有自定義類或函數,可以將它們直接添加到 run.csx 文件中。
不過,若有可重用部分,則需要采取不同措施,將它們的編譯版本添加到 \bin 文件夾,或通過 project.json 文件以 NuGet 包形式引用它們,然后使用 #r 引用庫。也可以將代碼置于其他 .csx 文件中,然后使用 #load 指令。
需要使用一些函數來幫助確定要檢測鄰近區域的車輛與傳遞到函數的點之間的距離。
距離學生時代已有一段時間,因此不常需要用到半正矢公式。維基百科對此提供了很好的參考資料 (bit.ly/2gCWrgb),我們借用了?bit.ly/2gD26mK?上的 C# 函數,并進行了一些更改。我們創建了作為半正矢類的靜態成員的必要函數:
代碼經編譯后會上傳到 bin 文件夾(相對于函數的根文件夾)。
在我們的示例中,路徑為 FindNearVehicle/bin,但我們必須創建 bin 文件夾,因為它默認不存在。完成這部分設置后,將重心轉移到所需的代碼上。在函數頂部,需要確保引用的是所需的庫。
尤其需要 DocumentDB 客戶端對象類型、Newtonsoft 以及已上傳到 /bin 文件夾的自定義庫。這些庫通過 #r 指令添加到文件頂部:
#r "Microsoft.Azure.Documents.Client"#r "Newtonsoft.Json"#r "SphericalDistanceLib.dll"如上所述,將根據 created_at 時間戳字段捕捉最后 100 條記錄。DocumentDB 有一個可以使此操作變得非常簡單的實用 SQL 語法:
IQueryable<Document> telemDocs = inputDocument.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(dbName, collectionName),new SqlQuerySpec("SELECT TOP 100 c.vehicle.vin, c.vehicle.model,c.location FROM c ORDER BY c.created_at DESC"), queryOptions);
你使用的是文檔類型,這樣可以略微簡化一下操作,因為你將把它轉換成動態類型,以便向對象輕松添加屬性。SQL 以 SqlQuerySpec 形式傳遞,你只會將 VIN、模型和位置投影到對象。
此時,需要循環訪問文檔列表,使用外部庫中的半正矢函數計算距離,然后確定最近目標并將其返回。不過,這樣做有點棘手。
需要跟蹤看到的所有 VIN,因為你只想獲取相應 VIN 的最新位置記錄。由于將按降序排序,因此第一個文檔是收到的最后一個文檔。
將檢查 VIN 是否為 NULL,因為你要獲取的是在開車輛。如果 VIN 為 NULL,可以假定文檔無效。如果元素中有非 NULL 值,只需嘗試將 VIN 添加到 HashSet 即可。如果列表中沒有,便會成功添加,否則將會失敗,并且你會知道列表中已經有該車輛的最新記錄,如圖 6?所示。
HashSet<string> seen = new HashSet<string>();foreach(dynamic doc in telemDocs){if(seen.Add(doc.vin)){// Calculate the distance??? if (doc.location != null) {doc.distance =Haversine.CalculateDistance(double.Parse(currentLong),double.Parse(currentLat), double.Parse(doc.location.lon.ToString()),double.Parse(doc.location.lat.ToString()));lastKnownLocations.Add(doc);}else{// Location is null, so we won't take this????? // record as last known location????? seen.Remove(doc.vin);}} }圖 6:不同 VIN 的上一已知位置
將整個文檔添加到 lastKnownLocations 列表,從而輕松逆向顯示并根據最小距離值排序來查詢第一個文檔:
var nearestVehicle = lastKnownLocations.OrderBy(x => ((dynamic)x).distance).First();return currentLat == null || currentLong == null? ? req.CreateResponse(HttpStatusCode.BadRequest,"Please pass a lat and lon on the query string or in the request body"): req.CreateResponse(HttpStatusCode.OK, nearestVehicle);可以返回經過排序的列表中的第一個文檔,如最后一行所示(代碼還為你處理了 NULL 參數檢查和文檔序列化)。
運行示例
最后一步是觀察實際效果。在開發視圖的右上角,有一個帶燒瓶圖標的“測試”鏈接。單擊此鏈接將打開測試 UI,如圖 7?所示。
在此 UI 中,不僅可以更改 HTTP 方法、添加參數和請求頭,還能設置要傳遞給選定 HTTP 方法的正文。如果有大量這樣的工作,我們通常會使用 Postman,因為我們有一個測試庫。然而,HTTP 函數的內置測試工具非常實用,可立即執行快速迭代工作。
圖 7:測試函數
我們捕捉了緯度和經度,并將其格式化成“運行”窗口中的預期 JSON 格式。
單擊“運行”后,可以在“日志”窗口中查看調用日志對象后的所有輸出和一般日志輸出,并能在“輸出”窗口的右下角看到輸出和狀態。
請注意日志輸出用時,我們的函數似乎需要約 250 毫秒才能運行。這非常符合我們努力通過 Azure Functions 實現的執行模型:用途單一且執行時間相對較短。
將內容從“輸出”窗口中移出并設置格式后,我們可以更清楚地看到車輛、記錄位置時的時間戳、位置和距離:
{"vin": "wmwrxxxxxxxx54251","model": "Cooper Hardtop","location": {"lat": 30.4xxxxxx,"lon": -97.8xxxxxx,"accuracy_m": 22.505,"ts": 1473116792970},"distance": 13.552438042837085 }計算距離時,我們給出了以公里為單位的地球周長,因此返回的距離大約為 13.6 公里。
?總結?
在此例子中,我們混合使用了各種聯機工具來開發和執行 Azure Functions,但對于開發團隊而言,更現實的做法是通過 Git 存儲庫在本地開發和構建持續集成和部署。使用 WebJobs.Script 和 WebJobs SDK,可以在本地開發和運行 Azure Functions。
有關此操作的逐步指導,請訪問?bit.ly/2hhUCt2。
你還會發現,可以將許多不同的源配置為部署源。
Azure Functions 是 Azure 平臺家族的新成員。它是享受云 PaaS 實現優勢所必需的無服務器計算的關鍵組成部分。
Azure Functions 將重心從管理基礎結構轉移到了代碼價值上。雖然平臺、工具支持和語言支持會繼續發展,但它已支持許多語言,并且可以綁定到 CI/CD 管道。
有關詳細信息,請訪問?bit.ly/2h7lo4C。
如果還沒用過 Azure Functions,應該試一試。若要開始使用,請訪問?bit.ly/2glBJnC。
無服務器計算代表我們對云計算以及生成云應用程序的看法發生了根本的范例轉變。無服務器計算為開發者提供了一個完全抽象且可無限擴展的環境來執行代碼,并提供了一種只根據代碼執行次數付費的付款模型。
無服務器計算可以被視為平臺即服務 (PaaS) 的下個革命。它兌現了 PaaS 的承諾,即將應用程序基礎結構與代碼分離,并提供自動縮放功能。
無服務器的主要賣點是根據執行次數付費的定價模型(而不是根據代碼托管時間付費)和即時無限縮放功能。無服務器計算提供了完全托管式的計算體驗(沒有管理任務)、即時無限縮放功能(沒有縮放配置)和事件響應功能(實時處理)。
這樣一來,開發者可以開發新一類的應用程序。這類應用程序可以根據設計進行縮放,總體來說可復原性更高。
生成無服務器應用程序的開發者不僅使用 Azure Functions 等無服務器計算,還使用越來越多的完全托管式 Azure 或第三方服務,以生成有效的端到端無服務器解決方案。
如今,開發者可以很容易地就建立和運行完整的無服務器體系結構,利用很少的成本就能根據設計輕松進行縮放。同時,開發者將擺脫管理和監視基礎結構的包袱,將重心放在業務邏輯以及解決業務相關問題上,而無需處理運行代碼的基礎結構的維護問題。
無服務器旨在改變我們對生成應用程序的看法,影響將持續很長一段時間。請訪問?bit.ly/2hhP41O?和?bit.ly/2gcbPzr?加入討論。
請將功能請求提交至?bit.ly/2hhY3jq,并通過 Twitter 與我們聯系:@Azure?加井號標簽?
#AzureFunctions。????—Yochay Kiriaty
Darren Brust?是 Microsoft 的云解決方案架構師,大部分時間他都在協助企業客戶遷移到 Microsoft Azure。
空閑時,他可能會去看他三個孩子的體育比賽,也可能會在當地的 Austin 咖啡店喝許多咖啡。可以通過?dbrust@microsoft.com?或 Twitter (@DarrenBrust) 與他聯系。
Joseph Fultz??是 Microsoft 的云解決方案架構師。他負責與 Microsoft 客戶合作,共同開發體系結構,從而利用 Microsoft Azure 解決業務問題。
此前,Fultz 負責開發和生成 GM 的汽車共享程序 (mavendrive.com)。可以通過 Twitter (@JosephRFultz) 或電子郵件 (jofultz@microsoft.com) 與他聯系。
衷心感謝以下 Microsoft 技術專家對本文的審閱:?Fabio Calvacante
原文地址:https://msdn.microsoft.com/zh-cn/magazine/mt793269
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
內容轉載自公眾號
微軟中國MSDN 了解更多贊賞
總結
以上是生活随笔為你收集整理的利用 Azure Functions 实现无服务器体系结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Productivity Power T
- 下一篇: visual studio 2017发布