我們是怎麼實現gRPC CodeFirst-生成proto

来源:https://www.cnblogs.com/rabbityi/archive/2020/04/01/12605202.html
-Advertisement-
Play Games

前言: 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). 通過代碼生成proto和註釋,給第三方語言使用

(3). 實現gRPC DashBoard,用於Http遠程調用和管理

(4). 實現服務註冊與發現

(5). 實現分散式日誌跟蹤

(6). 日誌監控等等

 

 

我們是怎麼實現gRPC CodeFirst-生成proto

 

1.怎麼根據代碼生成Proto,上文我們調用了GrpcMethodHelper.AutoRegisterMethod()方法,這是通過反射自動註冊GrpcMethod的方法

(1).這裡面調用了一個BuildMethod方法,用於生成grpc的序列化和反序列化的委托

(2).同時可以收集grpc方法和參數的信息,用於生成proto

    /// <summary>
    /// 生成Grpc方法(CodeFirst方式)
    /// </summary>
    /// <typeparam name="TRequest"></typeparam>
    /// <typeparam name="TResponse"></typeparam>
    /// <param name="srv"></param>
    /// <param name="methodName"></param>
    /// <param name="package"></param>
    /// <param name="srvName"></param>
    /// <param name="mType"></param>
    /// <returns></returns>
    public static Method<TRequest, TResponse> BuildMethod<TRequest, TResponse>(this IGrpcService srv,
        string methodName, string package = null, string srvName = null, MethodType mType = MethodType.Unary)
    {
        var serviceName = srvName ??
                          GrpcExtensionsOptions.Instance.GlobalService ??
                          srv.GetType().Name;
        var pkg = package ?? GrpcExtensionsOptions.Instance.GlobalPackage;
        if (!string.IsNullOrWhiteSpace(pkg))
        {
            serviceName = $"{pkg}.{serviceName}";
        }
        #region 為生成proto收集信息
        if (!(srv is IGrpcBaseService) || GrpcExtensionsOptions.Instance.GenBaseServiceProtoEnable)
        {
            ProtoInfo.Methods.Add(new ProtoMethodInfo
            {
                ServiceName = serviceName,
                MethodName = methodName,
                RequestName = typeof(TRequest).Name,
                ResponseName = typeof(TResponse).Name,
                MethodType = mType
            });
            ProtoGenerator.AddProto<TRequest>(typeof(TRequest).Name);
            ProtoGenerator.AddProto<TResponse>(typeof(TResponse).Name);
        }
        #endregion
        var request = Marshallers.Create<TRequest>((arg) => ProtobufExtensions.Serialize<TRequest>(arg), data => ProtobufExtensions.Deserialize<TRequest>(data));
        var response = Marshallers.Create<TResponse>((arg) => ProtobufExtensions.Serialize<TResponse>(arg), data => ProtobufExtensions.Deserialize<TResponse>(data));
        return new Method<TRequest, TResponse>(mType, serviceName, methodName, request, response);
    }

 

2.不重覆造輪子,通過protobuf-net的Serializer.GetProto()來生成請求參數和返回參數的proto

(1).這裡簡單過濾了重覆的proto,但GetProto()會把依賴的類都生成proto,這樣公用類就會生成多份,需要再次過濾重覆即可

(2).生成message非關鍵代碼這裡我就不列出來了,都是字元串拼接的活

    /// <summary>
    /// 添加proto
    /// </summary>
    public static void AddProto<TEntity>(string entityName)
    {
        if (!ProtoMethodInfo.Protos.ContainsKey(entityName))
        {
            var msg = Serializer.GetProto<TEntity>(ProtoBuf.Meta.ProtoSyntax.Proto3);
            ProtoMethodInfo.Protos.TryAdd(entityName, msg.FilterHead().AddMessageComment<TEntity>());
        }
    }

 

 3.服務方法的proto就更簡單了,直接根據方法類型拼出來即可

    /// <summary>
    /// 生成grpc的service的proto內容
    /// </summary>
    private static string GenGrpcServiceProto(string msgProtoName, string pkgName, string srvName, List<ProtoMethodInfo> methodInfo, bool spiltProto)
    {
        var sb = new StringBuilder();
        sb.AppendLine("syntax = \"proto3\";");
        if (!string.IsNullOrWhiteSpace(GrpcExtensionsOptions.Instance.ProtoNameSpace))
        {
            sb.AppendLine("option csharp_namespace = \"" + GrpcExtensionsOptions.Instance.ProtoNameSpace.Trim() + "\";");
        }
        if (!string.IsNullOrWhiteSpace(pkgName))
        {
            sb.AppendLine($"package {pkgName.Trim()};");
        }
        if (spiltProto)
        {
            sb.AppendLine(string.Format("import \"{0}\";", msgProtoName));
        }
        sb.AppendLine(Environment.NewLine);
        sb.AppendLine("service " + srvName + " {");

        var template = @"   rpc {0}({1}) returns({2})";
        methodInfo.ForEach(q => {
            var requestName = q.RequestName;
            var responseName = q.ResponseName;
            switch (q.MethodType)
            {
                case Core.MethodType.Unary:
                    break;
                case Core.MethodType.ClientStreaming:
                    requestName = "stream " + requestName;
                    break;
                case Core.MethodType.ServerStreaming:
                    responseName = "stream " + responseName;
                    break;
                case Core.MethodType.DuplexStreaming:
                    requestName = "stream " + requestName;
                    responseName = "stream " + responseName;
                    break;
            }
            ProtoCommentGenerator.AddServiceComment(q,sb);
            sb.AppendLine(string.Format(template, q.MethodName, requestName, responseName) + ";" + Environment.NewLine);
        });

        sb.AppendLine("}");
        return sb.ToString();
    }

 

4.生成 proto沒有註釋,第三方對接時就尷尬了,雖然命名規範,但註釋還是要有的,減少溝通成本

(1).我們通過在類和方法上加入註釋,然後項目里設置生成xml註釋文檔

(2).生成proto時通過掃描xml註釋文檔來給proto加入註釋即可

 

未完,待續,歡迎評論拍磚

這些功能早在2018年就已經實現並運行在生產,感興趣的同學可以去 github(grpc.extensions) 上查看,你要的都有,歡迎提issue

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 C 的lambda和Linq可以說是一大亮點,C 的Lambda無處不在,Linq在數據查詢上也有著舉足輕重的地位。 那麼什麼是Linq呢,Linq是 (語言集成查詢)的縮寫,可以對本地對象 集合 或者遠程數據源進行結構化的查詢操作。 那什麼又是Lambda呢?嗯,簡單來講就是匿名函數,我們不 ...
  • 使用Xaml+C#,使WPF/UWP運行在Linux和Mac上 ...
  • 下麵的靜態代碼中: 現在想把箭頭所指的值,改為動態。 根據不同條件,它將有可能是1,或是3或是2或是5等。 ...
  • 四、C#表達式與運算符 4.1.表達式 操作數+運算符 4.2.數學運算符 var++ 先用後加 ++var 先加後用 4.3.賦值運算符 略 4.4.關係運算符 結果只會是bool類型 1)對象的不同 數值類型比較兩個數的大小 字元類比較Unicode編碼大小,'A'=65 'a'=97 '0'= ...
  • 三、C#數據類型 3.1.變數 聲明->賦值->使用 作用域:變數作用域為包含它的大括弧內 3.2.常量 1)const 數據類型 常量名稱 = 常量值 聲明常量時一定要賦值 2)@作用 輸出轉義字元 @"Hello World\n" 讓字元串換行 關鍵字用作標識符 @namespace @clas ...
  • 傳遞數據至部分視圖: 在ps.cshtml中get到上面高亮的參數: ...
  • VS2013如何轉成VS2010且不會出現此項目與Visual Studio的當前版本不相容的報錯 解決方法: 1.用記事本打開解決方案文件“解決方案名.sln”,然後修改最上面兩行為如下代碼:Microsoft Visual Studio Solution File, Format Version ...
  • [toc] 1.背景 接上篇文章 "深入淺出C 結構體——封裝乙太網心跳包的結構為例" ,使用結構體性能不佳,而且也說明瞭原因。本篇文章詳細描述了以類來封裝網路心跳包的優缺點,結果大大提升瞭解析性能。 2.用類來封裝乙太網心跳包的優缺點 2.1.優點 + 可以在類里直接new byte[],即直接實 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...