.NET 微服務:gRPC

對于這個話題柠逞,我相信大家很容易想到昧狮,HTTP 接口的方式可以解決。

大概的做法類似這樣:

  1. 根據 HTTP 接口需要的參數板壮,將參數序列化成 json 字符串逗鸣;

  2. 根據當前項目開發(fā)語言,封裝一個處理 Post||Get 請求的方法绰精,發(fā)送請求撒璧;

  3. 接口返回結果,然后反序列化成實體在邏輯代碼中使用笨使;


可能在單個項目內使用這樣的方式痛點并不是很明顯。但如果是在多個項目內贡珊,每個項目都依賴于這個接口烤送,那為了獲取到數據可能會寫很多重復代碼妻往。而且可能因為沒有統(tǒng)一的反序列實體來映射,會出現A項目對這個屬性名反序列化后叫西門慶,到了B項目同樣的屬性名就叫成了武大郎拳锚,這樣開發(fā)的同學也會覺得很不開心拌蜘。

我們目前的情況是多個項目,而且項目也不是統(tǒng)一的語言。為了解決上面的問題,我們決定使用 gRPC 的方式來處理,基本需要滿足以下條件:

  1. 微服務化
  2. 跨語言
  3. 性能
  4. 調用者方便

因為我目前參與的項目基于 .NET 開發(fā),所以下面的一些例子可能會不自覺偏向 C#。


在使用 gRPC 之前,需要先了解一下 RPC、HTTP 接口

RPC
  • RPC(Remote Procedure Call Protocol)就是從一臺機器(客戶端)上通過參數傳遞的方式調用另一臺機器(服務器)上的一個函數或方法并得到返回的結果随闽,流程如下:
RPC流程
HTTP 接口
  • 論復雜度父丰,RPC 框架肯定是高于簡單的 HTTP 接口的;

  • HTTP 接口由于受限于 HTTP 協議橱脸,需要帶 HTTP 請求頭础米,導致傳輸起來效率不如 RPC;

  • RPC 是長鏈接添诉,不必每次通信都要像 HTTP 一樣去握手。

gRPC
  • gRPC 是一個高性能 RPC 框架医寿,和 HTTP 一樣都是一種對 RPC 的實現栏赴,但性能相比之下更好;

  • 使用 Protocol Buffers 來作為序列化和反序列化靖秩,以及接口定義語言须眷,Protocol Buffers 已經被證明是非常高效序列化框架;

  • 跨語言沟突,跨平臺花颗,gRPC 支持多種平臺和多種語言;

  • 基于 HTTP/2惠拭。


gRPC 服務創(chuàng)建大概流程如下:

1娃循、創(chuàng)建 .proto 文件沃呢,定義通信的數據結構和服務接口。文件內包含方法、請求參數敲霍、返回結果等;

syntax = "proto3";

package MD.CacheService;

service MDCache {
  // 獲取單賬號信息
  rpc GetAccountInfo (AccountInfoRequest) returns (AccountInfoResponse) {}
}

// 獲取單賬號數據請求參數
message AccountInfoRequest {
  // 賬號id
  string accountId = 1;
}

// 獲取單個賬號數據返回值
message AccountInfoResponse {
  // 賬號實體
  AccountInfo accountInfo = 1;
}

// 賬號實體
message AccountInfo{
  // 賬號id
  string accountId = 1;
  // 姓名
  string fullname = 2;
  // 頭像
  string avatar = 3;
  // 賬號創(chuàng)建時間巴帮,時間戳
  int64 createTime = 4;
}

2晶密、根據 .proto 文件通過 Protocol Buffer 編譯器分別生成服務隊和客戶端的代碼(看官方的支持,可以選擇一種你最擅長的語言秀鞭,服務端和客戶端完全不需要規(guī)定一樣的語言)趋观;

生成的代碼

3扛禽、基于生成的代碼創(chuàng)建服務端和客戶端應用;

4皱坛、在服務端創(chuàng)建一個類對服務接口進行 override旋圆;

public class CacheServiceImpl : MDCache.MDCacheBase
{
  /// <summary>
  /// 獲取單個賬號accountInfo
  /// </summary>
  /// <param name="request"></param>
  /// <param name="context"></param>
  /// <returns></returns>
  public override Task<AccountInfoResponse> GetAccountInfo(AccountInfoRequest request, ServerCallContext context)
  {
    var response = new AccountInfoResponse();

    if (!string.IsNullOrEmpty(request.AccountId))
    {
        response.AccountInfo = GetServiceAccountInfo(request.AccountId);
    }
    return Task.FromResult(response);
  }
}

5、啟動服務(服務安裝基于 Topshelf)麸恍;

class Program
{
  static void Main(string[] args)
  {
    HostFactory.Run(x =>
    {
      x.Service<CacheService>(s =>
      {
          s.ConstructUsing(name => new CacheService());
          s.WhenStarted(tc => tc.Start());
          s.WhenStopped(tc => tc.Stop());
      });
      x.RunAsLocalSystem();
      
      x.SetDisplayName("MD.CacheService");
      x.SetServiceName("MD.CacheService");
    });
  }
}

public class CacheService
{
  private readonly string host = ConfigurationManager.AppSettings["Host"];//服務IP
  private readonly string port = ConfigurationManager.AppSettings["Port"];//服務端口

  readonly Server server;
  public CacheService()
  {
    server = new Server
    {
      Services = { MDCache.BindService(new CacheServiceImpl()) },
      Ports = { 
        new ServerPort(host, Convert.ToInt32(port), ServerCredentials.Insecure) 
      }
    };
  }
  public void Start() { server.Start(); }
  public void Stop() { server.ShutdownAsync(); }
}

6灵巧、客戶端連接測試

var channel = new Channel("host:port", ChannelCredentials.Insecure);
var client = new MDCacheClient(channel);

C#官方 example ,照著做也可以實現效果

部署:

  • 基于.NET Core 的可以將服務部署在Linux上抹沪;
  • 基于.NET Framework 的可以部署成 Windows 服務刻肄,關于 Windows 服務方式,這里推薦使用Topshelf融欧,非常簡單敏弃。

服務部署后涉及到負載和高可用的問題,單點故障是不能接收的噪馏。關于這部分的實現方式麦到,相信熟悉 Nginx 的同學可以很快解決,當然也可以使用其他方案欠肾。

參考鏈接:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末瓶颠,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子刺桃,更是在濱河造成了極大的恐慌粹淋,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑟慈,死亡現場離奇詭異桃移,居然都是意外死亡,警方通過查閱死者的電腦和手機葛碧,發(fā)現死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門借杰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人进泼,你說我怎么就攤上這事蔗衡。” “怎么了缘琅?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵粘都,是天一觀的道長。 經常有香客問我刷袍,道長翩隧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮堆生,結果婚禮上专缠,老公的妹妹穿的比我還像新娘。我一直安慰自己淑仆,他們只是感情好涝婉,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔗怠,像睡著了一般墩弯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寞射,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天渔工,我揣著相機與錄音,去河邊找鬼桥温。 笑死引矩,一個胖子當著我的面吹牛,可吹牛的內容都是我干的侵浸。 我是一名探鬼主播旺韭,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掏觉!你這毒婦竟也來了区端?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤履腋,失蹤者是張志新(化名)和其女友劉穎珊燎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體遵湖,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年晚吞,在試婚紗的時候發(fā)現自己被綠了延旧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡槽地,死狀恐怖迁沫,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情捌蚊,我是刑警寧澤集畅,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站缅糟,受9級特大地震影響挺智,放射性物質發(fā)生泄漏。R本人自食惡果不足惜窗宦,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一赦颇、第九天 我趴在偏房一處隱蔽的房頂上張望二鳄。 院中可真熱鬧,春花似錦媒怯、人聲如沸订讼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欺殿。三九已至,卻和暖如春鳖敷,著一層夾襖步出監(jiān)牢的瞬間脖苏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工哄陶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留帆阳,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓屋吨,卻偏偏與公主長得像蜒谤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子至扰,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內容