微服务架构已成为构建云原生应用程序的标准,微服务架构提供了使人信服的好处,包括可伸缩性,松散的服务耦合和独立部署,可是这种方法的成本很高,须要了解和熟练掌握分布式系统。为了使用全部开发人员可以使用任何语言和任何框架轻松地构建便携式微服务应用程序,不管是开发新项目仍是迁移现有代码html
Github: https://github.com/dapr/daprgit
Dapr是一种可移植的,事件驱动的,无服务器运行时,用于构建跨云和边缘的分布式应用程序。github
Distributed Application Runtime. An event-driven, portable runtime for building microservices on cloud and edge.web
其中提到了多语言和多开发者框架,我认为这是他选择的经过通讯共享信息,即 HTTP
和 GRPC
支持多语言等特性。微软想经过这个设定一个构建微服务应用的规则。从根本上确立你开发的每个应用的独立性。赋能每一个开发者,为了使Dapr对于不一样的语言更加方便,它还包括针对Go,Java,JavaScript,.NET和Python的语言特定的SDK。这些SDK经过类型化的语言API(而不是调用http / gRPC API)公开了Dapr构建块中的功能,例如保存状态,发布事件或建立actor。这使开发人员可使用他们选择的语言编写无状态和有状态功能以及参与者的组合。而且因为这些SDK共享Dapr运行时,您甚至能够得到跨语言的actor和功能支持!redis
Dapr还能够与任何开发人员框架集成。例如,在Dapr .NET SDK中,您将找到ASP.NET Core集成,该集成带来了可响应其余服务的发布/订阅事件的状态路由控制器,从而使ASP.NET Core成为构建微服务Web应用程序的更好框架。docker
不过须要注意的是Dapr目前正处于Alpha阶段, 今天刚发布了0.2版本。在v1.0稳定版本发布以前,建议不要用于生产环境。shell
下面进行一个 QuickStartapi
在Windows 上经过Powershell 安装:服务器
powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
而后把 c:\dapr 添加到环境变量 PATH
运行dapr命令,检查输出是否正常
C:\workshop\Github\dotnet-sdk>dapr --help架构
__
____/ /___ _____ _____
/ __ / __ '/ __ \/ ___/
/ /_/ / /_/ / /_/ / /
\__,_/\__,_/ .___/_/
/_/
======================================================
A serverless runtime for hyperscale, distributed systems
Usage:
dapr [command]
Available Commands:
help Help about any command
init Setup dapr in Kubernetes or Standalone modes
list List all dapr instances
publish publish an event to multiple consumers
run Launches dapr and your app side by side
send invoke a dapr app with an optional payload
stop Stops a running dapr instance and its associated app
uninstall removes a dapr installation
Flags:
-h, --help help for dapr
--version version for dapr
Use "dapr [command] --help" for more information about a command.
执行初始化(会启动 docker 容器)
dapr init
Making the jump to hyperspace...
Downloading binaries and setting up components
Success! Dapr is up and running
下载.NET SDk代码
https://github.com/dapr/dotnet-sdk ,里面有.NET Core的多个示例代码:
示例 | 描述 |
1. Actor | Demonstrates creating virtual actors that encapsulate code and state. Also see docs in this repo for a tutorial. |
2. ASP.NET Core | Demonstrates ASP.NET Core integration with Dapr by create Controllers and Routes. |
3. gRPC client | The gRPC client sample shows how to make Dapr calls to publish events, save state, get state and delete state using a gRPC client. |
咱们一块儿来看下ASP.NET Core的Demo;
例子中主 咱们使用 Dapr
的交互。Dapr经过 Runtime
/// <summary>
/// Sample showing Dapr integration with controller.
/// </summary>
[ApiController]
public class SampleController : ControllerBase
{
/// <summary>
/// Gets the account information as specified by the id.
/// </summary>
/// <param name="account">Account information for the id from Dapr state store.</param>
/// <returns>Account information.</returns>
[HttpGet("{account}")]
public ActionResult<Account> Get(StateEntry<Account> account)
{
if (account.Value is null)
{
return this.NotFound();
}
return account.Value;
}
/// <summary>
/// Method for depositing to account as psecified in transaction.
/// </summary>
/// <param name="transaction">Transaction info.</param>
/// <param name="stateClient">State client to interact with dapr runtime.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[Topic("deposit")]
[HttpPost("deposit")]
public async Task<ActionResult<Account>> Deposit(Transaction transaction, [FromServices] StateClient stateClient)
{
var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
state.Value ??= new Account() { Id = transaction.Id, };
state.Value.Balance += transaction.Amount;
await state.SaveAsync();
return state.Value;
}
/// <summary>
/// Method for withdrawing from account as specified in transaction.
/// </summary>
/// <param name="transaction">Transaction info.</param>
/// <param name="stateClient">State client to interact with dapr runtime.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[Topic("withdraw")]
[HttpPost("withdraw")]
public async Task<ActionResult<Account>> Withdraw(Transaction transaction, [FromServices] StateClient stateClient)
{
var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
if (state.Value == null)
{
return this.NotFound();
}
state.Value.Balance -= transaction.Amount;
await state.SaveAsync();
return state.Value;
}
}
这里重点是状态存储,即将 经过 StateClient 存储在 中,咱们经过状态转移在 里实现了 stateDaprDaprstateless。
演示Dapr的服务调用,在终端中切换到项目目录,而后使用dapr启动应用
C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample>dapr run --app-id routing --app-port 5000 dotnet run
Starting Dapr with id routing. HTTP Port: 61102. gRPC Port: 61103
You're up and running! Both Dapr and your app logs will appear here.
注意: 以上dapr run命令,经过app-id指定了应用的ID,经过app-port指定了应用的端口(webapi默认使用5000做为http端口),后跟dotnet run命名启动当前项目。可参考Dapr文档服务调用
后台运行的 CLI
命令,这里是前台打印的日志, 注意到 .NET App
在指定的 5000
端口运行,同时还有状态存储的 redis
在 6379
端口运行
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="starting Dapr Runtime -- version 0.2.0 -- commit c75b11
1-dirty"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="log level set to: info"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="standalone mode configured"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="dapr id: routing"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component messagebus (pubsub.redis)"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component statestore (state.redis)"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="application protocol: http. waiting on port 5000"
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Now listening on: http://localhost:5000
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Application started. Press Ctrl+C to shut down.
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Hosting environment: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Content root path: C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample
== DAPR == time="2019-11-16T18:33:31+08:00" level=info msg="application discovered on port 5000"
== DAPR == 2019-11-16 18:33:32.029764 I | redis: connecting to localhost:6379
== DAPR == 2019-11-16 18:33:32.036316 I | redis: connected to localhost:6379 (localAddr: [::1]:61164, remAddr:
[::1]:6379)
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s.
actor scan interval: 30s"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: starting connection attempt to placement se
rvice at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="local service entry announced"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 917
6.5164ms"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: established connection to placement service
at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: lock"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: update"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement tables updated"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: unlock"
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 2228.2998000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 2257.3405000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 67.46000000000001ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 68.0343ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 5.8247ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 6.268400000000001ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 4.5181000000000004ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 4.6208ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP == Received HTTP response after 20.2967ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP == End processing HTTP request after 20.691100000000002ms – OK
为了同时实现可移植性和与现有代码的轻松集成,Dapr经过http或gRPC提供了标准API。Dapr端口可从Dapr启动日志中获取,如如下日志表示Dapr公开的HTTP端口为61102(经过Dapr也可以使用gRPC方式进行服务调用)
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
咱们可经过如下地址来调用示例方法:
直接调用:GET http://localhost:5000/17
经过Dapr服务调用: GET http://localhost:61102/v1.0/invoke/routing/method/17
注意: Dapr的服务调用是有dapr sidecar来实现的,在被调用的服务中无需注入任何与dapr相关的代码。