本文將採用本地部署的方式。mysql
打開 Windows PowerShell 或 cmd ,運行如下命令以安裝 Dapr CLI
,並添加安裝路徑到系統環境變量中。git
powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
這裏安裝可能會失敗。若是失敗能夠手動安裝。github
dapr_windows_amd64.zip
C:\dapr
中Docker 啓動 Mysqlgolang
docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=123456 -d mysql
在 Windows PowerShell 或 cmd 中使用命令 dapr init
以安裝 Dapr。sql
同時能夠在 Docker 中查看 Dapr 容器。docker
至此,一個本地 Dapr 服務搭建完成。shell
Asp.Net Core
搭建 ProductService 服務ProductService 提供兩個服務json
使用 ASP.Net Core
建立 ProductService ,具體參考源碼windows
Dapr 啓動 ProductServiceapp
dapr run --app-id productService --app-port 5000 dotnet run
獲取全部產品集合,使用 curl 命令
curl -X GET http://localhost:5000/getlist
或者
curl -X GET http://localhost:54680/v1.0/invoke/productService/method/getlist
添加一個產品
curl -X POST https://localhost:5001/product -H "Content-Type: application/json" -d "{ \"id\": \"14a3611d-1561-455f-9c72-381eed2f6ee3\" }"
重點,經過 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 建立完成。
建立 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 }
使用 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
建立 StorageServiceStartup.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(); }); }
添加 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; } } }
使用 Dapr cli 啓用 StorageService 服務
dapr run --app-id storageService --app-port 5003 dotnet run
使用 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 。