云原生 | .NET 5 with Dapr 初体验
【Dapr】|?總結/Edison Zhou
分布式應用運行時Dapr目前已經發布了1.1.0版本,阿里云也在積極地為Dapr貢獻代碼和落地實踐。作為一名開發者,自然也想玩一玩,看看Dapr帶來的新“視”界到底是怎么樣的。
1關于Dapr
Dapr(Distributed?Application?Runtime)是一個開源、可移植、事件驅動的運行時。它使開發人員能夠輕松地構建運行在云平臺和邊緣的彈性而微服務化的應用程序,無論是無狀態還是有狀態。Dapr 讓開發人員能夠專注于編寫業務邏輯,而不是解決分布式系統的挑戰,從而顯著提高生產力并減少開發時間。此外,Dapr 也降低了大部分中小型企業基于微服務架構構建現代云原生應用的準入門檻。
Dapr 的核心構建模塊 (或者說核心功能)如下:
服務調用: 彈性服務與服務之間(service-to-service)調用可以在遠程服務上啟用方法調用,包括重試,無論遠程服務在受支持的托管環境中運行在何處。
狀態管理:通過對鍵 / 值對的狀態管理,可以很容易編寫長時間運行、高可用性的有狀態服務,以及同一個應用中的無狀態服務。狀態存儲是可插入的,并且可以包括 Azure Cosmos 或 Redis,以及組件路線圖上的其他組件,如 AWS DynamoDB 等。
在服務之間發布和訂閱消息(Pub/Sub):使事件驅動的架構能夠簡化水平可擴展性,并使其具備故障恢復能力。
事件驅動的資源綁定:資源綁定和觸發器在事件驅動的架構上進一步構建,通過從任何外部資源(如數據庫、隊列、文件系統、blob 存儲、webhooks 等)接收和發送事件,從而實現可擴展性和彈性。例如,你的代碼可以由 Azure EventHub 服務上的消息觸發,并將數據寫入 Azure CosmosDB。
虛擬角色:無狀態和有狀態對象的模式,通過方法和狀態封裝使并發變得簡單。Dapr 在其虛擬角色(Virtual Actors)運行時提供了許多功能,包括并發、狀態、角色激活 / 停用的生命周期管理以及用于喚醒角色的計時器和提醒。
服務之間的分布式跟蹤:使用 W3C 跟蹤上下文(W3C Trace Context)標準,輕松診斷和觀察生產中的服務間調用,并將事件推送到跟蹤和監視系統。
目前Dapr提供了如下所示的主流語言的SDK:
更多關于Dapr的介紹不是本文的重點,有興趣的讀者可以移步閱讀:
(1)阿里巴巴的Dapr實踐與探索
(2)Dapr是否會引領云原生中間件的未來
(3)分布式運行時?Dapr 知多少
本文的試玩會主要集中在服務調用(service invocation)和 發布訂閱(pub / sub)上面,并且只會在入門小DEMO的程度,期望值過高的童鞋可以自行學習 或 繞道行走,畢竟我的時間也有限。
2準備工作
一臺Linux虛擬機
為了后面的DEMO,在VMware Workstation中準備一個Linux虛擬機環境,這里我選擇的是CentOS 7.6。
在此虛擬機中設定靜態IP地址(本示例為?192.168.2.100),關閉防火墻,設定主機名等一系列基本操作。
安裝.NET 5 SDK
這里我的DEMO是基于local-host部署模式(也可以選擇Kubernetes模式部署,但我沒時間弄),因此給Linux安裝一下.NET 5 SDK,命令如下:
安裝Dapr CLI
官網提示直接在Linux下執行以下命令就可以將Dapr CLI下載到/usr/local/bin目錄下:
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash不過由于網絡原因,我選擇了直接下載Release來安裝:
(1)到github上下載1.1.0的release壓縮包(dapr_linux_amd64.tar.gz),并將其傳到Linux中。
(2)解壓該壓縮包,并將解壓后的目錄移動到/usr/local/bin目錄下:
tar -zvxf dapr_linux_amd64.tar.gz(3)通過輸入 dapr 來驗證是否安裝成功:
此外,也可以通過 dapr --version 查看Dapr版本:
CLI version: 1.1.0 Runtime version: 1.1.0初始化Dapr
安裝好Dapr CLI之后,就可以在Linux上初始化Dapr了,命令如下:
dapr init這個命令會幫你做一些列的事情,包括但不限于?拉取一波docker鏡像 &?運行一波docker容器,如下圖所示:
可以看到,dapr, redis, zipkin都已經運行起來了。
為什么有redis?因為它會作為默認的pub/sub中間件為dapr提供具體的實現能力。
為什么會有zipkin?因為它會作為默認的tracing中間件為我們提供鏈路追蹤的能力。
OK,到此為止,本地的Dapr運行時基礎環境已基本就緒。
3.NET 5?應用集成Dapr SDK
準備三個.NET WebAPI
這里我們準備了三個WebAPI項目,分別是訂單服務、購物車服務 以及 商品服務。
具體的代碼可以去github上查看,github地址為:。
為所有WebAPI項目添加集成
為所有項目添加Dapr SDK的nuget包,這里是 Dapr.AspNetCore 組件。
為所有WebAPI項目注冊Dapr
在StartUp類中,對Dapr Client進行注冊,這里的AddDapr背后的操作其實就是給IoC容器注入了一個單例的DaprClient對象。
public void ConfigureServices(IServiceCollection services) {services.AddControllers().AddDapr();...... }4服務調用示例
這里假設CartService要和ProductService進行通信,通過REST獲取商品數據。這里,就可以借助Dapr提供的服務間調用的功能進行通信。其工作原理如下圖所示:
這里使用的方式是通過DaprClient直接InvokeMethod進行服務間的通信,傳遞了兩個重要的參數,一個是依賴服務的app-id(根據你部署時設定的名字來寫),另一個是依賴接口的route。
具體如下代碼所示:
[ApiController] [Route("[controller]")] public class CartController : ControllerBase {private readonly ILogger<CartController> _logger;private readonly DaprClient _daprClient;public CartController(ILogger<CartController> logger, DaprClient daprClient){_logger = logger;_daprClient = daprClient;}[HttpGet]public async Task<IEnumerable<SKU>> Get(){_logger.LogInformation("[Begin] Query product data from Product Service");var products = await _daprClient.InvokeMethodAsync<IEnumerable<SKU>>(HttpMethod.Get, "ProductService", "Product");_logger.LogInformation($"[End] Query product data from Product Service, data : {products.ToArray().ToString()}");return products;} }這里對應ProductService的接口默認返回一些假數據:
[ApiController] [Route("[controller]")] public class ProductController : ControllerBase {private static readonly string[] FakeProducts = new[]{"SKU1", "SKU2", "SKU3", "SKU4", "SKU5", "SKU6", "SKU7", "SKU8", "SKU9", "SKU10"};......[HttpGet]public IEnumerable<SKU> Get(){_logger.LogInformation("[Begin] Query product data.");var rng = new Random();var result = Enumerable.Range(1, 5).Select(index => new SKU{Date = DateTime.Now.AddDays(index),Index = rng.Next(1, 100),Summary = FakeProducts[rng.Next(FakeProducts.Length)]}).ToArray();_logger.LogInformation("[End] Query product data.");return result;} }然后,將這兩個服務發布到Linux服務器上,當然,我們要通過dapr來部署,讓.net application和dapr sidecar形成一體。
部署命令如下所示,可以看到我們既要為.net application指定端口,也要為dapr sidecar指定端口(這里主要為dapr指定了http端口,也可以為其指定grpc端口)。
你會發現,當你run成功之后,會看到以下log,其中既有dapr的log,也有.net application的log,雖然他們是兩個應用程序,但是你看到的它們是一體的。
最后,通過swagger來測試一下,結果如下,成功進行了服務調用。
5消息發布及訂閱示例
發布訂閱模式(Publish-Subscribe)是眾所周知且廣泛使用的消息模式。這里我們假設OrderService的某個接口完成后就發布一個消息,告知訂閱方有新訂單的事件產生。
在Dapr中其工作原理如下圖所示:
具體代碼示例如下,借助DaprClient的PublishEvent接口實現消息發布:
假設ProductService作為訂閱方,需要消費這個事件,并扣減某個商品的庫存。而基于Dapr,我們需要對ProductService添加一點配置:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {......app.UseCloudEvents(); // 標準化的消息傳遞格式app.UseEndpoints(endpoints =>{endpoints.MapSubscribeHandler(); // 訂閱消費處理......}); }然后,在ProductService中添加一個方法/接口 來作為訂閱處理。
具體代碼示例如下,需要注意的就是:
(1)作為消息處理接口,需要指定為HttpPost方式。
(2)需要指定Topic特性,并標注pubsubname 和?事件名。
private const string DaprPubSubName = "pubsub";[HttpPost] [Topic(DaprPubSubName, "neworder")] public Models.Product SubProductStock(OrderStockDto orderStockDto) {_logger.LogInformation($"[Begin] Sub Product Stock, Stock Need : {orderStockDto.Count}.");var product = _productService.GetProductById(orderStockDto.ProductId);if (orderStockDto.Count < 0 || orderStockDto.Count > product.Stock){throw new InvalidOperationException("Invalid Product Count!");}product.Stock = product.Stock - orderStockDto.Count;_productService.SaveProduct(product);_logger.LogInformation($"[End] Sub Product Stock Finished, Stock Now : {product.Stock}.");return product; }這里的DaprPubSubName是pubsub,這是因為Dapr默認的pubsub實現是基于Redis的,而在配置中為Redis設置的name就是 pubsub,因此對于我們入門的話,就不要去更改,或者和配置中的name保持一致。
[root@dapr-lab-server ~]# cat ~/.dapr/components/pubsub.yaml apiVersion: dapr.io/v1alpha1 kind: Component metadata:name: pubsub spec:type: pubsub.redismetadata:- name: redisHostvalue: localhost:6379- name: redisPasswordvalue: ""當然,我們也可以將默認的pubsub實現Redis換為熟悉的RabbitMQ。我們只需要更改上面的yml文件內容如下:
然后,將這兩個服務發布到Linux服務器上,當然,我們要通過dapr來部署,讓.net application和dapr sidecar形成一體。
run成功后,通過 dapr list 查看,可以看到三個服務都已經啟動起來了,它們是三個由.net application + dapr sidecar 組成的“合體應用”。
最后,我們通過swagger來測試一下,測試結果如下圖所示:
(1)OrderService:
(2)ProductService:
這里的99其實是假總庫存100 -?消息傳遞過來的商品數量得到的,具體可以參考代碼示例。
6小結
本文總結了我試玩Dapr的一些經過,包括Dapr的Local環境搭建、.NET 5 Application與Dapr的集成 和 兩個具體場景的小DEMO(服務調用 和 Pub/Sub)。
這里借助知乎上?iyacontrol?童鞋的評論(來源:https://www.zhihu.com/question/351298264),作為結尾:
Dapr 本身是一種 Sidecar 模式(雖然Dapr也提供了SDK,但是個人認為這并不是Dapr以后的發展方向)。Sidecar 模式的意義在于, 解耦了基礎設施和核心業務。
簡單來看,Dapr的意義在于:
對于小公司,甚至沒有基礎架構和中間件團隊的公司,Dapr 提供了開箱即用的基礎設施功能,可以讓小公司輕松構建彈性,分布式應用。
對于中等單位,具備一定的基礎架構能力,在使用Dapr的過程中,可能Dapr并不能完全滿足需求,那么也可以在Dapr框架體系下,花費較小的成本進行自定義擴展。
對于大公司,Dapr 提供了一種思路。相信基礎架構團隊會越來越傾向于通過交付Sidecar的形式來提供基礎設施。
長遠來看,Dapr背后的架構模式是符合未來架構趨勢(多運行時架構)和云原生發展趨勢的。
代碼示例
github:https://github.com/EdisonChou/EDT.Dapr.Sample
參考資料
Microsoft,《Dapr for .NET Developer》: https://docs.microsoft.com/zh-cn/dotnet/architecture/dapr-for-net-developers
Tony,《Dapr公開課》:https://www.bilibili.com/video/BV1Fb4y197fT
相關文章:
Dapr能否引領云原生中間件的未來?
云原生 | 阿里巴巴的Dapr實踐與探索
Dapr 可視化指南
Dapr 知多少 | 分布式應用運行時
Dapr 正式發布 1.0
Dapr 交通流量控制示例
Dapr是如何簡化微服務的開發和部署
微軟開源微服務運行時Dapr,賦能云原生應用開發
Dapr微服務應用開發系列0:概述
Dapr微服務應用開發系列1:環境配置
Dapr微服務應用開發系列2:Hello World與SDK初接觸
Dapr微服務應用開發系列3:服務調用構件塊
Dapr微服務應用開發系列4:狀態管理構件塊
Dapr微服務應用開發系列5:發布訂閱構建塊
Windows環境下Dapr入門
通過Dapr實現一個簡單的基于.net的微服務電商系統
總結
以上是生活随笔為你收集整理的云原生 | .NET 5 with Dapr 初体验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NET问答: C# 中是否有最高效的方式
- 下一篇: 我敢说,这是最全的常用设计模式汇总