Dapr for dotnet | 服务调用-Service invoke(gRPC协议)

$ | 1 什么是 RPC ?

在应用 gRPC 之前,我们先来了解下 RPC,究竟什么是 RPC 呢? RPC(Remote Procedure Call) 是远程过程调用 ,这么说比较抽象,来举个例子,比如现在有两台服务器环境(A,B服务器)分别部署了各自的服务 Service A 和 Service B,其中 Service A 需要调用 Service B 上的某个方法,由于各自的 Service 服务部署在独立的宿主环境,两个方法不在同一个进程内(内存空间),不能直接调用,需要通过网络表达调用的语义和传达调用的数据。这种类似的情况常存在于分布式系统中。

$ | 2 为什么有了 HTTP 还需要 RPC ?

基于上面的例子,在分布式系统环境中经常面临类似的需求场景,同样是服务跨进程的通信,有人会说 HTTP 协议不是也可以做到服务跨进程的通信,为啥还需要 RPC 呢?没错 HTTP 的确可以实现服务间跨进程的调用通信,但在某些特定场景下 HTTP 满足不了应用需求(后面举例),并且 RPC 跟 HTTP 不是对立面,RPC 中可以使用 HTTP 作为通讯协议,两者更是一种互补关系

RPC 是一种设计、实现框架,通讯协议只是其中一部分。 HTTP 协议是在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。

  • HTTP 和 RPC 的简单对比
名称协议是否长连接效率复杂度跨语言支持应用场景
HTTP基于HTTP协议否(HTTP 协议是无状态的, 每次通信都要 3 次握手 )一般(OSI 第七层-应用层)简单(以 RESTful 规范为代表,可读性好)支持小型网站(单体)
RPC通常基于TCP/IP协议,HTTP 协议可选是(长链接,不必每次通信都要像HTTP 一样去3次握手,减少了网络开销)较高(OSI 第五层-会话层)复杂(涉及RPC框架,服务注册与发现,服务治理)支持中/大型网站(分布式SOA/微服务,特别是高并发场景)

关于 TCP/IP 四层模型和 OSI 七层模型,请对照后面的【附件】查看

其次就是 RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。接下来我们通过一张图简单对比 HTTP 和 RPC
http 和 rpc 对比

$ | 3 RPC 中要解决哪些问题?

上面我们提到 在特定场景 RPC 是 HTTP 的互补,那么 RPC 要解决哪些问题呢?

  • RPC 调用过程

在这里插入图片描述

  • 建立通信:在客户端与服务端建立起数据传输通道,大都是 TCP 连接(gRPC 使用了 HTTP2 )。
  • 寻址:A 服务器上的应用需要告诉 RPC 框架:B 服务器地址、端口,调用函数名称。所以必须实现待调用方法到 call ID 的映射。
  • 序列化与反序列化:由于网络协议都是二进制的,所以调用方法的参数在进行传递时首先要序列化成二进制,B 服务器收到请求后要再对参数进行反序列化。恢复为内存中的表达方式,找到对应的方法进行本地调用,得到返回值。返回值从 B 到 A 的传输仍要经过序列化与反序列化的过程。

关于 http 1.0/1.1/2.0/3.0 各版本的对比,请参考以下文章

  • https://blog.csdn.net/glpghz/article/details/106063833
  • https://www.cnblogs.com/heluan/p/8620312.html

$ | 4 什么是 HTTPS

HTTP 协议传输的数据都是未加密的。为了保证这些隐私数据能加密传输,于是网景公司设计了 SSL(Secure Sockets Layer,安全套接字协议) 协议用于对HTTP协议传输的数据进行加密,从而就诞生了 HTTPS。现在的 HTTPS 都是用的 TLS(Transport Layer Security Protocol,安全传输层协议) 协议,但是由于 SSL 出现的时间比较早,并且依旧被现在浏览器所支持,因此 SSL 依然是 HTTPS 的代名词。

HTTPS 默认端口号是 443

$ | 5 什么是 gRPC

gRPC 是谷歌开源的一种语言无关且高性能轻量级的 RPC 框架,面向移动和 HTTP/2 设计。

  • 内容交换格式采用 ProtoBuf(Google Protocol Buffers),开源已久,提供了一种灵活、高效、自动序列化结构数据的机制,作用与 XML,JSON 类似,但使用二进制,(反)序列化速度快,压缩效率高,从而减少网络开销的使用。
  • 传输协议采用 HTTP 2.0,性能比 HTTP 1.1 好了很多。
  • 和很多 RPC 系统一样,服务端(Server)负责实现定义好的接口并处理客户端的请求,客户端(Client)根据接口描述直接调用需要的服务。客户端和服务端可以分别使用 gPRC 支持的不同语言实现。
  • ProtoBuf 具有强大的 IDL(interface description language,接口描述语言)和相关工具集(主要是protoc)。用户写好【.proto】描述文件后,protoc 可以将其编译成众多语言的接口代码。

这些优点使 gRPC 适用于以下应用场景:

  • 效率至关重要的轻量级微服务。
  • 需要多种编程语言用于开发的系统。
  • 需要处理(双向)流式调用请求或响应的客户端、服务器或点对点实时服务。

注意:【Dapr 中的 gRPC 】与我们通常情况下使用 gRPC 大致是一致的,唯一的不同是 Dapr 中的 gRPC 会固定调用接口【 /dapr.proto.runtime.v1.AppCallback/OnInvoke 】,因此我们在 Dapr 中使用 gRPC 时,proto 文件中不需要再定义 Service 节点中的 RPC 接口,只需要定义交互使用的序列化结构体 即可。

了解更多,请查看【gRPC 官网

$ | 6 使用 DaprClient 调用 gRPC

上面我们对 HTTP、RPC 和 gRPC 进行了简单的了解,接下来我们改造 BackEnd 服务和 FrontEnd 服务以支持 gRPC 的调用,操作如下:

$ | 6.1 BackEnd 项目改造

6.1.1 修改配置的【applicationUrl】属性值,并添加 https 端口

注意:之前创建的 BackEnd 服务只启用 http 协议,此处演示 gRPC 调用需要启用 https 协议监听。

同样修改 launchsettings.json 配置的【applicationUrl】属性值,添加 https 并配置端口 5010,如下所示:

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:26409",
      "sslPort": 44338
    }
  },
  "profiles": {
    "BackEnd": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "https://localhost:5010;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

6.1.2 BackEnd 项目新增 Nuget 引用

  • Dapr.AspNetCore
  • Grpc.AspNetCore

在 BackEnd 项目中添加 Nuget 依赖包引用:
BackEnd 添加 Nuget 依赖包

6.1.3 BackEnd 项目新增 Protos 文件夹,并添加 greet.proto 文件

定义 gRPC 服务中交互使用的序列化结构体

syntax = "proto3";
option csharp_namespace = "GrpcGreeter";
package greet;

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

6.1.4 BackEnd 项目修改 .csproj 文件

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Dapr.AspNetCore" Version="1.6.0" />
    <PackageReference Include="Grpc.AspNetCore" Version="2.43.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
  </ItemGroup>
  
  <!--添加 gRPC Server-->
  <ItemGroup>
	 <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
  </ItemGroup>
</Project>

注意:添加引用依赖后,先编译一下项目,方便为后面的环境提供引用。

6.1.5 BackEnd 项目新增 Services文件夹,并添加 HelloService.cs 文件

using Dapr.AppCallback.Autogen.Grpc.v1;
using Dapr.Client.Autogen.Grpc.v1;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using GrpcGreeter;

namespace BackEnd.Service
{
    public class HelloService : AppCallback.AppCallbackBase
    {
        public override async Task<InvokeResponse> OnInvoke(InvokeRequest request, ServerCallContext context)
        {
            var response = new InvokeResponse();
            switch (request.Method)
            {
                case "sayhi":
                    var input = request.Data.Unpack<HelloRequest>();
                    response.Data = Any.Pack(new HelloReply { Message = "ok" });
                    break;
            }
            return response;
        }
    }
}

6.1.6 BackEnd 项目 Program.cs 注册 Grpc

using BackEnd.Service;
...
builder.Services.AddGrpc(); // 注册 Grpc
...
app.MapGrpcService<HelloService>();
...

$ | 6.2 FrontEnd 项目改造

6.2.1 FrontEnd 项目新增 Nuget 引用

  • Google.Protobuf
  • Grpc.Net.Client
  • Grpc.Tools

FrontEnd 添加 Nuget 依赖包

6.2.2 FrontEnd 项目新增 Protos 文件夹,并添加 greet.proto 文件

定义 gRPC 服务中交互使用的序列化结构体(同上保持一致)

syntax = "proto3";
option csharp_namespace = "GrpcGreeter";
package greet;

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

6.2.3 FrontEnd 项目修改 .csproj 文件

<Project Sdk="Microsoft.NET.Sdk.Web">
	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Dapr.AspNetCore" Version="1.6.0" />
		<PackageReference Include="Google.Protobuf" Version="3.19.4" />
		<PackageReference Include="Grpc.Net.Client" Version="2.43.0" />
		<PackageReference Include="Grpc.Tools" Version="2.44.0">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
	</ItemGroup>
	
	<!--添加 gRPC Client-->
	<ItemGroup>
		<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
	</ItemGroup>
</Project>

注意:添加引用依赖后,同样先编译一下项目,方便为后面的环境提供引用。

6.2.4 FrontEnd 项目新增 DaprGrpcInvoke 接口

using Dapr.Client;
using GrpcGreeter;
using Microsoft.AspNetCore.Mvc;

namespace FrontEnd.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class DaprGrpcInvokeController : ControllerBase
    {
        private readonly static string _appId = "backend";
        private readonly static string _methodName = "sayhi";
        private readonly ILogger<DaprHttpInvokeController> _logger;
        private readonly DaprClient _daprClient;

        public DaprGrpcInvokeController(ILogger<DaprHttpInvokeController> logger, DaprClient daprClient)
        {
            _logger = logger;
            _daprClient = daprClient; // (推荐)构造函数注入 DaprClient
        }

        [HttpGet]
        public async Task<ActionResult> GrpcAsync()
        {
            _logger.LogInformation("构建 daprClient (gRPC)调用 BackEnd 服务");
            using var daprClient = new DaprClientBuilder().Build();
            var result = await daprClient.InvokeMethodGrpcAsync<HelloRequest, HelloReply>(_appId, _methodName, new HelloRequest { Name = "aaa" });
            return Ok(result);
        }

        [HttpGet]
        public async Task<ActionResult> Grpc2Async()
        {
            _logger.LogInformation("构造函数 DI 注入 daprClient (gRPC)调用 BackEnd 服务");
            var result = await _daprClient.InvokeMethodGrpcAsync<HelloRequest, HelloReply>(_appId, _methodName, new HelloRequest { Name = "bbb" });
            return Ok(result);
        }
    }
}

6.2.5 测试 gRPC 服务调用

  • 启动 BackEnd 服务(启用 https)
dapr run --dapr-http-port 3502 --app-port 5010 --app-id backend --app-protocol grpc dotnet run --app-ssl
  • 启动 FrontEnd 服务(启用 http)
dapr run --dapr-http-port 3501 --app-port 5001 --app-id frontend dotnet run

注意:服务发布文件启动,只需把 dotnet run 修改为 dotnet .\xxx\FrontEnd.dll 即可。

  • 检查 BackEnd 和 FrontEnd 是否正常启动,浏览器输入:
  1. BackEnd 服务:https://localhost:5010/swagger/index.html (或 http 端口 5000 访问)
  3. FrontEnd 服务:http://localhost:5001/swagger/index.html

Swagger 页面正常访问
swagger 页面

  • 发送请求执行 gRPC 服务调用测试
  1. 浏览器 url 访问
http://localhost:5001/api/DaprGrpcInvoke/Grpc
http://localhost:5001/api/DaprGrpcInvoke/Grpc2
  1. curl 访问
# 构建 daprClient (gRPC)调用 BackEnd 服务
curl -X 'GET' \
  'http://localhost:5001/api/DaprGrpcInvoke/Grpc' \
  -H 'accept: */*'

# 构造函数 DI 注入 daprClient (gRPC)调用 BackEnd 服务
curl -X 'GET' \
  'http://localhost:5001/api/DaprGrpcInvoke/Grpc2' \
  -H 'accept: */*'
  1. dapr sidecar api 访问(ApiPost 工具)
http://localhost:3501/v1.0/invoke/frontend/method/api/DaprGrpcInvoke/Grpc
http://localhost:3501/v1.0/invoke/frontend/method/api/DaprGrpcInvoke/Grpc2
  1. dapr cli 访问
dapr invoke --app-id frontend --verb "GET" --method api/DaprGrpcInvoke/Grpc
dapr invoke --app-id frontend --verb "GET" --method api/DaprGrpcInvoke/Grpc2

上面的测试均可正常访问,此处展示 dapr cli 访问测试结果如下:

PS C:\Users\sws-dev-server> dapr invoke --app-id frontend --verb "GET" --method api/DaprGrpcInvoke/Grpc
{"message":"ok"}
App invoked successfully
PS C:\Users\sws-dev-server> dapr invoke --app-id frontend --verb "GET" --method api/DaprGrpcInvoke/Grpc2
{"message":"ok"}
App invoked successfully

感兴趣的小伙伴可自行测试其他访问方式。

$ | 7 查看 Dapr Sidecar 服务调用的链路追踪

自承载的方式下,Dapr 默认启动了 Zipkin 容器,可以通过以下链接查看

http://localhost:9411/zipkin/

显示如下页面信息:
链路监控
点击对应服务的【SHOW】可以查看调用链路的相关详细信息
链路调用详细
以上演示环境均使用 Docker 运行 Dapr 的相关组件
docker dapr run

附件

  • OSI 七层模型和 TCP/IP 四层模型对比

OSI 和 TCP/IP

  • OSI 七层模型参考图

OSI 七层模型

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChaITSimpleLove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值