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>
其中,
-
節點:
<Protobuf Include="Protos\product.proto" GrpcServices="Server"/>
是設定 gRpc 的服務檔案,以便 grpc.tools 該檔案自動生成服務端程式碼
-
節點:
<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"
}