欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

我们是怎么实现gRPC CodeFirst

程序员文章站 2022-04-27 18:38:10
前言: gRPC默认是ProtoFirst的,即先写 proto文件,再生成代码,需要人工维护proto,生成的代码也不友好,所以出现了gRPC CodeFirst,下面来说说我们是怎么实现gRPC CodeFirst 目录: 实现和WCF一样的CodeFirst (1). 实现gRPC CodeF ......

前言:

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