前置条件: 《Dapr 运用》


改造 ProductService 以提供 gRPC 服务

  1. 从 NuGet 或程序包管理控制台安装 gRPC 服务必须的包

    • Grpc.AspNetCore
  2. 配置 Http/2

    • gRPC 服务需要 Http/2 协议

      public static IHostBuilder CreateHostBuilder(string[] args)
      {
      return Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
      webBuilder.ConfigureKestrel(options =>
      {
      options.Listen(IPAddress.Loopback, 50![](https://img2018.cnblogs.com/blog/757544/201912/757544-20191218205830077-211023565.png)

01, listenOptions =>

{

listenOptions.Protocols = HttpProtocols.Http2;

});

});

webBuilder.UseStartup();

});

}

```

  1. 新建了 product.proto 以定义 GRPC 服务,它需要完成的内容是返回所有产品集合,当然目前产品内容只有一个 ID

    • 定义产品 proto

      syntax = "proto3";
      
      package productlist.v1;
      
      option csharp_namespace = "ProductList.V1";
      
      service ProductRPCService{
      rpc GetAllProducts(ProductListRequest) returns(ProductList);
      } message ProductListRequest{ } message ProductList {
      repeated Product results = 1;
      } message Product {
      string ID=1;
      }

      说明

      • 定义产品列表 gRPC 服务,得益于宇宙第一 IDE Visual Studio ,只要添加 Grpc.Tools 包就可以自动生成 gRPC 所需的代码,这里不再需要手动去添加 Grpc.Tools ,官方提供的 Grpc.AspNetCore 中已经集成了
      • 定义了一个服务 ProductRPCService
      • 定义了一个函数 ProductRPCService
      • 定义了一个请求构造 ProductListRequest ,内容为空
      • 定义了一个请求返回构造 ProductList ,使用 repeated 表明返回数据是集合
      • 定义了一个数据集合中的一个对象 Product
    • 添加 ProductListService 文件,内容如下

          public class ProductListService : ProductRPCService.ProductRPCServiceBase
      {
      private readonly ProductContext _productContext; public ProductListService(ProductContext productContext)
      {
      _productContext = productContext;
      } public override async Task<ProductList.V1.ProductList> GetAllProducts(ProductListRequest request, ServerCallContext context)
      {
      IList<Product> results = await _productContext.Products.ToListAsync();
      var productList = new ProductList.V1.ProductList();
      foreach (Product item in results)
      {
      productList.Results.Add(new ProductList.V1.Product
      {
      ID = item.ProductID.ToString()
      });
      } return productList;
      }
      }
  2. 在 Startup.cs 修改代码如下

    public void ConfigureServices(IServiceCollection services)
    {
    //启用 gRPC 服务
    services.AddGrpc();
    services.AddTransient<ProductListService>();
    ...
    }

    这里的 services.AddTransient(); 的原因是在 Dapr 中需要使用构造器注入,以完成 GetAllProducts(...) 函数的调用

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    } app.UseRouting(); app.UseEndpoints(endpoints =>
    {
    ... //添加 gRPC 到路由管道中
    endpoints.MapGrpcService<DaprClientService>();
    });
    }

    这里添加的代码的含义分别是启用 gRPC 服务和添加 gRPC 路由。得益于 ASP.NET Core 中间件的优秀设计,ASP.NET Core 可同时支持 Http 服务。

  3. 添加 daprclient.proto 文件以生成 Dapr Grpc 服务,daprclient.proto 内容如下

    syntax = "proto3";
    
    package daprclient;
    
    import "google/protobuf/any.proto";
    import "google/protobuf/empty.proto";
    import "google/protobuf/duration.proto"; option java_outer_classname = "DaprClientProtos";
    option java_package = "io.dapr"; // User Code definitions
    service DaprClient {
    rpc OnInvoke (InvokeEnvelope) returns (google.protobuf.Any) {}
    rpc GetTopicSubscriptions(google.protobuf.Empty) returns (GetTopicSubscriptionsEnvelope) {}
    rpc GetBindingsSubscriptions(google.protobuf.Empty) returns (GetBindingsSubscriptionsEnvelope) {}
    rpc OnBindingEvent(BindingEventEnvelope) returns (BindingResponseEnvelope) {}
    rpc OnTopicEvent(CloudEventEnvelope) returns (google.protobuf.Empty) {}
    } message CloudEventEnvelope {
    string id = 1;
    string source = 2;
    string type = 3;
    string specVersion = 4;
    string dataContentType = 5;
    string topic = 6;
    google.protobuf.Any data = 7;
    } message BindingEventEnvelope {
    string name = 1;
    google.protobuf.Any data = 2;
    map<string,string> metadata = 3;
    } message BindingResponseEnvelope {
    google.protobuf.Any data = 1;
    repeated string to = 2;
    repeated State state = 3;
    string concurrency = 4;
    } message InvokeEnvelope {
    string method = 1;
    google.protobuf.Any data = 2;
    map<string,string> metadata = 3;
    } message GetTopicSubscriptionsEnvelope {
    repeated string topics = 1;
    } message GetBindingsSubscriptionsEnvelope {
    repeated string bindings = 1;
    } message State {
    string key = 1;
    google.protobuf.Any value = 2;
    string etag = 3;
    map<string,string> metadata = 4;
    StateOptions options = 5;
    } message StateOptions {
    string concurrency = 1;
    string consistency = 2;
    RetryPolicy retryPolicy = 3;
    } message RetryPolicy {
    int32 threshold = 1;
    string pattern = 2;
    google.protobuf.Duration interval = 3;
    }

    说明

    • 此文件为官方提供,Dapr 0.3 版本之前提供的已经生成好的代码,现在看源码可以看出已经改为提供 proto 文件了,这里我认为提供 proto 文件比较合理
    • 此文件定义了5个函数,此文主要讲的就是 OnInvoke() 函数
    • OnInvoke() 请求构造为 InvokeEnvelope
      • method 提供调用方法名称
      • data 请求数据
      • metadata 额外数据,此处使用键值对形式体现
  4. 创建 DaprClientService.cs 文件,此文件用于终结点路由,内容为

    public class DaprClientService : DaprClient.DaprClientBase
    {
    private readonly ProductListService _productListService; /// <summary>
    /// Initializes a new instance of the <see cref="ProductService" /> class.
    /// </summary>
    /// <param name="productListService"></param>
    public DaprClientService(ProductListService productListService)
    {
    _productListService = productListService;
    } public override async Task<Any> OnInvoke(InvokeEnvelope request, ServerCallContext context)
    {
    switch (request.Method)
    {
    case "GetAllProducts":
    ProductListRequest productListRequest = ProductListRequest.Parser.ParseFrom(request.Data.Value);
    ProductList.V1.ProductList productsList = await _productListService.GetAllProducts(productListRequest, context);
    return Any.Pack(productsList);
    } return null;
    }
    }

    说明

    • 使用构造器注入已定义好的 ProductListService
    • InvokeEnvelope 中的 Method 用于路由数据
    • 使用 ProductListRequest.Parser.ParseFrom 转换请求构造
    • 使用 Any.Pack() 打包需要返回的数据
  5. 运行 productService

    dapr run --app-id productService --app-port 5001 --protocol grpc dotnet run

小结

至此,ProductService 服务完成。此时 ProductService.Api.csproj Protobuf 内容为

<ItemGroup>
<Protobuf Include="Protos\daprclient.proto" GrpcServices="Server" />
<Protobuf Include="Protos\productList.proto" GrpcServices="Server" />
</ItemGroup>

改造 StorageService 服务以完成 Dapr GRPC 服务调用

  1. 添加 productList.proto 文件,内容同 ProductService 中的 productList.proto

  2. 添加 dapr.proto 文件,此文件也为官方提供,内容为

    syntax = "proto3";
    
    package dapr;
    
    import "google/protobuf/any.proto";
    import "google/protobuf/empty.proto";
    import "google/protobuf/duration.proto"; option java_outer_classname = "DaprProtos";
    option java_package = "io.dapr"; option csharp_namespace = "Dapr.Client.Grpc"; // Dapr definitions
    service Dapr {
    rpc PublishEvent(PublishEventEnvelope) returns (google.protobuf.Empty) {}
    rpc InvokeService(InvokeServiceEnvelope) returns (InvokeServiceResponseEnvelope) {}
    rpc InvokeBinding(InvokeBindingEnvelope) returns (google.protobuf.Empty) {}
    rpc GetState(GetStateEnvelope) returns (GetStateResponseEnvelope) {}
    rpc SaveState(SaveStateEnvelope) returns (google.protobuf.Empty) {}
    rpc DeleteState(DeleteStateEnvelope) returns (google.protobuf.Empty) {}
    } message InvokeServiceResponseEnvelope {
    google.protobuf.Any data = 1;
    map<string,string> metadata = 2;
    } message DeleteStateEnvelope {
    string key = 1;
    string etag = 2;
    StateOptions options = 3;
    } message SaveStateEnvelope {
    repeated StateRequest requests = 1;
    } message GetStateEnvelope {
    string key = 1;
    string consistency = 2;
    } message GetStateResponseEnvelope {
    google.protobuf.Any data = 1;
    string etag = 2;
    } message InvokeBindingEnvelope {
    string name = 1;
    google.protobuf.Any data = 2;
    map<string,string> metadata = 3;
    } message InvokeServiceEnvelope {
    string id = 1;
    string method = 2;
    google.protobuf.Any data = 3;
    map<string,string> metadata = 4;
    } message PublishEventEnvelope {
    string topic = 1;
    google.protobuf.Any data = 2;
    } message State {
    string key = 1;
    google.protobuf.Any value = 2;
    string etag = 3;
    map<string,string> metadata = 4;
    StateOptions options = 5;
    } message StateOptions {
    string concurrency = 1;
    string consistency = 2;
    RetryPolicy retryPolicy = 3;
    } message RetryPolicy {
    int32 threshold = 1;
    string pattern = 2;
    google.protobuf.Duration interval = 3;
    } message StateRequest {
    string key = 1;
    google.protobuf.Any value = 2;
    string etag = 3;
    map<string,string> metadata = 4;
    StateRequestOptions options = 5;
    } message StateRequestOptions {
    string concurrency = 1;
    string consistency = 2;
    StateRetryPolicy retryPolicy = 3;
    } message StateRetryPolicy {
    int32 threshold = 1;
    string pattern = 2;
    google.protobuf.Duration interval = 3;
    }

    说明

    • 此文件提供6个 GRPC 服务,此文介绍的函数为 InvokeService()

      • 请求构造为 InvokeServiceEnvelope

        • id 请求的服务的 --app-id ,比如 productService
        • method 请求的方法
        • data 请求函数的签名
        • metadata 元数据键值对
  3. 修改 StorageController 中的 InitialStorage() 函数为

    /// <summary>
    /// 初始化仓库.
    /// </summary>
    /// <returns>是否成功.</returns>
    [HttpGet("InitialStorage")]
    public async Task<bool> InitialStorage()
    {
    string defaultPort = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "5001"; // 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); InvokeServiceResponseEnvelope result = await client.InvokeServiceAsync(new InvokeServiceEnvelope
    {
    Method = "GetAllProducts",
    Id = "productService",
    Data = Any.Pack(new ProductListRequest())
    });
    ProductList.V1.ProductList productResult = ProductList.V1.ProductList.Parser.ParseFrom(result.Data.Value); var random = new Random(); foreach (Product item in productResult.Results)
    {
    _storageContext.Storage.Add(new Storage
    {
    ProductID = Guid.Parse(item.ID),
    Amount = random.Next(1, 1000)
    });
    } await _storageContext.SaveChangesAsync();
    return true;
    }
  4. 启动 StorageService

    dapr run --app-id storageService --app-port 5003 dotnet run
  5. 使用 Postman 请求 StorageService 的 InitialStorage

  6. 使用 MySql Workbench 查看结果

小结

至此,以 Dapr 框架使用 GRPC 客户端在 StorageService 中完成了对 ProductService 服务的调用。

源码地址

Dapr 运用之集成 Asp.Net Core Grpc 调用篇的更多相关文章

  1. 旧 WCF 项目迁移到 asp.net core + gRPC 的尝试

    一个月前,公司的运行WCF的windows服务器down掉了,由于 AWS 没有通知,没有能第一时间发现问题. 所以,客户提出将WCF服务由C#改为JAVA,在Linux上面运行:一方面,AWS对Li ...

  2. ASP.NET Core gRPC 入门全家桶

    一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...

  3. ASP.NET Core gRPC 健康检查的实现方式

    一. 前言 gRPC 服务实现健康检查有两种方式,前面在此文 ASP.NET Core gRPC 使用 Consul 服务注册发现 中有提到过,这里归纳整理一下.gRPC 的健康检查,官方是定义了标准 ...

  4. 从零搭建一个IdentityServer——集成Asp.net core Identity

    前面的文章使用Asp.net core 5.0以及IdentityServer4搭建了一个基础的验证服务器,并实现了基于客户端证书的Oauth2.0授权流程,以及通过access token访问被保护 ...

  5. ASP.NET Core GRPC 和 Dubbo 互通

    一.前言 Dubbo 是比较流行的服务治理框架,国内不少大厂都在使用.以前的 Dubbo 使用的是私有协议,采集用的 hessian 序列化,对于多语言生态来说是极度的不友好.现在 Dubbo 发布了 ...

  6. .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.html 本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新 ...

  7. net core体系-web应用程序-4asp.net core2.0 项目实战(CMS)-第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了   原文链接:https://www.cnblogs.com/yilezhu/p/9985451.ht ...

  8. ASP.NET Core gRPC 使用 Consul 服务注册发现

    一. 前言 gRPC 在当前最常见的应用就是在微服务场景中,所以不可避免的会有服务注册与发现问题,我们使用gRPC实现的服务可以使用 Consul 或者 etcd 作为服务注册与发现中心,本文主要介绍 ...

  9. 5. abp集成asp.net core

    一.前言 参照前篇<4. abp中的asp.net core模块剖析>,首先放张图,这也是asp.net core框架上MVC模块的扩展点 二.abp的mvc对象 AbpAspNetCor ...

随机推荐

  1. python三种格式化输出

    name = '张三'age = 22job = Engineersalary = 99999999 info1 = ''' ---------- info1 of %s ---------- nam ...

  2. Session,Token,Cookie相关区别

    1. 为什么要有session的出现? 答:是由于网络中http协议造成的,因为http本身是无状态协议,这样,无法确定你的本次请求和上次请求是不是你发送的.如果要进行类似论坛登陆相关的操作,就实现不 ...

  3. [转发]CSR 量产 烧录 软件

    蓝牙量产软件主要是为了应对蓝牙设备在批量生产时的一些如固件下载,地址下载,名字修改,以及一些辅助测试和检验功能. 目前,CSR推出的蓝牙芯片按照存储介质以及可编程与否分为两大类:ROM版本和Flash ...

  4. Mac 下安装并配置 Tomcat

    1,下载 点击 官网 ,进入下载页面, 2,安装 解压出来,即安装完成. 移动解压后的文件,换个文件目录(方便集中管理),将它改个名字(毕竟名字太长了). 我将其改名为 tomcat9 ,移入资源库目 ...

  5. vant-ui的van-uploader上传图片

    移动端上传图片是很常用的功能,这里使用vant-ui实现. 效果如图 上传图片的vue页面:Customer.vue html <div :class="postData.length ...

  6. java运算符详解

    java运算符: 定义:用来指明对于操作数的运算方式 按照操作数数目分类: 单目运算    数目运算    三目运算 a++              a+b           (a>b) ? ...

  7. Ubuntu--pip3 -V 问题

    问题原因:可能是因为重新下载或更新python版本的时候,将系统的‘软连接’破坏掉了 解决办法: 1.重新创建--软连接: 进入bin目录:cd /usr/bin 删除pip3连接:rm pip3 重 ...

  8. vue防抖节流之v-debounce--throttle使用指南

    最新封装了一个vue防抖节流自定义指令,发布到npm上,有用欢迎star,谢谢! npm地址:https://www.npmjs.com/package/v-debounce-throttle git ...

  9. createTextNode

    createTextNode()方法将创建一个包含给定文本的新文本文档节点.这个方法的返回值是一个指向新建文本节点的引用指针: reference = document.createTextNode( ...

  10. vsftpd架设(配置pam模块)

    Vsftpd 是很安全的ftp软件 VSFTPD的目录结构 /usr/sbin/vsftpd: VSFTPD的可执行文件 /etc/rc.d/init.d/vsftpd:启动脚本 /etc/vsftp ...