gRPC是一个现代的开源高性能远程过程调用(RPC)框架,它可以高效地连接数据中心内和跨数据中心的服务,支持负载平衡、跟踪、运行状况检查和身份验证。

gRPC通过使用 Protocol Buffers 作为数据传输格式,实现了在不同平台上的通信,并支持双向流和流式传输。RPC 是远程过程调用的缩写,实现跨服务器调用。在开发中,规定调用规则、网络传输协议以及数据序列化反序列化规范是确保前后端通信规范性的关键。

了解GRpc前需要了解Rpc概念。

什么是 RPC

RPC 是 Remote Procedure Call 的简称,中文叫远程过程调用

说的白话一点,可以这么理解:比如有两台服务器A和B,A服务器上的应用想调用B服务器上的另一个应用提供的方法,但由于不在同一个内存空间,无法直接调用,所以需要通过网络来实现调用效果。

其实大家在平时开发中有接触过,例如:前端去请求后端的接口。我们来想一下前后端要制定什么规则,才能进行接口请求:

  • 调用的语义,也可以理解为接口规范。(比如 RESTful )
  • 网络传输协议 (比如 HTTP )
  • 数据序列化反序列化规范(比如 JSON )

只有制定了这些规则,才能保证前后端通信的规范性

交互图

从上图中可以看出,RPC 是一种客户端-服务端(Client/Server)模式。从某种角度来看,所有本身应用程序之外的调用都可以归类为 RPC。无论是微服务、第三方 HTTP 接口,还是读写数据库中间件 Mysql、Redis。

RPC 特点

  • RPC 是一种协议。RPC实现包括:Dubbo、Thrift、Grpc、Netty等。
  • 网络协议和网络 IO 模型对其透明。RPC 的客户端认为自己是在调用本地对象,因此其对使用的网络协议( HTTP 协议等)以及网络 IO 模型,是不关心的。
  • 信息格式对其透明。调用方法是需要传递参数的,对于远程调用来说,传递过程中参数的信息格式是怎样构成,以及提供者如何使用这些参数,都是不用关心的。
  • 有跨语言能力。因为调用方实际上也不清楚远程服务器的应用程序是使用什么语言运行的。那么对于调用方来说,无论服务器方使用的是什么语言,本次调用都应该成功,并且返回值也应该按照调用方程序语言所能理解的形式进行描述。

RPC  HTTP 的对比

其实 RPC 跟 HTTP 不是一个层级的东西,RPC 应该是跟 HTTP + RestFul 进行对比。

深入了解:HTTP 与 RPC 接口区别

传输协议

RPC 可以基于 HTTP 或者 TCP 进行传输,而 HTTP 只能基于 HTTP

传输效率

RPC 包含了 HTTP2 的优点,所以他的传输效率比 HTTP1 更高~

性能消耗

RPC 包含 HTTP2 的优点,比如二进制传输、头部压缩等,所以性能消耗自然比 HTTP1 低~

负载均衡

RPC 基本都自带负载均衡策略,而 HTTP 需要配置 Nginx/HAProxy 来完成

服务治理

RPC 能做到自动通知,不影响上游,而 HTTP 需要事先通知,修改 Nginx/HAProxy 配置

gRPC 和 RPC 的关系

Grpc 是由谷歌开源的一种 RPC 框架,设计之初便是为了解决谷歌内部的 RPC 使用场景所遇到的问题。因此你可以说 gRPC 就是一种 RPC 框架类型。具体来说:

  • RPC是一种编程范式,定义了客户端像调用本地函数一样调用远程函数的方式。
  • gRPC 是 Google 基于 HTTP/2 和 Protocol Buffers 实现的 RPC 框架。
  • gRPC 支持双向流、流控、头压缩等,性能优异。

所以 gRPC 是  RPC 模式的一种高效实现,提供了语言中立、高性能、安全的 RPC 服务框架,使得RPC服务调用更加高效、简单、通用。它是 RPC 模式的一种优秀代表。

gRPC 的优势有哪些?

gRPC 是基于 HTTP/2 设计的~所以 gRPC 的优点自然也包含了 HTTP/2 的优点:

  • 数据传输二进制分帧
  • 多路复用
  • 服务端推送
  • 头部压缩

gRPC的主要优势及其简要描述:

优势
描述
高性能
利用HTTP/2提供高效的网络传输,支持双向流、头部压缩、多路复用。
跨语言支持
支持多种编程语言间的无缝通信和集成。
自动化生成代码
使用Protobuf定义服务,自动生成客户端和服务器代码。
错误处理
定义丰富的错误码和状态码,便于异常处理和调试。
通信模式多样
支持多种RPC通信模型,如一对一、服务端流、客户端流、双向流等。
可扩展性
拦截器和插件机制允许功能的扩展和定制。
社区和生态系统
拥有活跃的社区支持和丰富的相关工具及库。

gRPC 是怎么传输的?

传输图

从上图的 gRPC 传输模型可以看出,客户端 Stub 从 gRPC Core 库发起请求,序列化成 Protobuf 消息格式,然后传输至服务端。

服务端 Stub 接收客户端请求,处理请求中的 Protobuf 数据并进行反序列化,然后将请求对象传入服务器并实现业务逻辑处理。最终再将响应序列化后返回给客户端,从而形成一次完整的接口调用过程。

以上概念以及相关知识点来自apifox

NET Core实现Grpc调用

话不多说,以下内容详细介绍一元调用的过程,贴代码。

-----------------服务端代码 Start-----------------

步骤一:

在Grpc服务端(Server)先创建一个.potos文件。文件名(IBook_Service.proto)文件路径(Protos/IBook_Service.proto)

//表明使用protobuf的编译器版本为v2,目前最新的版本为v3。
syntax = "proto3"; //定义命名空间
option csharp_namespace = "ZP_BookService_Grpc.Application.Book_Service"; //包名:多个 .proto 文件生成代码时,可避免命名冲突。
package Book_Service; //1、定义接口名称,用于后期实现
service IBook_Service{ // 1.1 根据商品主键,获取商品
rpc GetBook (BookFrom) returns (BookDto);
} // 2、定义入参(类)Form:顺序要写,且不能重复
message BookFrom{
string BookName = 1;
} // 3、定义出参Dto(类):顺序要写,且不能重复
message BookDto{
string ID = 1;
string CreateTime = 2;
string BookName = 3;
string BookPrice =4;
string PublicationDate = 5;
string Type = 6;
string Publisher = 7;
int32 Residue = 8;
}

步骤二:

文件建立好后需要在项目的Project内加上文件的目录(Protos\IBook_Service.prot) GrpcServices意思是服务端,一定要加上<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Server" />

引用包(Google.Protobuf、Grpc.AspNetCore、Grpc.Tools)

完成以上步骤,右键项目选择 “重新生成”。生成以后可以在项目的obj文件夹(~\Application_Grpc\obj\Debug\net7.0\Protos)看到一个自动生成的Protos文件夹

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.61.0" />
<PackageReference Include="Grpc.Tools" Version="2.62.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Server" />
</ItemGroup>

步骤三:

创建一个实现类(Book_Service)用于实现protos文件夹内自动生成的接口(IBook_Service.IBook_ServiceBase)。注:实现带有Base的接口。接口名称是定义proto文件的时候自定义的。

namespace Application_Grpc.Application.BusinessServices
{
//注入作用域生命周期
[Service(ServiceLifetime.Scoped)]
public class Book_Service : IBook_Service.IBook_ServiceBase
{
private IBook_Repository _bookRepository { get; }
private IMapper _mapper { get; }
public Book_Service(IBook_Repository book_Repository, IMapper mapper)
{
_bookRepository = book_Repository;
_mapper = mapper;
}
public override async Task<BookDto> GetBook(BookFrom request, ServerCallContext context)
{
BookDto data=new BookDto();
data.ID = Guid.NewGuid().ToString();
data.CreateTime = DateTime.Now.ToString();
data.BookName = request.BookName;
data.BookPrice = "29.99";
data.PublicationDate = "1999-03-21";
data.Type = "经典";
data.Publisher = "清华大学出版社";
data.Residue = 5;
return data;
}
}
}

至此,服务端的Grpc就完成了。剩下的就是把项目进行服务依赖注入操作,本实例代码通过贴特征方式注入。可以在 Program.cs 以常规方式注入自己的服务。

如:builder.Services.AddSingleton<Book_Service>();

-----------------服务端代码 END-----------------

-----------------客户端代码 Start-----------------

步骤一:

步骤一:

在Grpc服务端(Client)先创建一个.potos文件。文件名(IBook_Service.proto)文件路径(Protos/IBook_Service.proto)。其实就是复制服务端的 proto 修改下 命名空间

//表明使用protobuf的编译器版本为v2,目前最新的版本为v3。
syntax = "proto3"; //定义命名空间
option csharp_namespace = "ZP_ProjectEntrance.MicroService.Book_Service"; //包名:多个 .proto 文件生成代码时,可避免命名冲突。
package Book_Service; //1、定义接口名称,用于后期实现
service IBook_Service{ // 1.1 根据商品主键,获取商品
rpc GetBook (BookFrom) returns (BookDto);
} // 2、定义入参(类)Form:顺序要写,且不能重复
message BookFrom{
string BookName = 1;
} // 3、定义出参Dto(类):顺序要写,且不能重复
message BookDto{
string ID = 1;
string CreateTime = 2;
string BookName = 3;
string BookPrice =4;
string PublicationDate = 5;
string Type = 6;
string Publisher = 7;
int32 Residue = 8;
}

步骤二:

文件建立好后需要在项目的加上文件的目录(Protos\IBook_Service.prot) GrpcServices意思是客户端(Client),一定要加上 <Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Client" />

引用包(Google.Protobuf、Grpc.AspNetCore、Grpc.Tools)

完成以上步骤,右键项目选择 “重新生成”。生成以后可以在项目的obj文件夹(~\ZP_ProjectEntrance\obj\Debug\net7.0\Protos)看到一个自动生成的Protos文件夹

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.61.0" />
<PackageReference Include="Grpc.Tools" Version="2.62.0" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\IBook_Service.proto" GrpcServices="Client" />
</ItemGroup>

步骤三:重点、重点、重点~

创建一个客户端类(ClientHelper)用于对接到Grpc的服务端,通过Func型委托形式构建,做成公共的请求服务端入口。

public static class GrpcClientHelper
{
/// <summary>
/// 一元rpc调用
/// </summary>
/// <typeparam name="TClient">客户端类型</typeparam>
/// <typeparam name="TRequest">请求类型 </typeparam>
/// <typeparam name="TResponse">服务端响应返回类型</typeparam>
/// <typeparam name="TResult">方法返回类型 </typeparam>
/// <param name="serverAddress">请求服务端地址</param>
/// <param name="callFunc">异步调用 gRPC方法的委托 </param>
/// <param name="request">封装请求对象(数据)</param>
/// <param name="clientFactory">创建 gRPC 客户端的委托工厂方法 </param>
/// <returns></returns>
public static async Task<TResult> CallGrpcServiceAsync<TClient, TRequest, TResponse, TResult>(
string serverAddress, Func<TClient, TRequest, Task<TResponse>> callFunc, TRequest request, Func<GrpcChannel, TClient> clientFactory)
where TClient : class
where TRequest : class
where TResponse : class
{
using var channel = GrpcChannel.ForAddress(serverAddress);
var client = clientFactory(channel);
try
{
var response = await callFunc(client, request);
// 这里添加转换逻辑,如果 TResponse 不是 TResult,强制类型转换,需要确保类型兼容
return (TResult)(object)response;
}
catch (RpcException)
{
// 处理异常
throw;
}
}
/// <summary>
/// 获取某个字段的值
/// </summary>
/// <typeparam name="Source"></typeparam>
/// <param name="source"></param>
/// <param name="field"></param>
/// <returns></returns>
public static object GetFieldValue<Source>(Source source, string field)
{
var fieldProperty = source.GetType().GetProperty(field);
if (fieldProperty != null)
return fieldProperty.GetValue(source);
else
return null;
}
}

步骤四:

通过Controllers定义的方法,以请求方法形式进行调用到GrpcClientHelper

namespace ZP_ProjectEntrance.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BooKController : ControllerBase
{
private readonly ILogger<BooKController> _logger;
public BooKController(ILogger<BooKController> logger)
{
_logger = logger;
}
/// <summary>
/// 获取书籍信息
/// </summary>
/// <returns></returns>
[HttpGet(Name = "GetBook")]
public async Task<BookDto> GetBookAsync()
{
// 服务端地址,可以扩展为请求分布式集群
string serverAddress = "http://localhost:5031";
// 使用 GrpcClientHelper 来调用 gRPC 服务 ,
BookDto bookDto = await GrpcClientHelper.CallGrpcServiceAsync<IBook_Service.IBook_ServiceClient, BookFrom, BookDto, BookDto>(
serverAddress,
async (client, request) => await client.GetBookAsync(request), // 异步调用 gRPC 方法的委托
new BookFrom { BookName = "三国演义" }, // 封装请求对象(值)
(channel) => new IBook_Service.IBook_ServiceClient(channel) // 实现 gRPC 客户端的委托方法
);
_logger.LogInformation(JsonConvert.SerializeObject(bookDto));
return bookDto;
}
}
}

至此,客户端的Grpc就完成了。

-----------------客户端代码 END-----------------

项目的结构

附:贴特征形式实现依赖注入的代码,通过反射机制实现。

注:业务层和仓储层必须是独立层项目,或相同形式进行隔离(要么统一接口I_BLL、I_DLL,要么单纯的BLL、DLL),否则自行对自动注册类进行改造。

代码可以写在公共层 ExternalService 项目中,作为基础服务进行引用。

using Microsoft.Extensions.DependencyInjection;
using System.Reflection; namespace ExternalService.RegisterServices
{
/// <summary>
/// 通过反射机制自动化进行依赖注入服务
/// </summary>
public static class ServiceCollectionExtension
{
/// <summary>
/// 注册接口类型服务,继承接口(贴特征)
/// </summary>
/// <param name="services">this服务</param>
/// <param name="assembly">程序集</param>
/// <returns></returns>
public static IServiceCollection RegisterIntfaceTypeService(this IServiceCollection services, Assembly assembly)
{
var interfaces = assembly.GetTypes().Where(t => t.IsInterface).ToList();
var types = assembly.GetTypes().Where(t => t.IsClass).ToList(); foreach (var interf in interfaces)
{
if (interf == null)
continue; var type = types.FirstOrDefault(interf.IsAssignableFrom);
if (type == null)
continue; var liftTime = ServiceLifetime.Scoped;
var attr = type.GetCustomAttribute<ServiceAttribute>();
if (attr != null)
liftTime = attr.LifeTime;
else
continue; switch (liftTime)
{
default:
case ServiceLifetime.Scoped:
{
//作用生命周期:同一请求之间状态共享,跟随HTTP请求生命周期
services.AddScoped(interf, type);
break;
}
case ServiceLifetime.Transient:
{
//瞬时生命周期:无状态化,每次使用是 new ()
services.AddTransient(interf, type);
break;
}
case ServiceLifetime.Singleton:
{
//单例生命周期:整个程序所有请求状态共享,整个程序只有一个实例
services.AddSingleton(interf, type);
break;
}
}
}
return services;
}
/// <summary>
/// 注册普通类服务,非接口类型(贴特征)
/// </summary>
/// <param name="services">this服务</param>
/// <param name="assembly">程序集</param>
/// <param name="NamespaceKeyWord">命名空间关键字</param>
/// <returns></returns>
public static IServiceCollection RegisterClassService(this IServiceCollection services, Assembly assembly, string NamespaceKeyWord = "")
{
var ClassTypes = assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract).ToList();
if (!string.IsNullOrEmpty(NamespaceKeyWord))
ClassTypes = assembly.GetTypes().Where(t => t.Name.Contains(NamespaceKeyWord)).ToList();
foreach (var types in ClassTypes)
{
if (types == null)
continue; var liftTime = ServiceLifetime.Scoped;
var attr = types.GetCustomAttribute<ServiceAttribute>();
if (attr != null)
liftTime = attr.LifeTime;
else
continue; switch (liftTime)
{
default:
case ServiceLifetime.Scoped:
{
//作用生命周期:同一请求之间状态共享,跟随HTTP请求生命周期
services.AddScoped(types);
break;
}
case ServiceLifetime.Transient:
{
//瞬时生命周期:无状态化,每次使用是 new ()
services.AddTransient(types);
break;
}
case ServiceLifetime.Singleton:
{
//单例生命周期:整个程序所有请求状态共享,整个程序只有一个实例
services.AddSingleton(types);
break;
}
}
}
return services;
}
} /// <summary>
/// 生命周期特征
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ServiceAttribute : Attribute
{
internal ServiceLifetime LifeTime { get; set; } public ServiceAttribute(ServiceLifetime lifeTime) => LifeTime = lifeTime;
}
}

在使用的项目程序集内创建一个扩展服务 AddApplication_Register。然后调用注册类扩展服务services.RegisterClassService(Assembly.GetExecutingAssembly());

using System.Reflection;

using ExternalService.RegisterServices;
using Microsoft.Extensions.DependencyInjection; namespace Application_Grpc
{
public static class Application_GrpcExtension
{
public static IServiceCollection AddApplication_Register(this IServiceCollection services)
{
services.RegisterClassService(Assembly.GetExecutingAssembly());
return services;
}
}
}

在Program.cs内注册添加项目程序集的扩展服务,把上面Application_GrpcExtension类的AddApplication_Register进行调用:builder.Services.AddApplication_Register();

NET Core使用Grpc通信(一):一元的更多相关文章

  1. .NET Core使用gRPC打造服务间通信基础设施

    一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制. 利用这种传输机制,不同进程(或服务)间像调用本地进程中 ...

  2. 使用 ASP.NET Core 的 gRPC 服务

    将 gRPC 服务添加到 ASP.NET Core 应用 gRPC 需要gRPC包. 配置 gRPC 在 Startup.cs 中: gRPC 是通过AddGrpc方法启用的. 每个 gRPC 服务通 ...

  3. dotnet core 之 gRPC

    dotnet core gRPC 原文在本人公众号中,欢迎关注我,时不时的会分享一些心得 HTTP和RPC是现代微服务架构中很常用的数据传输方式,两者有很多相似之处,但是又有很大的不同.HTTP是一种 ...

  4. .net core中Grpc使用报错:The remote certificate is invalid according to the validation procedure.

    因为Grpc采用HTTP/2作为通信协议,默认采用LTS/SSL加密方式传输,比如使用.net core启动一个服务端(被调用方)时: public static IHostBuilder Creat ...

  5. .net core 用grpc实现微服务

    GRPC 是Google发布的一个开源.高性能.通用RPC(Remote Procedure Call)框架.提供跨语言.跨平台支持.以下以.NET Core 使用控制台.docker中演示如何使用G ...

  6. 重新整理 .net core 实践篇—————grpc[三十三]

    前言 简单整理一下grpc. 正文 什么是grpc? 一个远程过程调用框架,可以像类一样调用远程方法. 这种模式一般来说就是代理模式,然后都是框架自我生成的. 由google 公司发起并开源,故而前面 ...

  7. .net core中Grpc使用报错:The response ended prematurely.

    当我们调用Grpc是出现下面的一堆异常时,一般是由于LTS导致的: Call failed with gRPC error status. Status code: 'Unavailable', Me ...

  8. grpc基础讲解和golang实现grpc通信小案例

    grpc简介 gRPC由google开发,是一款语言中立.平台中立.开源的远程过程调用系统 gRPC客户端和服务端可以在多种环境中运行和交互,例如用java写一个服务端,可以用go语言写客户端调用 g ...

  9. 记录core中GRPC长连接导致负载均衡不均衡问题 二,解决长连接问题

    题外话: 1.这几天收到蔚来的面试邀请,但是自己没做准备,并且远程面试,还在上班时间,再加上老东家对我还不错.没想着换工作,导致在自己工位上做算法题不想被人看见,然后非常紧张.估计over了.不过没事 ...

  10. .net core中Grpc使用报错:Request protocol 'HTTP/1.1' is not supported.

    显然这个报错是说HTTP/1.1不支持. 首先,我们要知道,Grpc是Google开源的,跨语言的,高性能的远程过程调用框架,它是以HTTP/2作为通信协议的,所以当我启动启用一个服务作为Grpc的服 ...

随机推荐

  1. 使用UTL_HTTP包获取网页内容

    UTL_HTTP 包提供了容易的方式通过HTTP协议获取网页内容,下面结合几个例子介绍一下: ----------------------------------------------------- ...

  2. Java I/O 教程(六) BufferedInputStream 类

    Java BufferedInputStream Class Java BufferedInputStream class 用于从输入流读取数据,和BufferedOutStream一样内部使用缓冲机 ...

  3. 【Android逆向】滚动的天空中插入smali日志

    1. 编写一个MyLog.java 放到一个android工程下,编译打包,然后反编译拿到MyLog的smali代码 package com.example.logapplication; impor ...

  4. startswith/endswith传元组用法

    className = ["jd_num01","jd_num02","tx_num01", "tx_num02", & ...

  5. 通过paramiko模块操作服务器

    用于帮助开发者通过代码远程连接服务器,并对服务器进行操作. 如果下面运行错误了,可以看我另外一篇文章有解决办法解决paramiko连接远程服务器错误 pip3 install paramiko imp ...

  6. Jenkins+maven+svn+tomcat持续集成环境

    前言 团队最近要把项目发布的工作拿过来,所以需要一个持续集成发布系统 直接上步骤. 下载 http://mirrors.jenkins-ci.org/war/latest/ 直接下载war包,我下载的 ...

  7. X86模拟龙芯与编译 .NET CoreCLR

    目录 .NET 收到一台龙芯机器 编译 CoreCLR 环境要求 部署虚拟机与环境 Linux 安装 KVM 下载需要的文件 启动模拟器 下载 CoreCLR 尝试编译 CoreCLR 前段时间得知龙 ...

  8. 【Openxml】如何为OpenXml元素创建超链接

    已知在OpenXml有以下几种超链接 功能 说明 跳转页面 跳转某一页:ppaction://hlinksldjump跳转第一页:ppaction://hlinkshowjump?jump=first ...

  9. 如何扩展Spark Catalyst,抓取spark sql 语句,通过listenerBus发送sql event以及编写自定义的Spark SQL引擎

    1.Spark Catalyst扩展点 Spark catalyst的扩展点在SPARK-18127中被引入,Spark用户可以在SQL处理的各个阶段扩展自定义实现,非常强大高效,是SparkSQL的 ...

  10. UG474

    为了对工程的资源利用率进行优化,我们首先需要知道当前工程对资源的利用率情况.在Vivado下,我们可以查看工程的资源利用率情况,在下面这张图中,其罗列出了整个工程所使用的资源情况.首先,下面我们需要一 ...