河南焦作有做网站开发的公司吗,wordpress 书架,wordpress 新手教程,网站建设关键要素1. 引言最近翻看最新3.0 eShopOncontainers源码#xff0c;发现其在架构选型中补充了 gRPC 进行服务间通信。那就索性也写一篇#xff0c;作为系列的补充。2. gRPC老规矩#xff0c;先来理一下gRPC的基本概念。gRPC是Google开源的RPC框架#xff0c;比肩dubbo、thrift、brp… 1. 引言最近翻看最新3.0 eShopOncontainers源码发现其在架构选型中补充了 gRPC 进行服务间通信。那就索性也写一篇作为系列的补充。2. gRPC老规矩先来理一下gRPC的基本概念。gRPC是Google开源的RPC框架比肩dubbo、thrift、brpc。其优势在于基于proto buffer二进制协议具有高性能的序列化机制。相较于JSON文本协议而言首先从数据包上就有60%-80%的减小其次其解包速度仅需要简单的数学运算完成无需复杂的词法语法分析具有8倍以上的性能提升。基于proto 文件可以更方便的在客户端和服务端之间进行交互。gRPC语言无关性 所有服务都是使用原型文件定义的。这些文件基于protobuffer语言并定义服务的接口。基于原型文件可以为每种语言生成用于创建服务端和客户端的代码。其中protoc编译工具就支持将其生成C #代码。从.NET Core 3 中gRPC在工具和框架中深度集成开发者会有更好的开发体验。支持数据流。3. gRPC 在 eShopOncontainers 的应用首先来理一下eShopOncontainers 中服务间同步通信的技术选型主要还是是基于HTTP/RESTgRPC作为补充。在eShopOncontainers中Ordering API、Catalog API、Basket API微服务通过gRPC端点暴露服务。其中Mobile Shopping、Web Shopping BFFs使用gRPC客户端访问服务。以下以Ordering API gRPC 服务举例说明。订单微服务中定义了一个gRPC服务用于从购物车创建订单。3.1 服务端实现proto文件定义如下syntax proto3;
option csharp_namespace GrpcOrdering;
package OrderingApi;
service OrderingGrpc {rpc CreateOrderDraftFromBasketData(CreateOrderDraftCommand) returns (OrderDraftDTO) {}
}
message CreateOrderDraftCommand {string buyerId 1;repeated BasketItem items 2;
}
message BasketItem {string id 1;int32 productId 2;string productName 3;double unitPrice 4;double oldUnitPrice 5;int32 quantity 6;string pictureUrl 7;
}
message OrderDraftDTO {double total 1;repeated OrderItemDTO orderItems 2;
}
message OrderItemDTO {int32 productId 1;string productName 2;double unitPrice 3;double discount 4;int32 units 5;string pictureUrl 6;
}
服务实现主要是借助Mediator充当CommandBus进行命令分发具体实现如下namespace GrpcOrdering
{public class OrderingService : OrderingGrpc.OrderingGrpcBase{private readonly IMediator _mediator;private readonly ILoggerOrderingService _logger;public OrderingService(IMediator mediator, ILoggerOrderingService logger){_mediator mediator;_logger logger;}public override async TaskOrderDraftDTO CreateOrderDraftFromBasketData(CreateOrderDraftCommand createOrderDraftCommand, ServerCallContext context){_logger.LogInformation(Begin gRPC call from method {Method} for ordering get order draft {CreateOrderDraftCommand}, context.Method, createOrderDraftCommand);_logger.LogTrace(----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({Command}),createOrderDraftCommand.GetGenericTypeName(),nameof(createOrderDraftCommand.BuyerId),createOrderDraftCommand.BuyerId,createOrderDraftCommand);var command new AppCommand.CreateOrderDraftCommand(createOrderDraftCommand.BuyerId,this.MapBasketItems(createOrderDraftCommand.Items));var data await _mediator.Send(command);if (data ! null){context.Status new Status(StatusCode.OK, $ ordering get order draft {createOrderDraftCommand} do exist);return this.MapResponse(data);}else{context.Status new Status(StatusCode.NotFound, $ ordering get order draft {createOrderDraftCommand} do not exist);}return new OrderDraftDTO();}public OrderDraftDTO MapResponse(AppCommand.OrderDraftDTO order){var result new OrderDraftDTO(){Total (double)order.Total,};order.OrderItems.ToList().ForEach(i result.OrderItems.Add(new OrderItemDTO(){Discount (double)i.Discount,PictureUrl i.PictureUrl,ProductId i.ProductId,ProductName i.ProductName,UnitPrice (double)i.UnitPrice,Units i.Units,}));return result;}public IEnumerableApiModels.BasketItem MapBasketItems(RepeatedFieldBasketItem items){return items.Select(x new ApiModels.BasketItem(){Id x.Id,ProductId x.ProductId,ProductName x.ProductName,UnitPrice (decimal)x.UnitPrice,OldUnitPrice (decimal)x.OldUnitPrice,Quantity x.Quantity,PictureUrl x.PictureUrl,});}}
}
同时服务端还要注册gRPC的请求处理管道app.UseEndpoints(endpoints
{endpoints.MapDefaultControllerRoute();endpoints.MapControllers();endpoints.MapGrpcServiceOrderingService();
});
3.2 客户端调用接下来看下客户端[web.bff.shopping]怎么消费的public class OrderingService : IOrderingService{private readonly UrlsConfig _urls;private readonly ILoggerOrderingService _logger;public readonly HttpClient _httpClient;public OrderingService(HttpClient httpClient, IOptionsUrlsConfig config, ILoggerOrderingService logger){_urls config.Value;_httpClient httpClient;_logger logger;}public async TaskOrderData GetOrderDraftAsync(BasketData basketData){return await GrpcCallerService.CallService(_urls.GrpcOrdering, async channel {var client new OrderingGrpc.OrderingGrpcClient(channel);_logger.LogDebug( gRPC client created, basketData{basketData}, basketData);var command MapToOrderDraftCommand(basketData);var response await client.CreateOrderDraftFromBasketDataAsync(command);_logger.LogDebug( gRPC response: {response}, response);return MapToResponse(response, basketData);});}private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData){if (orderDraft null){return null;}var data new OrderData{Buyer basketData.BuyerId,Total (decimal)orderDraft.Total,};orderDraft.OrderItems.ToList().ForEach(o data.OrderItems.Add(new OrderItemData{Discount (decimal)o.Discount,PictureUrl o.PictureUrl,ProductId o.ProductId,ProductName o.ProductName,UnitPrice (decimal)o.UnitPrice,Units o.Units,}));return data;}private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData){var command new CreateOrderDraftCommand{BuyerId basketData.BuyerId,};basketData.Items.ForEach(i command.Items.Add(new BasketItem{Id i.Id,OldUnitPrice (double)i.OldUnitPrice,PictureUrl i.PictureUrl,ProductId i.ProductId,ProductName i.ProductName,Quantity i.Quantity,UnitPrice (double)i.UnitPrice,}));return command;}}
其中 GrpcCallerService是对gRPC Client的一层封装主要是为了解决未启用TLS无法使用gRPC的问题。4. 不启用TLS使用gRPC我们已经知道gRpc 是基于HTTP2.0 协议。然而连接的建立默认并不是一步到位直接基于HTTP2.0建立连接的。客户端是先基于HTTP1.1进行协议协商协商成功后确认服务端支持HTTP2.0后才会建立HTT2.0连接协议协商需要TLS的ALPN协议来实现。流程如下这意味着默认情况下您需要启用TLS协议才能完成HTTP2.0协议协商进而才能使用gRPC。然而在微服务架构中并不是所有服务都需要启用安全传输层协议尤其是微服务间的内部调用。那么在微服务内部如何使用gRPC进行通信呢客户端绕过协议协商直连HTTP2.0前提是服务端必须支持HTTP2.0。服务端配置如下WebHost.CreateDefaultBuilder(args).ConfigureKestrel(options {options.Listen(IPAddress.Any, ports.httpPort, listenOptions {listenOptions.Protocols HttpProtocols.Http1AndHttp2; //同时监听协议HTTP1HTTP2});options.Listen(IPAddress.Any, ports.gRPCPort, listenOptions {listenOptions.Protocols HttpProtocols.Http2; // gRPC端口仅监听HTTP2.0});})
客户端需要添加以下设置这些设置只能在客户端开始时设置一次AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport, true);
AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2Support, true);
知道了这些再回过来看 GrpcCallerService的实现就一目了然了。public static class GrpcCallerService
{public static async TaskTResponse CallServiceTResponse(string urlGrpc, FuncGrpcChannel, TaskTResponse func)
{AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport, true);AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2Support, true);var channel GrpcChannel.ForAddress(urlGrpc);/*using var httpClientHandler new HttpClientHandler{ServerCertificateCustomValidationCallback (message, cert, chain, errors) { return true; }};*/Log.Information(Creating gRPC client base address urlGrpc {urlGrpc}, BaseAddress{BaseAddress} , urlGrpc, channel.Target);try{return await func(channel);}catch (RpcException e){Log.Error(Error calling via gRPC: {Status} - {Message}, e.Status, e.Message);return default;}finally{AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport, false);AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2Support, false);}}public static async Task CallService(string urlGrpc, FuncGrpcChannel, Task func)
{AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport, true);AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2Support, true);/*using var httpClientHandler new HttpClientHandler{ServerCertificateCustomValidationCallback (message, cert, chain, errors) { return true; }};*/var channel GrpcChannel.ForAddress(urlGrpc);Log.Debug(Creating gRPC client base address {httpClient.BaseAddress} , channel.Target);try{await func(channel);}catch (RpcException e){Log.Error(Error calling via gRPC: {Status} - {Message}, e.Status, e.Message);}finally{AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport, false);AppContext.SetSwitch(System.Net.Http.SocketsHttpHandler.Http2Support, false);}}
}
5. 最后本文简要介绍了 eShopOnContainers 如何通过集成 gRPC 来完善服务间同步通信机制希望对你在对微服务进行RPC相关技术选型时有一定的启示和帮助。参考资料[HTTP2.0笔记之连接建立http://www.blogjava.net/yongboy/archive/2015/03/18/423570.html][eShopOnContainers/wiki/gRPChttps://github.com/dotnet-architecture/eShopOnContainers/wiki/gRPC][Google Protocol Buffer 的使用和原理https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html]