在单体应用中,相互调用都是在一个进程内部调用,也就是说调用发生在本机内部,因此也被叫做本地方法调用;在微服务中,服务之间调用就变得比较复杂,需要跨网络调用,他们之间的调用相对于与本地方法调用,可称为远程过程调用,简称RPC(Remote procedure call)。
看过上篇API网关篇,知道案例中包含商品、订单两个微服务,本文将会演示如何采用开源的,高性能rpc框架(grpc),通过订单微服务调用产品微服务内的接口。没有看过上篇文章不影响,我先提供下项目代码结构图,方便你阅读。下面将会一步一步分享如何使用Grpc进行服务之间调用。
步骤1:首先定义服务锲约-proto文件
1.创建类库(.NET Standard),作为服务契约项目,命名为-AAStore.ProductCatalog.DataContracts如图:
2.安装三个nuget包
Google.Protobuf
Grpc
Grpc.Tools
3.开始定义proto文件:product.api.proto
syntax = "proto3"; option csharp_namespace = "AAStore.ProductCatalog.Api.V1"; package AAStore; service ProductApi{ rpc GetProduct(GetProductRequest) returns (GetProductResponse); } //请求消息体 message GetProductRequest{ int32 Id=1; } //返回消息体 message GetProductResponse{ string productName=1; }
Grpc协议使用Protobuf简称proto文件来定义接口名、调用参数以及返回值类型。比如product.api.proto文件,定义一个接口GetProduct方法,它的请求结构体是GetProductRequest,包含一个int类型的id属性,它的返回结构体GetProductResponse包含一个输出string类型的产品名称属性。
4.添加product.api.proto文件到项目中,双击项目或者右键-》编辑项目文件,添加一下代码
<ItemGroup> <ProjectReference Include="..\AAStore.ProductCatalog\AAStore.ProductCatalog.csproj" /> </ItemGroup>
然后build项目,此时gprc代码已经生成了,在obj文件项目,如图
步骤2:Grpc服务端实现
选择项目AAStore.ProductCatalog(详情见项目代码结构图)安装包Grpc.AspNetCore,同时添加引用项目AAStore.ProductCatalog.DataContracts
<ItemGroup> <PackageReference Include="Grpc.AspNetCore" Version="2.29.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AAStore.ProductCatalog.DataContracts\AAStore.ProductCatalog.DataContracts.csproj" /> </ItemGroup>
然后定义获取产品方法的逻辑和实现,供产品api站点项目调用
public class GrpcProductServices { public Task<GetProductResponse> GetProduct(GetProductRequest request, ServerCallContext context) { return Task.FromResult(new GetProductResponse { //todo 具体的逻辑 下面代码仅为显示 ProductName = "测试商品grpc" }) ; } }
选择AAStore.ProductCatalog.Api产品api项目添加引用项目AAStore.ProductCatalog
<ItemGroup> <ProjectReference Include="..\AAStore.ProductCatalog\AAStore.ProductCatalog.csproj" /> </ItemGroup>
新增ProductServices.cs并继承ProductApiBase类,实现grpc服务
public class ProductServices : ProductApi.ProductApiBase { public override Task<GetProductResponse> GetProduct(GetProductRequest request, ServerCallContext context) { return new GrpcProductServices().GetProduct(request, context); } }
由于AAStore.ProductCatalog.Api项目不是通过grpc模板项目创建的,所以在Startup类中手工添加gprc服务和中间件代码:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddGrpc(); } app.UseEndpoints(endpoints => { endpoints.MapGrpcService<ProductServices>(); endpoints.MapControllers(); });
最后在Program文件配置站点启动监听8081端口,我们并运行它
webBuilder.ConfigureKestrel(options => { // Setup a HTTP/2 endpoint without TLS. options.ListenLocalhost(8081, o => o.Protocols = HttpProtocols.Http2); });
步骤3:Grpc客户端实现-调用Grpc服务
选择AAStore.Orde项目(具体见项目代码结构图),安装Grpc.AspNetCore包,并且添加项目引用AAStore.ProductCatalog.DataContracts
<ItemGroup>
<ProjectReference Include="..\AAStore.ProductCatalog.DataContracts\AAStore.ProductCatalog.DataContracts.csproj" />
</ItemGroup>
新建ProductGateway.cs,封装产品微服务公开grpc服务的调用
public class ProductGateway { private readonly ProductApiClient _client; public ProductGateway() { AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); _client = new ProductApiClient(GrpcChannel.ForAddress("http://localhost:8081"));//TODO 根据动态配置 } public async Task<GetProductResponse> GetProduct(GetProductRequest request) { return await _client.GetProductAsync(request); } }
新建OrderService.cs,添加GetOrder方法,在其方法内调用产品微服务GetProduct方法
public async Task<string> GetOrder() { var productModel = await _productGateway.GetProduct(new GetProductRequest() { Id = 1 }); return $"Order Service=>从产品微服务获取产品信息:{productModel.ProductName}"; }
然后在订单控制器中调用
[Route("api/[controller]")] [ApiController] public class OrderController : ControllerBase { private readonly RestOrderService _restOrderService; public OrderController() { _restOrderService = new RestOrderService(); } [HttpGet(template:"Get")] public async Task<string> GetOrder() { return await _restOrderService.GetOrder(); } }
至此,客户端代码已经完成,我们运行来看看
通过网关访问订单服务,查看调用结果
我们发现结果也是一样的,以上演示了如何使用grpc进行服务间的调用,最后使用一张图作结。