gRPC-微服务间通信实践
微服务间通信常见的两种方式
由于微服务架构慢慢被更多人使用后,迎面而来的问题是如何做好微服务间通信的方案。我们先分析下目前最常用的两种服务间通信方案。
gRPC(rpc远程调用)
场景:A服务主动发起请求到B服务,同步方式
范围:只在微服务间通信应用
EventBus(基于消息队列的集成事件)
技术:NotNetCore.Cap + Rabbitmq + Database
场景:A服务要在B服务做某件事情后响应,异步方式
实现:B服务在完成某件事情后发布消息,A服务订阅此消息
范围:只在微服务间通信应用
通过对比,两种方式完全不一样。rpc是类似于http请求的及时响应机制,但是比http更轻量、快捷,它更像以前的微软的WCF,可以自动生成客户端代码,充分体现了面向实体对象的远程调用的思想;Eventbus是异步的消息机制,基于cap的思想,不关心下游订阅方服务是否消费成功,保障了主服务业务的流畅性,同时也是一款分布式事务的实现方案,可以保障分布式架构中的数据的最终一致性。
我们今天主要介绍gRPC在微服务中的实践案例。
gRPC-Server(服务端)
框架介绍
- .Net Core sdk 3.1
- Grpc.AspNetCore 2.30.0
- Grpc.Core 2.30.0


搭建步骤
以.net core webapi 项目为例,详细说明如何集成gRPC。
创建项目
创建web api项目,此步骤说明省略
引入nuget包
引入gRPC 服务端需要的 nuget包,Grpc.AspNetCore 2.30.0和Grpc.Core 2.30.0
外部访问
考虑到项目发布后,有webapi本身的http的接口和gRPC的接口都要给外部访问,就需要暴露http1和http2两个端口。
方式1:本地调试时,可以直接暴露http和https,如果你的服务器支持https,也可以在生产环境使用https来访问gRPC服务。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseNLog()
.UseUrls("http://*:5000;https://*:5001");
方式2:如果在容器化部署场景下,一般会在dockerfile中指定ASPNETCORE_PORT环境变量,然后程序监听http1和http2两个端口。
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
var aspnetcorePort = Environment.GetEnvironmentVariable("ASPNETCORE_PORT") ?? 5000;
int.TryParse(aspnetcorePort, out int port);
webBuilder.ConfigureKestrel(options =>
{
options.ListenAnyIP(port, options => options.Protocols = HttpProtocols.Http1);
options.ListenAnyIP(port + 1, options => options.Protocols = HttpProtocols.Http2);
})
.UseStartup<Startup>();
webBuilder.UseNLog();
});
异常处理
由于gRPC服务端只能throw 基于 Grpc.Core.RpcException 的异常类型,所以我们可以自定义中间件来统一处理下异常
using Grpc.Core;
using Grpc.Core.Interceptors;
using System;
using System.Threading.Tasks;
public class ExceptionInterceptor : Interceptor
{
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation
)
{
try
{
return await continuation(request, context);
}
catch (RpcException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new RpcException(new Status(StatusCode.Internal, ex.Message + "\r\n" + ex.StackTrace));
}
}
}
代码中被继承的 Interceptor 是 Grpc.Core.Interceptors.Interceptor。主要处理的目的是把在gRPC接口中抛出的非 RpcException 的异常,转换为 RpcException。此中间件也是根据具体的业务需求来做的,主要是告诉大家可以重写 Grpc.Core.Interceptors.Interceptor 的拦截器来统一处理一些事情。
定义协议缓冲区(protocol3)
新建项搜索rpc可以出现协议缓冲区文件

定义示例接口,创建订单方法,以及创建订单入参和出参。关于proto3协议具体说明,请参考往期文章。
syntax = "proto3";
option csharp_namespace = "GrpcTest.Protos";
service Order {
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderReply);
}
message CreateOrderRequest {
string ItemCode = 1;
string ItemName = 2;
string Spec = 3;
double Price = 4;
double Quantity = 5;
string Unit = 6;
double Cost = 7;
}
message CreateOrderReply {
bool success = 1;
}
在项目的csproj文件中,需要有proto包含进去,GrpcServices="Server"表示当前是服务端。改好后重新生成下项目。
<ItemGroup>
<Protobuf Include="Protos/GrpcTest.Protos" GrpcServices="Server" />
</ItemGroup>
创建OrderService
手动创建OrderService,继承自Order.OrderBase(proto自动生成的代码)
public class OrderService : Order.OrderBase
{
public async override Task<CreateOrderReply> CreateOrder(CreateOrderRequest request, ServerCallContext context)
{
//todo something
//throw RpcException异常
throw new RpcException(new Status(StatusCode.NotFound, "资源不存在"));
//返回
return new CreateOrderReply
{
Success = true
};
}
}
重写CreateOrder方法,此处就可以写你的实际的业务代码,相当于Controller接口入口。如果业务中需要主动抛出异常,可以使用RpcException,有定义好的一套状态码和异常封装。
修改Startup
在ConfigureServices方法中加入AddGrpc,以及上面提到的异常处理中间件,代码如下
services.AddGrpc(option => option.Interceptors.Add<ExceptionInterceptor>());
在Configure方法中将OrderService启用,代码如下
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<OrderService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("this is a gRPC server");
});
});
至此 gRPC服务端搭建完成。
gRPC-Client(客户端)
框架介绍
- .Net Core sdk 3.1
- Google.Protobuf 3.12.4
- Grpc.Tools 2.30.0
- Grpc.Net.ClientFactory 2.30.0

搭建步骤
以.net core webapi 项目为例,详细说明如何集成gRPC客户端
创建项目
创建web api项目,此步骤说明省略
引入nuget包
引入gRPC 客户端需要的 nuget包,Google.Protobuf 3.12.4、Grpc.Tools 2.30.0和Grpc.Net.ClientFactory 2.30.0
引入proto文件
将服务端的 order.proto 拷贝到客户端的web api项目中,并在csproj文件中添加ItemGroup节点。GrpcServices="Client"表示当前是客户端。改好后重新生成下项目。
<ItemGroup>
<Protobuf Include="Protos/OutpAggregation.proto" GrpcServices="Client" />
</ItemGroup>
修改Startup
在ConfigureServices方法中加入AddGrpcClient,代码如下
services.AddHttpContextAccessor();
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var baseUrl = "http://localhost:5001/";
services.AddGrpcClient<Order.OrderClient>(
options =>
{
options.Address = new Uri(baseUrl);
});
注意:要使用.NET Core客户端调用不安全的gRPC服务,需要进行其他配置。 gRPC客户端必须将System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport开关设置为true,并在服务器地址中使用http。可以在以下链接查看具体说明。
[Troubleshoot gRPC on .NET Core]
另外说明下services.AddGrpcClient方法,来自于nuget包Grpc.Net.ClientFactory 2.30.0,将gRPC客户端的注入封装,具体代码实现可以查看以下链接。
客户端调用
以在Controller中调用为例,示例代码如下
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly Order.OrderClient _orderClient;
public WeatherForecastController(Order.OrderClient orderClient)
{
_orderClient = orderClient;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
var result = await _orderClient.CreateOrderAsync(new CreateOrderRequest
{
ItemCode = "123",
ItemName = "名称1"
});
}
}
通过构造函数注入gRPC客户端,然后就可以使用里面的同步或者异步方法啦!
gRPC-微服务间通信实践的更多相关文章
- CAP-微服务间通信实践
微服务间通信常见的两种方式 由于微服务架构慢慢被更多人使用后,迎面而来的问题是如何做好微服务间通信的方案.我们先分析下目前最常用的两种服务间通信方案. gRPC(rpc远程调用) gRPC-微服务间通 ...
- .NET Core使用gRPC打造服务间通信基础设施
一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制. 利用这种传输机制,不同进程(或服务)间像调用本地进程中 ...
- 使用gRPC打造服务间通信基础设施
一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制. 利用这种传输机制,不同进程(或服务)间像调用本地进程中 ...
- eShopOnContainers 知多少[11]:服务间通信之gRPC
引言 最近翻看最新3.0 eShopOncontainers源码,发现其在架构选型中补充了 gRPC 进行服务间通信.那就索性也写一篇,作为系列的补充. gRPC 老规矩,先来理一下gRPC的基本概念 ...
- .NET Core 微服务学习与实践系列文章目录索引(2019版)
参考网址: https://archy.blog.csdn.net/article/details/103659692 2018年,我开始学习和实践.NET Core,并开始了微服务的学习,以及通过各 ...
- 浅谈服务间通信【MQ在分布式系统中的使用场景】
解决的问题 一项技术的产生必然是为了解决问题而生,了解了一项技术解决的问题,就能够很轻松的理解这项技术的设计根本,从而更好地理解与使用这项技术. 消息中间件和RPC从根本上来说都是为了解决分布式系统的 ...
- 007. 服务间通信 RPC & REST over HTTP(s) & 消息队列
服务间通信 服务间通信的几种方式: RPC.REST over HTTP(s).消息队列. https://www.jianshu.com/p/2a01d4383d0b RPC https://bl ...
- 从Uber微服务看最佳实践如何炼成?
导读:Uber成长非常迅速,工程师团队快速扩充,据说Uber有2000名工程师,8000个代码仓库,部署了1000多个微服务.微服务架构是Uber应对技术团队快速增长,功能快速上线很出色的解决方案.本 ...
- SpringCloud使用Feign实现服务间通信
SpringCloud的服务间通信主要有两种办法,一种是使用Spring自带的RestTemplate,另一种是使用Feign,这里主要介绍后者的通信方式. 整个实例一共用到了四个项目,一个Eurek ...
随机推荐
- Python 爬虫+tkinter界面 实现历史天气查询
文章目录 一.实现效果 1. python代码 2. 运行效果 二.基本思路 1. 爬虫部分 2. tkinter界面 一.实现效果 很多人学习python,不知道从何学起.很多人学习python,掌 ...
- facebookPixel代码安装详解
最近接到一个需求,优化独立站的facebookPixel代码,完成后对这个项目进行复盘.首先要介绍facebookPixel的理论知识. Facebook像素是一段JavaScript代码,其中加载了 ...
- Unity2018.4.7导出Xcode工程报错解决方案
1. unity导出xcode工程有两种模式,一种为模拟器运行的工程,一种为真机运行的工程,这里遇到的错误,都是导出模拟器运行工程时报的错误. 错误1: unity UnityMetalSupport ...
- 【转】对初学LoadRunner朋友的建议
对初学LoadRunner朋友的建议 作者:wind摘要:随着Internet的普及与迅速发展,企业业务量的迅速加大,数据大集中成为一种趋势,IT系统承载的负荷越来越重,系统性能的好坏严重的影响了企业 ...
- Infinite Inversions(树状数组+离散化)
思路及代码参考:https://blog.csdn.net/u014800748/article/details/45420085 There is an infinite sequence cons ...
- js map对象处理if
onButtonClick只有一个参数时候,map和object对象都可以 // onButtonClick1(3) onButtonClick只有一个参数时候,map和object对象都可以 con ...
- Oracle重做日志和日志挖掘
重做日志-Redo log 首先给出参考资料: 1.Oracle官网-Managing the Redo Log 为什么需要redo log 内存中数据修改后,不必立即更新到磁盘---效率 由日志完成 ...
- 设置logback的log文件地址为程序运行的当前目录
这个需求虽然怪异,却也不是无事生非,在以jar包为执行主体的程序中就会遇到. 设置方法就是指定Log_HOME为./,其在如下配置文件的第四行: <?xml version="1.0& ...
- Git 实用操作:撤销 Commit 提交
有的时候,改完代码提交 commit 后发现写得实在太烂了,连自己的都看不下去,与其修改它还不如丢弃重写.怎么操作呢? 使用 reset 撤销 如果是最近提交的 commit 要丢弃重写可以用 res ...
- leetcode刷题-91解码方法
题目 一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1'B' -> 2...'Z' -> 26给定一个只包含数字的非空字符串,请计算解码方法的总数. 示例 1: ...