1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        云原生 | .NET 5 with Dapr 初體驗

        共 9692字,需瀏覽 20分鐘

         ·

        2021-04-19 17:37





        分布式應(yīng)用運(yùn)行時Dapr目前已經(jīng)發(fā)布了1.1.0版本,阿里云也在積極地為Dapr貢獻(xiàn)代碼和落地實踐。作為一名開發(fā)者,自然也想玩一玩,看看Dapr帶來的新“視界到底是怎么樣的。

        1關(guān)于Dapr

        Dapr(Distributed Application Runtime)是一個開源、可移植、事件驅(qū)動的運(yùn)行時。它使開發(fā)人員能夠輕松地構(gòu)建運(yùn)行在云平臺和邊緣的彈性而微服務(wù)化的應(yīng)用程序,無論是無狀態(tài)還是有狀態(tài)。Dapr 讓開發(fā)人員能夠?qū)W⒂诰帉憳I(yè)務(wù)邏輯,而不是解決分布式系統(tǒng)的挑戰(zhàn),從而顯著提高生產(chǎn)力并減少開發(fā)時間。此外,Dapr 也降低了大部分中小型企業(yè)基于微服務(wù)架構(gòu)構(gòu)建現(xiàn)代云原生應(yīng)用的準(zhǔn)入門檻。

        Dapr 的核心構(gòu)建模塊 (或者說核心功能)如下:
        • 服務(wù)調(diào)用: 彈性服務(wù)與服務(wù)之間(service-to-service)調(diào)用可以在遠(yuǎn)程服務(wù)上啟用方法調(diào)用,包括重試,無論遠(yuǎn)程服務(wù)在受支持的托管環(huán)境中運(yùn)行在何處。

        • 狀態(tài)管理:通過對鍵 / 值對的狀態(tài)管理,可以很容易編寫長時間運(yùn)行、高可用性的有狀態(tài)服務(wù),以及同一個應(yīng)用中的無狀態(tài)服務(wù)。狀態(tài)存儲是可插入的,并且可以包括 Azure Cosmos 或 Redis,以及組件路線圖上的其他組件,如 AWS DynamoDB 等。

        • 在服務(wù)之間發(fā)布和訂閱消息(Pub/Sub):使事件驅(qū)動的架構(gòu)能夠簡化水平可擴(kuò)展性,并使其具備故障恢復(fù)能力。

        • 事件驅(qū)動的資源綁定:資源綁定和觸發(fā)器在事件驅(qū)動的架構(gòu)上進(jìn)一步構(gòu)建,通過從任何外部資源(如數(shù)據(jù)庫、隊列、文件系統(tǒng)、blob 存儲、webhooks 等)接收和發(fā)送事件,從而實現(xiàn)可擴(kuò)展性和彈性。例如,你的代碼可以由 Azure EventHub 服務(wù)上的消息觸發(fā),并將數(shù)據(jù)寫入 Azure CosmosDB。

        • 虛擬角色:無狀態(tài)和有狀態(tài)對象的模式,通過方法和狀態(tài)封裝使并發(fā)變得簡單。Dapr 在其虛擬角色(Virtual Actors)運(yùn)行時提供了許多功能,包括并發(fā)、狀態(tài)、角色激活 / 停用的生命周期管理以及用于喚醒角色的計時器和提醒。

        • 服務(wù)之間的分布式跟蹤:使用 W3C 跟蹤上下文(W3C Trace Context)標(biāo)準(zhǔn),輕松診斷和觀察生產(chǎn)中的服務(wù)間調(diào)用,并將事件推送到跟蹤和監(jiān)視系統(tǒng)。

        目前Dapr提供了如下所示的主流語言的SDK:

        更多關(guān)于Dapr的介紹不是本文的重點(diǎn),有興趣的讀者可以移步閱讀:

        (1)阿里巴巴的Dapr實踐與探索

        (2)Dapr是否會引領(lǐng)云原生中間件的未來

        (3)分布式運(yùn)行時 Dapr 知多少

        本文的試玩會主要集中在服務(wù)調(diào)用(service invocation)和 發(fā)布訂閱(pub / sub)上面,并且只會在入門小DEMO的程度,期望值過高的童鞋可以自行學(xué)習(xí) 或 繞道行走,畢竟我的時間也有限。

        2準(zhǔn)備工作

        一臺Linux虛擬機(jī)

        為了后面的DEMO,在VMware Workstation中準(zhǔn)備一個Linux虛擬機(jī)環(huán)境,這里我選擇的是CentOS 7.6。

        在此虛擬機(jī)中設(shè)定靜態(tài)IP地址(本示例為 192.168.2.100),關(guān)閉防火墻,設(shè)定主機(jī)名等一系列基本操作。

        安裝.NET 5 SDK

        這里我的DEMO是基于local-host部署模式(也可以選擇Kubernetes模式部署,但我沒時間弄),因此給Linux安裝一下.NET 5 SDK,命令如下:

        添加受信源sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm安裝.NET 5 SDKsudo yum install dotnet-sdk-5.0

        安裝Dapr CLI

        官網(wǎng)提示直接在Linux下執(zhí)行以下命令就可以將Dapr CLI下載到/usr/local/bin目錄下:

        wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash

        不過由于網(wǎng)絡(luò)原因,我選擇了直接下載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鏡像 & 運(yùn)行一波docker容器,如下圖所示:

        可以看到,dapr, redis, zipkin都已經(jīng)運(yùn)行起來了。

        為什么有redis?因為它會作為默認(rèn)的pub/sub中間件為dapr提供具體的實現(xiàn)能力。

        為什么會有zipkin?因為它會作為默認(rèn)的tracing中間件為我們提供鏈路追蹤的能力。

        OK,到此為止,本地的Dapr運(yùn)行時基礎(chǔ)環(huán)境已基本就緒。

        3.NET 5 應(yīng)用集成Dapr SDK

        準(zhǔn)備三個.NET WebAPI

        這里我們準(zhǔn)備了三個WebAPI項目,分別是訂單服務(wù)、購物車服務(wù) 以及 商品服務(wù)。

        具體的代碼可以去github上查看,github地址為:。

        為所有WebAPI項目添加集成

        為所有項目添加Dapr SDK的nuget包,這里是 Dapr.AspNetCore 組件。

        為所有WebAPI項目注冊Dapr

        在StartUp類中,對Dapr Client進(jìn)行注冊,這里的AddDapr背后的操作其實就是給IoC容器注入了一個單例的DaprClient對象。

        public void ConfigureServices(IServiceCollection services){    services.AddControllers()        .AddDapr();    ......}
        4服務(wù)調(diào)用示例

        這里假設(shè)CartService要和ProductService進(jìn)行通信,通過REST獲取商品數(shù)據(jù)。這里,就可以借助Dapr提供的服務(wù)間調(diào)用的功能進(jìn)行通信。其工作原理如下圖所示:

        這里使用的方式是通過DaprClient直接InvokeMethod進(jìn)行服務(wù)間的通信,傳遞了兩個重要的參數(shù),一個是依賴服務(wù)的app-id(根據(jù)你部署時設(shè)定的名字來寫),另一個是依賴接口的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; }}

        這里對應(yīng)ProductService的接口默認(rèn)返回一些假數(shù)據(jù):

        [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; }}

        然后,將這兩個服務(wù)發(fā)布到Linux服務(wù)器上,當(dāng)然,我們要通過dapr來部署,讓.net application和dapr sidecar形成一體。

        部署命令如下所示,可以看到我們既要為.net application指定端口,也要為dapr sidecar指定端口(這里主要為dapr指定了http端口,也可以為其指定grpc端口)。

        dapr run --app-id CartService --app-port 5000 --dapr-http-port 5005 -- dotnet EDT.EMall.Cart.API.dll --urls "http://*:5000" dapr run --app-id ProductService --app-port 5010 --dapr-http-port 5015 -- dotnet EDT.EMall.Product.API.dll --urls "http://*:5010"

        你會發(fā)現(xiàn),當(dāng)你run成功之后,會看到以下log,其中既有dapr的log,也有.net application的log,雖然他們是兩個應(yīng)用程序,但是你看到的它們是一體的。

        最后,通過swagger來測試一下,結(jié)果如下,成功進(jìn)行了服務(wù)調(diào)用。

        5消息發(fā)布及訂閱示例

        發(fā)布訂閱模式(Publish-Subscribe)是眾所周知且廣泛使用的消息模式。這里我們假設(shè)OrderService的某個接口完成后就發(fā)布一個消息,告知訂閱方有新訂單的事件產(chǎn)生。

        在Dapr中其工作原理如下圖所示:

        具體代碼示例如下,借助DaprClient的PublishEvent接口實現(xiàn)消息發(fā)布:

        [ApiController][Route("[controller]")]public class OrderController : ControllerBase{    private const string DaprPubSubName = "pubsub";
        private readonly ILogger<OrderController> _logger; private readonly DaprClient _daprClient;
        public OrderController(ILogger<OrderController> logger, DaprClient daprClient) { _logger = logger; _daprClient = daprClient; }
        [HttpPost] public async Task<Models.Order> Post(OrderDto orderDto) { _logger.LogInformation("[Begin] Create Order.");
        var order = new Models.Order() { // some mapping Id = orderDto.Id, ProductId = orderDto.ProductId, Count = orderDto.Count }; // some other logic for order
        var orderStockDto = new OrderStockDto() { ProductId = orderDto.ProductId, Count = orderDto.Count }; await _daprClient.PublishEventAsync(DaprPubSubName, "neworder", orderStockDto);
        _logger.LogInformation($"[End] Create Order Finished. Id : {orderStockDto.ProductId}, Count : {orderStockDto.Count}");
        return order; }}

        假設(shè)ProductService作為訂閱方,需要消費(fèi)這個事件,并扣減某個商品的庫存。而基于Dapr,我們需要對ProductService添加一點(diǎn)配置:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env){    ......    app.UseCloudEvents(); // 標(biāo)準(zhǔn)化的消息傳遞格式    app.UseEndpoints(endpoints =>    {        endpoints.MapSubscribeHandler(); // 訂閱消費(fèi)處理        ......    });}

        然后,在ProductService中添加一個方法/接口 來作為訂閱處理。

        具體代碼示例如下,需要注意的就是:

        (1)作為消息處理接口,需要指定為HttpPost方式。

        (2)需要指定Topic特性,并標(biāo)注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默認(rèn)的pubsub實現(xiàn)是基于Redis的,而在配置中為Redis設(shè)置的name就是 pubsub,因此對于我們?nèi)腴T的話,就不要去更改,或者和配置中的name保持一致。

        [root@dapr-lab-server ~]# cat ~/.dapr/components/pubsub.yamlapiVersion: dapr.io/v1alpha1kind: Componentmetadata:  name: pubsubspec:  type: pubsub.redis  metadata:  - name: redisHost    value: localhost:6379  - name: redisPassword    value: ""

        當(dāng)然,我們也可以將默認(rèn)的pubsub實現(xiàn)Redis換為熟悉的RabbitMQ。我們只需要更改上面的yml文件內(nèi)容如下:

        apiVersion: dapr.io/v1alpha1kind: Componentmetadata:  name: pubsub-rqspec:  type: pubsub.rabbitmq  version: v1  metadata:  - name: host    value: "amqp://localhost:5672"  - name: durable    value: true

        然后,將這兩個服務(wù)發(fā)布到Linux服務(wù)器上,當(dāng)然,我們要通過dapr來部署,讓.net application和dapr sidecar形成一體。

        dapr run --app-id OrderService --app-port 5020 --dapr-http-port 5025 -- dotnet EDT.EMall.Order.API.dll --urls "http://*:5020"dapr run --app-id ProductService --app-port 5010 --dapr-http-port 5015 -- dotnet EDT.EMall.Product.API.dll --urls "http://*:5010"

        run成功后,通過 dapr list 查看,可以看到三個服務(wù)都已經(jīng)啟動起來了,它們是三個由.net application + dapr sidecar 組成的“合體應(yīng)用

        最后,我們通過swagger來測試一下,測試結(jié)果如下圖所示:

        (1)OrderService:

        (2)ProductService:

        這里的99其實是假總庫存100 - 消息傳遞過來的商品數(shù)量得到的,具體可以參考代碼示例。

        6小結(jié)

        本文總結(jié)了我試玩Dapr的一些經(jīng)過,包括Dapr的Local環(huán)境搭建、.NET 5 Application與Dapr的集成 和 兩個具體場景的小DEMO(服務(wù)調(diào)用 和 Pub/Sub)。

        這里借助知乎上 iyacontrol 童鞋的評論(來源:https://www.zhihu.com/question/351298264),作為結(jié)尾:

        Dapr 本身是一種 Sidecar 模式(雖然Dapr也提供了SDK,但是個人認(rèn)為這并不是Dapr以后的發(fā)展方向)。Sidecar 模式的意義在于, 解耦了基礎(chǔ)設(shè)施和核心業(yè)務(wù)。

        簡單來看,Dapr的意義在于:

        • 對于小公司,甚至沒有基礎(chǔ)架構(gòu)和中間件團(tuán)隊的公司,Dapr 提供了開箱即用的基礎(chǔ)設(shè)施功能,可以讓小公司輕松構(gòu)建彈性,分布式應(yīng)用。

        • 對于中等單位,具備一定的基礎(chǔ)架構(gòu)能力,在使用Dapr的過程中,可能Dapr并不能完全滿足需求,那么也可以在Dapr框架體系下,花費(fèi)較小的成本進(jìn)行自定義擴(kuò)展。

        • 對于大公司,Dapr 提供了一種思路。相信基礎(chǔ)架構(gòu)團(tuán)隊會越來越傾向于通過交付Sidecar的形式來提供基礎(chǔ)設(shè)施

        長遠(yuǎn)來看,Dapr背后的架構(gòu)模式是符合未來架構(gòu)趨勢(多運(yùn)行時架構(gòu))和云原生發(fā)展趨勢的。


        代碼示例

        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


        往期精彩回顧




        【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程 ★★★

        .NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃

        【.NET Core微服務(wù)實戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引

        Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

        .NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了

        10個小技巧助您寫出高性能的ASP.NET Core代碼

        用abp vNext快速開發(fā)Quartz.NET定時任務(wù)管理界面

        在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實現(xiàn)作業(yè)調(diào)度

        現(xiàn)身說法:實際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

        關(guān)于C#異步編程你應(yīng)該了解的幾點(diǎn)建議

        C#異步編程看這篇就夠了


        瀏覽 64
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久邪恶| 伊人成人在线视频观看 | 特级婬片A片AAA毛片哪里有 | 天天插一插 | 午夜成人大片 | 国产又粗又猛又大的视频 | 国产AV女主播 | 中国老太卖婬HD视频 | 黄色电影网站免费观看 | 69AV影院 |