Dapr 運用

Dapr 運用

  • 前置條件
    • Docker
    • Win10

Dapr 部署

本文將採用本地部署的方式。mysql

安裝 Dapr CLI

打開 Windows PowerShell 或 cmd ,運行如下命令以安裝 Dapr CLI,並添加安裝路徑到系統環境變量中。git

powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"

這裏安裝可能會失敗。若是失敗能夠手動安裝。github

  • 打開 Dapr 發佈頁面下載 dapr_windows_amd64.zip
  • 解壓文件 zip 文件
  • 把解壓後的文件拷貝到 C:\dapr

安裝 MySql

Docker 啓動 Mysqlgolang

docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=123456 -d mysql

使用 Dapr CLI 安裝 Darp runtime

在 Windows PowerShell 或 cmd 中使用命令 dapr init 以安裝 Dapr。sql

同時能夠在 Docker 中查看 Dapr 容器。docker

至此,一個本地 Dapr 服務搭建完成。shell

使用 Asp.Net Core 搭建 ProductService 服務

ProductService 提供兩個服務json

  • 獲取全部產品集合
  • 添加產品
  1. 使用 ASP.Net Core 建立 ProductService ,具體參考源碼windows

  2. Dapr 啓動 ProductServiceapp

    dapr run --app-id productService --app-port 5000 dotnet run
  3. 獲取全部產品集合,使用 curl 命令

    curl -X GET http://localhost:5000/getlist

    或者

    curl -X GET http://localhost:54680/v1.0/invoke/productService/method/getlist
  4. 添加一個產品

    curl -X POST https://localhost:5001/product -H "Content-Type: application/json" -d "{ \"id\": \"14a3611d-1561-455f-9c72-381eed2f6ee3\" }"
  5. 重點,經過 Dapr 添加一個產品,先看添加產品的代碼

    /// <summary>
     /// 建立產品
     /// </summary>
     /// <param name="productCreate">產品建立模型</param>
     /// <returns></returns>
     [Topic("product")]
     [HttpPost("product")]
     public async Task<bool> CreateProduct(ProductCreate productCreate)
     {
         _productContext.Products.Add(new Product
         {
             ProductID = productCreate.ID
         });
         return await _productContext.SaveChangesAsync() == 1;
     }
    • 使用 Dapr cli 發佈事件

      dapr invoke -a productService -m product -p "{\"id\":\"b1ccf14a-408a-428e-b0f0-06b97cbe4135\"}"

      輸出爲:

      true
      App invoked successfully
    • 使用 curl 命令直接請求 ProductService 地址

      curl -X POST http://localhost:5000/product -H "Content-Type: application/json" -d "{ \"id\": \"14a3611d-1561-455f-9c72-381eed2f64e3\" }"

      輸出爲:

      true
    • 使用 curl 命令經過 Dapr runtime

      curl -X POST http://localhost:54680/v1.0/invoke/productService/method/product -H "Content-Type: application/json" -d "{ \"id\": \"14a3611d-1561-455f-9c72-381eed2f54e3\" }"

      輸出爲:

      true

注意:

  • Dapr 使用 App 端口號應與服務端口號相同,例如:ASP.Net Core 服務端口號爲5000,則在使用 Dapr 託管應用程序時的端口號也應使用 5000

至此, ProductService 建立完成。

使用 Golang 建立 gRPC Server

  1. 建立 Server

    package main
    
    import (
        "context"
        "fmt"
        "log"
        "net"
    
        "github.com/golang/protobuf/ptypes/any"
        "github.com/golang/protobuf/ptypes/empty"
    
        pb "github.com/dapr/go-sdk/daprclient"
        "google.golang.org/grpc"
    )
    
    // server is our user app
    type server struct {
    }
    
    func main() {
        // create listiner
        lis, err := net.Listen("tcp", ":4000")
        if err != nil {
            log.Fatalf("failed to listen: %v", err)
        }
    
        // create grpc server
        s := grpc.NewServer()
        pb.RegisterDaprClientServer(s, &server{})
    
        fmt.Println("Client starting...")
    
        // and start...
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }
    
    // Sample method to invoke
    func (s *server) MyMethod() string {
        return "Hi there!"
    }
    
    // This method gets invoked when a remote service has called the app through Dapr
    // The payload carries a Method to identify the method, a set of metadata properties and an optional payload
    func (s *server) OnInvoke(ctx context.Context, in *pb.InvokeEnvelope) (*any.Any, error) {
        var response string
    
        fmt.Println(fmt.Sprintf("Got invoked with: %s", string(in.Data.Value)))
    
        switch in.Method {
        case "MyMethod":
            response = s.MyMethod()
        }
        return &any.Any{
            Value: []byte(response),
        }, nil
    }
    
    // Dapr will call this method to get the list of topics the app wants to subscribe to. In this example, we are telling Dapr
    // To subscribe to a topic named TopicA
    func (s *server) GetTopicSubscriptions(ctx context.Context, in *empty.Empty) (*pb.GetTopicSubscriptionsEnvelope, error) {
        return &pb.GetTopicSubscriptionsEnvelope{
            Topics: []string{"TopicA"},
        }, nil
    }
    
    // Dapper will call this method to get the list of bindings the app will get invoked by. In this example, we are telling Dapr
    // To invoke our app with a binding named storage
    func (s *server) GetBindingsSubscriptions(ctx context.Context, in *empty.Empty) (*pb.GetBindingsSubscriptionsEnvelope, error) {
        return &pb.GetBindingsSubscriptionsEnvelope{
            Bindings: []string{"storage"},
        }, nil
    }
    
    // This method gets invoked every time a new event is fired from a registerd binding. The message carries the binding name, a payload and optional metadata
    func (s *server) OnBindingEvent(ctx context.Context, in *pb.BindingEventEnvelope) (*pb.BindingResponseEnvelope, error) {
        fmt.Println("Invoked from binding")
        return &pb.BindingResponseEnvelope{}, nil
    }
    
    // This method is fired whenever a message has been published to a topic that has been subscribed. Dapr sends published messages in a CloudEvents 0.3 envelope.
    func (s *server) OnTopicEvent(ctx context.Context, in *pb.CloudEventEnvelope) (*empty.Empty, error) {
        fmt.Println("Topic message arrived")
        return &empty.Empty{}, nil
    }
  2. 使用 Dapr 命令啓動 StorageService

    dapr run --app-id client --protocol grpc --app-port 4000 go run main.go

注意:

  • Dapr 使用 App 端口號應與服務端口號相同,使用 --protocal grpc 指定通信協議爲 grpc 。此外,OnInvoke 中的 switch 方法用於調用者路由。

使用 ASP.NET Core 建立 StorageService

  1. 使用 NuGet 獲取程序管理包控制檯安裝如下包
    • Dapr.AspNetCore
    • Dapr.Client.Grpc
    • Grpc.AspNetCore
    • Grpc.Net.Client
    • Grpc.Tools
  2. Startup.cs 文件中修改代碼以下:

    /// <summary>
    /// This method gets called by the runtime. Use this method to add services to the container.
    /// </summary>
    /// <param name="services">Services.</param>
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().AddDapr();
        services.AddDbContextPool<StorageContext>(options => { options.UseMySql(Configuration.GetConnectionString("MysqlConnection")); });
    }
    /// <summary>
    /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    /// </summary>
    /// <param name="app">app.</param>
    /// <param name="env">env.</param>
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseRouting();
        app.UseCloudEvents();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapSubscribeHandler();
            endpoints.MapControllers();
        });
    }
  3. 添加 StorageController.cs 文件,內容以下

    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using Dapr.Client.Grpc;
    using Google.Protobuf;
    using Grpc.Net.Client;
    using Microsoft.AspNetCore.Mvc;
    using StorageService.Api.Entities;
    
    namespace StorageService.Api.Controllers
    {
        [ApiController]
        public class StorageController : ControllerBase
        {
            private readonly StorageContext _storageContext;
    
            public StorageController(StorageContext storageContext)
            {
                _storageContext = storageContext;
            }
    
            /// <summary>
            /// 初始化倉庫.
            /// </summary>
            /// <returns>是否成功.</returns>
            [HttpGet("InitialStorage")]
            public async Task<bool> InitialStorage()
            {
                string defaultPort = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "54681";
    
                // Set correct switch to make insecure gRPC service calls. This switch must be set before creating the GrpcChannel.
                AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
                // Create Client
                string daprUri = $"http://127.0.0.1:{defaultPort}";
                GrpcChannel channel = GrpcChannel.ForAddress(daprUri);
                var client = new Dapr.Client.Grpc.Dapr.DaprClient(channel);
                Console.WriteLine(daprUri);
    
                InvokeServiceResponseEnvelope result = await client.InvokeServiceAsync(new InvokeServiceEnvelope
                {
                    Method = "MyMethod",
                    Id = "client",
                    Data = new Google.Protobuf.WellKnownTypes.Any
                    {
                        Value = ByteString.CopyFromUtf8("Hello ProductService")
                    }
                });
                Console.WriteLine("this is call result:" + result.Data.Value.ToStringUtf8());
                //var productResult = result.Data.Unpack<ProductList.V1.ProductList>();
                //Console.WriteLine("this is call result:" + productResult.Results.FirstOrDefault());
                return true;
            }
    
            /// <summary>
            /// 修改庫存
            /// </summary>
            /// <param name="storage"></param>
            /// <returns></returns>
            [HttpPut("Reduce")]
            public bool Reduce(Storage storage)
            {
                Storage storageFromDb = _storageContext.Storage.FirstOrDefault(q => q.ProductID.Equals(storage.ProductID));
                if (storageFromDb == null)
                {
                    return false;
                }
    
                if (storageFromDb.Amount <= storage.Amount)
                {
                    return false;
                }
    
                storageFromDb.Amount -= storage.Amount;
                return true;
            }
        }
    }
  4. 使用 Dapr cli 啓用 StorageService 服務

    dapr run --app-id storageService --app-port 5003 dotnet run
  5. 使用 curl 命令訪問 StorageService InitialStorage 方法

    curl -X GET http://localhost:56349/v1.0/invoke/storageService/method/InitialStorage

    輸入

    true

    其中打印信息爲:

    this is call result:Hi there!

注意:

  • Dapr 使用 App 端口號應與服務端口號相同,例如:ASP.Net Core 服務端口號爲5003,則在使用 Dapr 託管應用程序時的端口號也應使用 5003,在 Client.InvokeServiceAsync 中的 Id 指被調用方的 App-Id ,Method 指被調用方方法名稱。參考 Go Server 中 OnInvoke 方法的 Switch 。

源碼地址

相關文章
相關標籤/搜索