.NET Core微服務開發服務間調用篇-GRPC

在單體應用中,相互調用都是在一個進程內部調用,也就是說調用發生在本機內部,因此也被叫做本地方法調用;在微服務中,服務之間調用就變得比較複雜,需要跨網路調用,他們之間的調用相對於與本地方法調用,可稱為遠程過程調用,簡稱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("//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進行服務間的調,最後使用一張圖作結。