1. 程式人生 > 其它 >Abp vnext 微服務架構下整合 gRpc

Abp vnext 微服務架構下整合 gRpc

Abp 整合 gRpc

gRpc 服務端 : 微服務 IotHub

建立微服務

建立微服務 IotHub

abp new Artisan.IotHub -u none -d ef -dbms SqlServer --separate-identity-server --version 5.2.1

引用 gRpc 包

專案【Artisan.IotHub.Application】引用 gRpc 服務端需要的包

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.20.1" />
    <PackageReference Include="Grpc.Core" Version="2.46.1" />
    <PackageReference Include="Grpc.Tools" Version="2.46.1">
   <ItemGroup>

新增 *.proto 檔案

在專案【Artisan.IotHub.Application】新建資料夾 Protos, 然後在該資料夾下新增一個 product.proto檔案,內容如下:

程式碼清單: Artisan.IotHub.Application/Protos/product.proto

syntax = "proto3";

option csharp_namespace = "Artisan.IotHub.Grpc";

package IotHubApi;

service ProductPublic {
	rpc GetById(ProductRequest) returns (ProductResponse);
}

message ProductRequest {
	string id = 1;
}

message ProductResponse {
	string id = 1;
	string name = 2;
}

修改專案檔案

右鍵專案【Artisan.IotHub.Application】的工程檔案,在彈出的選單中選擇【編譯專案檔案】,新增如下內容:

  <ItemGroup>
    <Protobuf Include="Protos\product.proto" GrpcServices="Server"/>
    <Content Include="@(Protobuf)" />
    <None Remove="@(Protobuf)" />
  </ItemGroup>

其中,

  1. 節點:

    <Protobuf Include="Protos\product.proto" GrpcServices="Server"/>
    

    是設定 gRpc 的服務檔案,以便 grpc.tools 該檔案自動生成服務端程式碼

  2. 節點:

        <Content Include="@(Protobuf)" />
        <None Remove="@(Protobuf)" />
    

    *.proto 檔案的屬性設定為:【內容】、【不復制】

新建 gRpc 服務

在專案【Artisan.IotHub.Application】下新建資料夾 Grpc, 在該資料夾下新建類PublicProductGrpcService

程式碼清單:Artisan.IotHub.Application/Grpc/PublicProductGrpcService.cs

   using Grpc.Core;
   using System.Threading.Tasks;
   
   namespace Artisan.IotHub.Grpc
   {
       public class PublicProductGrpcService : ProductPublic.ProductPublicBase
       {
           public PublicProductGrpcService()
           {
           }
   
           public override async Task<ProductResponse> GetById(
               ProductRequest request, 
               ServerCallContext context)
           {
               return new ProductResponse
               {
                   Id = request.Id,
                   Name = "TestProduct"
               };
           }
       }
   }
   

其中,

ProductPublic.ProductPublicBase 是由 grpc.tools 根據 product.proto 檔案自動生成的。

如果沒有自動生成,嘗試編譯專案【Artisan.IotHub.Application】

ApsNet Core 整合 gRpc

引用包

專案【Artisan.IotHub.HttpApi.Host】引入如下包:

    <PackageReference Include="Grpc.AspNetCore.Server" Version="2.46.0" />

新增服務

程式碼清單:程式碼清單:Artisan.IotHub.HttpApi.Host/IotHubHttpApiHostModule.cs

public class IotHubHttpApiHostModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //......
        ConfigureGrpc(context, configuration);
    }
    
    /// <summary>
    /// 將 gRPC 服務新增到 ASP.NET Core 應用
    /// https://docs.microsoft.com/zh-cn/aspnet/core/grpc/aspnetcore?view=aspnetcore-6.0&tabs=visual-studio
    /// </summary>
    /// <param name="context"></param>
    /// <param name="configuration"></param>
    private void ConfigureGrpc(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddGrpc(options =>
        {
            options.EnableDetailedErrors = true;
        });
    }
}

新增 gRpc 服務 Endpoints

程式碼清單:程式碼清單:Artisan.IotHub.HttpApi.Host/IotHubHttpApiHostModule.cs

using Artisan.IotHub.Grpc;

public class IotHubHttpApiHostModule : AbpModule
{
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        //......
        app.UseConfiguredEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<PublicProductGrpcService>();
        });
    }
}

設定 gRpc 的埠

在配置檔案 appsettings.json中新增微服務IotHub的埠:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:7059",
        "Protocols": "Http1AndHttp2" //有些中介軟體走的還是http1.0,故要相容
      },
      "Https": {
        "Url": "https://localhost:44359",
        "Protocols": "Http1AndHttp2" // 有些中介軟體走的還是http1.0,故要相容
      },
      "gRPC": {
        "Url": "http://localhost:81",
        "Protocols": "Http2" // gRPC是基於Http2的,必須設定為Http2
      }
    }
  },
  // ......
}

特別注意:

gRPC 是基於 Http2的,而專案中的Http 和 Https ,有些功能還是基於 Http1.0的,

故需要通過配置節點:Protocols來分別設定它們的協議。

gRpc客戶端:微服務 IotEdge

建立微服務

建立微服務 IotEdge

abp new Artisan.IotEdge -u none -d ef -dbms SqlServer --separate-identity-server --version 5.2.1

引用包

專案【Artisan.IotEdge.Application】引入如下包:

    <PackageReference Include="Google.Protobuf" Version="3.20.1" />
    <PackageReference Include="Grpc.Core" Version="2.46.1" />
    <PackageReference Include="Grpc.Tools" Version="2.46.1">

*.proto 檔案

拷貝式

可以將專案【 Artisan.IotHub.Application】下的 Artisan.IotHub.Application/Protos/product.proto 檔案拷貝到專案【Artisan.IotEdge.Application】中,然後在專案【Artisan.IotEdge.Application】檔案中新增:

  <ItemGroup>
    <Protobuf Include="Protos\product.proto" GrpcServices="Client"/>
  </ItemGroup>

注意:GrpcServices="Client" 是 Client,而不是:Server

但是這樣做有點麻煩,當專案【 Artisan.IotHub.Application】的 Artisan.IotHub.Application/Protos/product.proto 檔案修改了,還得拷貝一次,比較麻煩而且容易因為遺忘而出錯。比較穩妥的做法是直接引用,而不是拷貝。

引用式

通過引用的方式,新增 *.proto 檔案

在專案【Artisan.IotEdge.Application】檔案中新增:

  <ItemGroup>
    <Protobuf Include="..\..\..\iot-hub\src\Artisan.IotHub.Application\Protos\product.proto" GrpcServices="Client" />
  </ItemGroup>

這樣就不用拷貝專案【 Artisan.IotHub.Application】下的 Artisan.IotHub.Application/Protos/product.proto 檔案了

編寫呼叫 gRpc 服務

服務介面

在專案【Artisan.IotEdge.Application.Contracts】中新增如下服務介面,
程式碼清單:Artisan.IotEdge.Application.Contracts/Products/IProductService

    public interface IProductService
    {
        Task<ProductDto> GetAsync(Guid productId);
    }

新增DTO:

    public class ProductDto : AuditedEntityDto<Guid>
    {
        public string Name { get; set; }
    }

新增AutoMapper
程式碼清單:Artisan.IotEdge.Application.Contracts/IotEdgeApplicationAutoMapperProfile

public class IotEdgeApplicationAutoMapperProfile : Profile
{
    public IotEdgeApplicationAutoMapperProfile()
    {
        CreateMap<ProductResponse, ProductDto>();
    }
}

服務實現

在專案【Artisan.IotEdge.Application.Contracts】中新增如下服務實現,

程式碼清單:Artisan.IotEdge.Application/Products/ProductService

    public class ProductService : ApplicationService, IProductService
    {
        private readonly ILogger<ProductService> _logger;
        private readonly IObjectMapper _mapper;
        private readonly ProductPublic.ProductPublicClient _productPublicGrpcClient;

        public ProductService(
            ILogger<ProductService> logger,
            IObjectMapper mapper,
             ProductPublic.ProductPublicClient productPublicGrpcClient)
        {
            _logger = logger;
            _mapper = mapper;
            _productPublicGrpcClient = productPublicGrpcClient;
        }

        public async Task<ProductDto> GetAsync(Guid productId)
        {
            var request = new ProductRequest { Id = productId.ToString() };
            _logger.LogInformation("=== GRPC request {@request}", request);
            var response = await _productPublicGrpcClient.GetByIdAsync(request);
            _logger.LogInformation("=== GRPC response {@response}", response);
            return _mapper.Map<ProductResponse, ProductDto>(response) ??
                   throw new UserFriendlyException(IotEdgeDomainErrorCodes.ProductNotFound);
        }
    }

其中:

  var response = await _productPublicGrpcClient.GetByIdAsync(request);

就是呼叫微服務:IotHub 的 gRpc 的服務

ApsNet Core 整合 gRpc

引用包

在專案【Artisan.IotEdge.HttpApi.Host】中引用如下包:

    <PackageReference Include="Grpc.Net.ClientFactory" Version="2.46.0" />

新增 gRpc 客戶端

程式碼清單:Artisan.IotEdge.HttpApi.Host/IotEdgeHttpApiHostModule.cs

public class IotEdgeHttpApiHostModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        // ......
        ConfigureGrpc(context, configuration);
    }
    
        private void ConfigureGrpc(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddGrpcClient<ProductPublic.ProductPublicClient>((services, options) =>
        {
            var iothubGrpcUrl = configuration["RemoteServices:IotHub:GrpcUrl"];
            options.Address = new Uri(iothubGrpcUrl);
        });
    }
}

在配置檔案 appsettings.json中新增微服務IotHub的配置:

  "RemoteServices": {
    "IotHub": {
      "BaseUrl": "https://localhost:44359",
      "GrpcUrl": "http://localhost:81"
    }
  },

測試

啟動微服務:IotHub

啟動微服務:IotEdge

在 IotEdge 的Swagger 中呼叫 微服務:IotEdge 的

微服務IotEdge 呼叫 微服務IotHub 的示意圖如下所示:

返回結果:

{
  "name": "TestProduct",
  "lastModificationTime": null,
  "lastModifierId": null,
  "creationTime": "0001-01-01T00:00:00",
  "creatorId": null,
  "id": "9af7f46a-ea52-4aa3-b8c3-9fd484c2af12"
}