天天看點

我們是怎麼實作Grpc CodeFirst

我們是怎麼實作Grpc CodeFirst

前言:

Grpc預設是ProtoFirst的,即先寫 proto檔案,再生成代碼,需要人工維護proto,生成的代碼也不友好,是以出現了Grpc CodeFirst,下面來說說我們是怎麼實作Grpc CodeFirst

目錄:

實作和WCF一樣的CodeFirst

(1). 實作Grpc CodeFirst,  簡化WCF一定要抽取接口的問題

(2). 通過代碼生成proto和注釋,給第三方語言使用

(3). 實作Grpc DashBoard,用于Http遠端調用和管理

(4). 實作服務注冊與發現

(5). 實作分布式日志跟蹤

(6). 日志監控等等

1.要實作CodeFirst先要了解ProtoFirst生成的代碼,下面我截了部分生成代碼

(1).關鍵是這個BindService,用于把實作的Grpc方法綁定到GrpcServiceDefinition

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即可

/// <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

作  者: 易   磊(Rabbit_Yi)

出  處: 

http://www.cnblogs.com/rabbityi/

繼續閱讀