我們是怎麼實現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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...