我们是怎么实现gRPC CodeFirst
前言:
grpc默认是protofirst的,即先写 proto文件,再生成代码,需要人工维护proto,生成的代码也不友好,所以出现了grpc codefirst,下面来说说我们是怎么实现grpc codefirst
目录:
实现和wcf一样的codefirst
(1). 实现grpc codefirst, 简化wcf一定要抽取接口的问题
(2).
(3). 实现grpc dashboard,用于http远程调用和管理
(4). 实现服务注册与发现
(5). 实现分布式日志跟踪
(6). 日志监控等等
我们是怎么实现grpc codefirst
1.要实现codefirst先要了解protofirst生成的代码,下面我截了部分生成代码
(1).关键是这个bindservice,用于把实现的grpc方法绑定到serverservicedefinition
public static partial class greeter { static readonly string __servicename = "helloworld.greeter"; static readonly grpc::marshaller<global::helloworld.hellorequest> __marshaller_helloworld_hellorequest = grpc::marshallers.create((arg) => global::google.protobuf.messageextensions.tobytearray(arg), global::helloworld.hellorequest.parser.parsefrom); static readonly grpc::marshaller<global::helloworld.helloreply> __marshaller_helloworld_helloreply = grpc::marshallers.create((arg) => global::google.protobuf.messageextensions.tobytearray(arg), global::helloworld.helloreply.parser.parsefrom); static readonly grpc::method<global::helloworld.hellorequest, global::helloworld.helloreply> __method_sayhello = new grpc::method<global::helloworld.hellorequest, global::helloworld.helloreply>( grpc::methodtype.unary, __servicename, "sayhello", __marshaller_helloworld_hellorequest, __marshaller_helloworld_helloreply); static readonly grpc::method<global::helloworld.hellorequest, global::helloworld.helloreply> __method_sayhellostream = new grpc::method<global::helloworld.hellorequest, global::helloworld.helloreply>( grpc::methodtype.clientstreaming, __servicename, "sayhellostream", __marshaller_helloworld_hellorequest, __marshaller_helloworld_helloreply); /// <summary>creates service definition that can be registered with a server</summary> /// <param name="serviceimpl">an object implementing the server-side handling logic.</param> public static grpc::serverservicedefinition bindservice(greeterbase serviceimpl) { return grpc::serverservicedefinition.createbuilder() .addmethod(__method_sayhello, serviceimpl.sayhello) .addmethod(__method_sayhellostream, serviceimpl.sayhellostream).build(); } /// <summary>register service method with a service binder with or without implementation. useful when customizing the service binding logic. /// note: this method is part of an experimental api that can change or be removed without any prior notice.</summary> /// <param name="servicebinder">service methods will be bound by calling <c>addmethod</c> on this object.</param> /// <param name="serviceimpl">an object implementing the server-side handling logic.</param> public static void bindservice(grpc::servicebinderbase servicebinder, greeterbase serviceimpl) { servicebinder.addmethod(__method_sayhello, serviceimpl == null ? null : new grpc::unaryservermethod<global::helloworld.hellorequest, global::helloworld.helloreply>(serviceimpl.sayhello)); servicebinder.addmethod(__method_sayhellostream, serviceimpl == null ? null : new grpc::clientstreamingservermethod<global::helloworld.hellorequest, global::helloworld.helloreply>(serviceimpl.sayhellostream)); } }
(2).__marshaller_helloworld_hellorequest这个是实现protobuffer的序列化和反序列化的一个委托
服务的请求参数和返回参数,我们使用protobuf-net来实现序列化和反序列化,和 wcf一样需要给类打上标签
/// <summary> /// 加法请求参数 /// </summary> [protocontract] public class addrequest { /// <summary> /// 第一个数字 /// </summary> [protomember(1)] public int num1 { get; set; } /// <summary> /// 第二个数字 /// </summary> [protomember(2)] public int num2 { get; set; } }
2.要实现codefirst需要实现这个bindservice,把我们的grpc方法添加到serverservicedefinition
(1).我们让grpc服务实现igrpcservice空接口,用于标识是grpcservice
/// <summary> /// mathgrpc /// </summary> public class mathgrpc : igrpcservice { /// <summary> /// 加法 /// </summary> /// <param name="request"></param> /// <param name="context"></param> /// <returns></returns> public task<intmessage> add(addrequest request, servercallcontext context) { var result = new intmessage(); result.value = request.num1 + request.num2; return task.fromresult(result); } }
(2).获取实现了igrpcservice接口的类,然后反射获取方法,再添加到serverservicedefinition即可
这里调用了grpcmethodhelper.autoregistermethod()方法,这是通过反射自动注册grpcmethod的方法
/// <summary> /// 注入igrpcservice /// </summary> /// <param name="grpcservices"></param> /// <returns></returns> private serverbuilder usegrpcservice(ienumerable<igrpcservice> grpcservices) { var builder = serverservicedefinition.createbuilder(); grpcservices.tolist().foreach(grpc => grpcmethodhelper.autoregistermethod(grpc, builder)); _servicedefinitions.add(builder.build()); return this; }
未完,待续,欢迎评论拍砖
这些功能早在2018年就已经实现并运行在生产,感兴趣的同学可以去 github上查看,你要的都有,欢迎提issue