微服务间通信常见的两种方式

由于微服务架构慢慢被更多人使用后,迎面而来的问题是如何做好微服务间通信的方案。我们先分析下目前最常用的两种服务间通信方案。

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客户端的注入封装,具体代码实现可以查看以下链接。

Grpc.Net.ClientFactory

客户端调用

以在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-微服务间通信实践的更多相关文章

  1. CAP-微服务间通信实践

    微服务间通信常见的两种方式 由于微服务架构慢慢被更多人使用后,迎面而来的问题是如何做好微服务间通信的方案.我们先分析下目前最常用的两种服务间通信方案. gRPC(rpc远程调用) gRPC-微服务间通 ...

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

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

  3. 使用gRPC打造服务间通信基础设施

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

  4. eShopOnContainers 知多少[11]:服务间通信之gRPC

    引言 最近翻看最新3.0 eShopOncontainers源码,发现其在架构选型中补充了 gRPC 进行服务间通信.那就索性也写一篇,作为系列的补充. gRPC 老规矩,先来理一下gRPC的基本概念 ...

  5. .NET Core 微服务学习与实践系列文章目录索引(2019版)

    参考网址: https://archy.blog.csdn.net/article/details/103659692 2018年,我开始学习和实践.NET Core,并开始了微服务的学习,以及通过各 ...

  6. 浅谈服务间通信【MQ在分布式系统中的使用场景】

    解决的问题 一项技术的产生必然是为了解决问题而生,了解了一项技术解决的问题,就能够很轻松的理解这项技术的设计根本,从而更好地理解与使用这项技术. 消息中间件和RPC从根本上来说都是为了解决分布式系统的 ...

  7. 007. 服务间通信 RPC & REST over HTTP(s) & 消息队列

    服务间通信 服务间通信的几种方式: RPC.REST over HTTP(s).消息队列.  https://www.jianshu.com/p/2a01d4383d0b RPC https://bl ...

  8. 从Uber微服务看最佳实践如何炼成?

    导读:Uber成长非常迅速,工程师团队快速扩充,据说Uber有2000名工程师,8000个代码仓库,部署了1000多个微服务.微服务架构是Uber应对技术团队快速增长,功能快速上线很出色的解决方案.本 ...

  9. SpringCloud使用Feign实现服务间通信

    SpringCloud的服务间通信主要有两种办法,一种是使用Spring自带的RestTemplate,另一种是使用Feign,这里主要介绍后者的通信方式. 整个实例一共用到了四个项目,一个Eurek ...

随机推荐

  1. 使用xShell 连接 docker 使用说明

    方式一:当不知道docker里镜像的root密码的时候 1.从Docker Hub下载需要的镜像 docker pull 镜像名字 2.使用docker run命令启动容器 docker run -i ...

  2. Unity使用可空类型(Nullable Types)

    译林军 范春彦|2014-04-09 09:46|5407次浏览|Unity(375)0 你怎么确定一个Vector3,int,或float变量是否被分配了一个值?一个方便的方式就是使用可空类型! 有 ...

  3. Lua 调用的 C 函数保存 state 的两种方式: Storing State in C Functions 笔记

    http://yanbin.is-programmer.com/posts/94214.html Registery的Key 1. 整数Key用于Lua的引用机制,所以不要使用整数作为Key 2. 通 ...

  4. 石子合并(区间dp典型例题)

    Description 有n堆石子排成一行,每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和.已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分. Input ...

  5. cannary

    canary是Linux为防止栈溢出的一种保护机制,接着我们分析glibc对canary的实现过程,首先给出跟canary相关的调用栈: security_init()  //在elf/rtld.c中 ...

  6. Java查找指定文件中指定字符的个数

    package lwl.youweb2.test; import java.io.BufferedReader; import java.io.FileReader; import java.io.I ...

  7. vue 实现原理及简单示例实现

    目录 相关html代码,用于被解析绑定数据 observer代码 Dep代码 Watcher 代码 Compile 代码 vue 简要构造函数 创建vue实例 结语 主要理解.实现如下方法: Obse ...

  8. 企业网站还是要考虑兼容至少IE10

    中国国情,大部分企业还在使用win7,IE浏览器.为了兼容这些,还是少用比较VUE等一些高级的框架,改为使用jquery.用惯了VUE,jquey好多忘得差不多了,其中遇到的问题及解决方案 ajax, ...

  9. jmeter服务器监控磁盘IO、网络-PerfMon Metrics Collector

    1.jmeetr客户端安装jp@gc - PerfMon Metrics Collector 先安装jmeter-plugins 启动jmeter ——> 客户端选项 ——> jmeter ...

  10. Linked List 单向链表

    Linked List 链表的理解 小结 链表是以节点的方式来储存的 每个节点包括 data域:存放数据,next域:指向下一个节点 如图:发现链表的各个节点不一定是连续储存的 链表分为带头节点的链表 ...