原文在本人公众号中,欢迎关注我,时不时的会分享一些心得
HTTP和RPC是现代微服务架构中很常用的数据传输方式,两者有很多相似之处,但是又有很大的不同。HTTP是一种规范性、通用性、非常标准的传输协议,几乎所有的语言都支持,如果要确保各平台无缝衔接,可以考虑使用HTTP协议,例如现在常规的RestFUL,整个传输过程通常使用Json数据格式。以至于不管是前端还是后端都可以很好的对接。
RPC协议不仅仅是服务间通信协议,甚至是进程间也存在。可以降低诸多微服务之间调用成本,屏蔽了通讯细节,调用远程方法处理数据时像调用本地方法一样丝滑顺畅。但是对前端和浏览器不是特别友好。
GRPC是google制定实现的一种Rpc形式,官网解释是:
gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
双向流和集成身份验证:基于HTTP/2的传输机制以及双向流和而完全可集成的插入式身份验证机制。
ProtoBuf协议:
该协议是一种序列化结构化数据的灵活,高效,自动化的机制,比xml更小,更快,更简单。您定义要一次构造数据的方式,然后可以使用生成的特殊源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。您甚至可以更新数据结构,而不会破坏已针对“旧”格式编译的已部署程序。
协议文件定义方式:
这是一个简单的定义,此文件在.proto文件中定义,与语言无关。每个消息类型都有一个或多个唯一编号的字段。每个字段都有一个名称和一个值类型,其中值类型可以是数字(整数或浮点数),布尔值,字符串等。定义好后可以使用编译器生成对应语言的操作对象。
更多协议内容请参阅:https://developers.google.com/protocol-buffers/docs/overview。
<ItemGroup> <Protobuf Include="Protos\greet.proto" GrpcServices="Client" /> </ItemGroup>
我本人习惯将生成的server和client放在一起,打包后供client端和server端使用,只需要这样设置:
<ItemGroup> <Protobuf Include="Protos\greet.proto" GrpcServices="Server;Client" /> </ItemGroup>
配置好后重新生成项目,grpctool会自动生成服务端,客户端代码。
client调用方式(asp.net core gRPC):
var channel = GrpcChannel.ForAddress("https://localhost:5001");var client = new Greeter.GreeterClient(channel);var response = await client.SayHello( new HelloRequest { Name = "World" });Console.WriteLine(response.Message);
<PackageReference Include="Google.Protobuf" Version="3.10.1" /> <PackageReference Include="Grpc" Version="2.24.0" /> <PackageReference Include="Grpc.Tools" Version="2.24.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
public class DemoService: GrpcExampleService.GrpcExampleServiceBase { public override Task<AskResponse> Ask(AskRequest request, ServerCallContext context) { return Task.FromResult(new AskResponse {Content = "Hello from Ask"}); } public override Task<ResponseModel> GetName(RequestModel request, ServerCallContext context) { return Task.FromResult(new ResponseModel { Name = "Hello Pluto" }); } }
public class GrpcServer:IHostedService { private readonly GrpcExampleService.GrpcExampleServiceBase _sampleServiceBase; public GrpcServer(GrpcExampleService.GrpcExampleServiceBase sampleServiceBase) { _sampleServiceBase = sampleServiceBase; } /// <summary> /// 启动自己定义的rpc服务 /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public Task StartAsync(CancellationToken cancellationToken) { return Task.Factory.StartNew(() => { GrpcServiceManager.Start(GrpcExampleService.BindService(_sampleServiceBase)); }, cancellationToken); } /// <summary> /// 停止 /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public Task StopAsync(CancellationToken cancellationToken) { return Task.Factory.StartNew(() => { GrpcServiceManager.Stop(); }, cancellationToken); } }
GrpcServiceManager中就是真正的启动和停止grpc服务:
public class GrpcServiceManager { static Server server; public static void Start(ServerServiceDefinition bindService) { if (bindService == null) throw new ArgumentNullException("bindService"); var services = new List<ServerServiceDefinition>() { bindService }; Start(services); //todo 添加配置,拦截器等等 } private static void Start(List<ServerServiceDefinition> services) { try { server = new Server() { Ports = { new ServerPort("0.0.0.0", 8890, ServerCredentials.Insecure) } }; foreach (var service in services) { server.Services.Add(service); } server.Start(); //todo consul 注册 } catch (Exception e) { Console.WriteLine(e); throw; } } public static void Stop() { try { server?.ShutdownAsync().Wait(); } catch (Exception e) { Console.WriteLine(e); throw; } } }
注意:这里应该将proto协议 服务端客户端端进行分离,协议生成的代码打包后 被客户端项目和服务端项目引用,这里只是为了简便客户端直接会引用服务,因为要用到GrpcExampleService.GrpcExampleServiceClient
上边的todo 后再后续增加对应的功能。
这样服务端就创建完了。接下来就是创建客户端进行调用:
控制台客户端:
main中实现调用逻辑
static void Main(string[] args) { var channel=new Grpc.Core.Channel("127.0.0.1",8890,ChannelCredentials.Insecure); var democlient=new GrpcExampleService.GrpcExampleServiceClient(channel); var res= democlient.Ask(new AskRequest { Cate = 0, Key = "1312" }); var res2 = democlient.GetName(new RequestModel { Key = "1313" }); Console.WriteLine($"Ask={res}.GetName={res2}"); }
然后就可以了,启动服务端,然后再启动客户端,就可以看到调用结果了。