.NET Core 使用 grpc 實(shí)現(xiàn)微服務(wù)

GRPC 是Google發(fā)布的一個(gè)開(kāi)源、高性能、通用RPC(Remote Procedure Call)框架阵赠。提供跨語(yǔ)言、跨平臺(tái)支持肌稻。以下以一個(gè).NET Core Console項(xiàng)目演示如何使用GRPC框架清蚀。

一、定義服務(wù)

通過(guò)proto定義一個(gè)數(shù)學(xué)計(jì)算服務(wù)爹谭,其中包括兩個(gè)服務(wù)方法(Add, Multipy)以及4個(gè)請(qǐng)求響應(yīng)對(duì)象(AddRequest, AddReply, MultiplyRequest, MultiplyReply)枷邪。

// 文件名:mathservice.proto

syntax = "proto3";
option java_multiple_files = false;
option java_package = "MathServices";
option java_outer_classname = "MathServicesProto";
option objc_class_prefix = "MathServices";
package MathServices;

// 數(shù)學(xué)運(yùn)算服務(wù)
service MathService 
{
  rpc Add (AddRequest) returns (AddReply) {}
  rpc Multiply (MultiplyRequest) returns (MultiplyReply) {}
}
message AddRequest {
  double First = 1;
  double Second = 2;
}
message AddReply {
  double Sum = 1;
}
message MultiplyRequest {
  double First = 1;
  double Second = 2;
}
message MultiplyReply {
  double Result = 1;
}

二、將服務(wù)編譯成存根(stub)

通過(guò)以下批處理命令generate_protos.bat將服務(wù)定義生成多種語(yǔ)言和平臺(tái)版本的客戶端和服務(wù)端存根诺凡。

@rem 生成客戶端和服務(wù)器端存根

setlocal

@rem 進(jìn)入當(dāng)前目錄
cd /d %~dp0

set TOOLS_PATH=C:\Users\Freeman\.nuget\packages\Grpc.Tools\1.0.0\tools\windows_x86

%TOOLS_PATH%\protoc.exe ^
--proto_path protos ^
--cpp_out=Interfaces/cpp ^
--csharp_out=Interfaces/csharp ^
--java_out=Interfaces/java ^
--js_out=Interfaces/javascript ^
--grpc_out=Interfaces/csharp ^
--plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe ^
protos/mathservice.proto

endlocal
timeout 5

針對(duì)CSHARP語(yǔ)言东揣,protoc.exe編譯器生成了如下圖幾個(gè)類,其中左邊4個(gè)類用于構(gòu)造請(qǐng)求和響應(yīng)對(duì)象腹泌,MathService類用于下一步構(gòu)造服務(wù)和消費(fèi)服務(wù)嘶卧。


CSHARP STUBS

三、實(shí)現(xiàn)并運(yùn)行服務(wù)

通過(guò)上一步的編譯凉袱,自動(dòng)生成了MathService類芥吟,下面通過(guò)該類構(gòu)造并啟動(dòng)grpc服務(wù)。

通過(guò)繼承基類實(shí)現(xiàn)服務(wù)接口

    /// <summary>
    /// 實(shí)現(xiàn)RPC服務(wù)端接口专甩。
    /// </summary>
    public class MathServiceImpl : MathService.MathServiceBase
    {
        public override Task<AddReply> Add(AddRequest request, ServerCallContext context)
        {
            return Task.FromResult(new AddReply { Sum = request.First + request.Second });
        }

        public override Task<MultiplyReply> Multiply(MultiplyRequest request, ServerCallContext context)
        {
            return Task.FromResult(new MultiplyReply { Result = request.First * request.Second });
        }
    }

啟動(dòng)服務(wù)

const string ip = "0.0.0.0";
const int port = 50051;
Server server = new Server();
server.Ports.Add(new ServerPort(ip, port, ServerCredentials.Insecure));
server.Services.Add(MathService.BindService(new MathServiceImpl()));
server.Start();
server.Ports.ToList().ForEach(a => Console.WriteLine($"Server listening on port {a.Port}..."));
Console.ReadLine();

四钟鸵、客戶端調(diào)用服務(wù)

客戶端通過(guò)創(chuàng)建一個(gè)Channel和一個(gè)服務(wù)客戶端來(lái)使用服務(wù)。

var channel = new Channel($"{"127.0.0.1"}:{port}", SslCredentials.Insecure);
var client = new MathService.MathServiceClient(channel);
var random = new Random();

while (true)
{
    var first = random.NextDouble();
    var second = random.NextDouble();
    var reply = client.Add(new AddRequest { First = first, Second = second });
    Console.WriteLine($"RPC call Add service: {first:F4} + {second:F4} = {reply.Sum:F4}");
    Thread.Sleep(500);
} 
RPC調(diào)用

五涤躲、使用SSL實(shí)現(xiàn)加密通訊

grpc默認(rèn)實(shí)現(xiàn)了基于證書(shū)的SSL加密通訊棺耍,使用中需要注意以下事項(xiàng)。

  • 在Windows上開(kāi)發(fā)請(qǐng)安裝 OpenSSL對(duì)應(yīng)版本并將openssl.exe所在路徑添加到環(huán)境變量中种樱。

  • 通過(guò)以下樣例腳本生成通訊中所需要的服務(wù)端和客戶端證書(shū)蒙袍,其中需要特別注意的是俊卤,Generate server signing request:中的CN=KEKYK字段如果是本機(jī)測(cè)試,請(qǐng)一定使用本機(jī)名稱左敌,如果是真實(shí)環(huán)境請(qǐng)使用域名瘾蛋,因?yàn)榭蛻舳吮仨毻ㄟ^(guò)機(jī)器名(本地測(cè)試)或域名訪問(wèn)該服務(wù)。如果此處CN字段不使用機(jī)器名或域名矫限,將導(dǎo)致以下錯(cuò)誤:


    CN字段不使用主機(jī)名或域名時(shí)產(chǎn)生的錯(cuò)誤
  • 生成服務(wù)端和客戶端證書(shū)腳本generate_ssl.bat

@echo off
set OPENSSL_CONF=c:\OpenSSL-Win64\bin\openssl.cfg   

echo Generate CA key:
openssl genrsa -passout pass:1111 -des3 -out ca.key 4096

echo Generate CA certificate:
openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=MyRootCA"

echo Generate server key:
openssl genrsa -passout pass:1111 -des3 -out server.key 4096

echo Generate server signing request:
openssl req -passin pass:1111 -new -key server.key -out server.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=kekyk"

echo Self-sign server certificate:
openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

echo Remove passphrase from server key:
openssl rsa -passin pass:1111 -in server.key -out server.key

echo Generate client key
openssl genrsa -passout pass:1111 -des3 -out client.key 4096

echo Generate client signing request:
openssl req -passin pass:1111 -new -key client.key -out client.csr -subj  "/C=US/ST=CA/L=Cupertino/O=YourCompany/OU=YourApp/CN=client"

echo Self-sign client certificate:
openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

echo Remove passphrase from client key:
openssl rsa -passin pass:1111 -in client.key -out client.key
pause
  • 基于SSL的服務(wù)端啟動(dòng)如下哺哼,創(chuàng)建服務(wù)的時(shí)候請(qǐng)使用主機(jī)名(開(kāi)發(fā)環(huán)境)或域名(生產(chǎn)環(huán)境),不要使用IP地址叼风。
 public static void RpcServerSsl()
{
    var cacert = File.ReadAllText(CombinePath("ca.crt"));
    var servercert = File.ReadAllText(CombinePath("server.crt"));
    var serverkey = File.ReadAllText(CombinePath("server.key"));
    var keypair = new KeyCertificatePair(servercert, serverkey);
    var sslCredentials = new SslServerCredentials(new List<KeyCertificatePair>() { keypair }, cacert, false);

    var server = new Server
    {
        Services = { MathService.BindService(new MathServiceImpl()) },
        Ports = { new ServerPort("KEKYK", sslPort, sslCredentials) }
    };
    server.Start();
    server.Ports.ToList().ForEach(a => Console.WriteLine($"Server (SSL) listening on port {a.Port}..."));
    Console.ReadLine();
}
  • 基于SSL的客戶端使用如下取董,注意測(cè)試環(huán)境中使用主機(jī)名,生產(chǎn)環(huán)境中使用域名來(lái)无宿,不要使用任何形式的IP地址茵汰。
public static void RpcClientSsl()
{
    var cacert = File.ReadAllText(CombinePath("ca.crt"));
    var clientcert = File.ReadAllText(CombinePath("client.crt"));
    var clientkey = File.ReadAllText(CombinePath("client.key"));
    var ssl = new SslCredentials(cacert, new KeyCertificatePair(clientcert, clientkey));
    var channel = new Channel("KEKYK", sslPort, ssl);
    var client = new MathService.MathServiceClient(channel);

    var random = new Random();
    while (true)
    {
        var first = random.NextDouble();
        var second = random.NextDouble();

        var reply = client.AddAsync(new AddRequest { First = first, Second = second }, new CallOptions()).ResponseAsync.Result;
        Console.WriteLine($"RPC call Add service: {first:F4} + {second:F4} = {reply.Sum:F4}");
        Thread.Sleep(1000);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孽鸡,隨后出現(xiàn)的幾起案子蹂午,更是在濱河造成了極大的恐慌,老刑警劉巖彬碱,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豆胸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡巷疼,警方通過(guò)查閱死者的電腦和手機(jī)晚胡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚼沿,“玉大人估盘,你說(shuō)我怎么就攤上這事÷饩。” “怎么了遣妥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)攀细。 經(jīng)常有香客問(wèn)我箫踩,道長(zhǎng),這世上最難降的妖魔是什么辨图? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮肢藐,結(jié)果婚禮上故河,老公的妹妹穿的比我還像新娘。我一直安慰自己吆豹,他們只是感情好鱼的,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布理盆。 她就那樣靜靜地躺著,像睡著了一般凑阶。 火紅的嫁衣襯著肌膚如雪猿规。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天宙橱,我揣著相機(jī)與錄音姨俩,去河邊找鬼。 笑死师郑,一個(gè)胖子當(dāng)著我的面吹牛环葵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宝冕,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼张遭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了地梨?” 一聲冷哼從身側(cè)響起菊卷,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宝剖,沒(méi)想到半個(gè)月后洁闰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诈闺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年渴庆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雅镊。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡襟雷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仁烹,到底是詐尸還是另有隱情耸弄,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布卓缰,位于F島的核電站计呈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏征唬。R本人自食惡果不足惜捌显,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望总寒。 院中可真熱鬧扶歪,春花似錦、人聲如沸摄闸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至炫欺,卻和暖如春乎完,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背品洛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工树姨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毫别。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓娃弓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親岛宦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子台丛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)砾肺,斷路器挽霉,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 主要思路: 1. Docker Registry 說(shuō)明 關(guān)于如何創(chuàng)建和使用本地倉(cāng)庫(kù)亩冬,其實(shí)已經(jīng)有很多文章介紹了。因?yàn)?..
    威谷子閱讀 5,667評(píng)論 0 30
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,313評(píng)論 0 6
  • 正星89閱讀 164評(píng)論 0 0
  • 前陣子看到了一個(gè)人表達(dá)過(guò)這樣一個(gè)觀點(diǎn):多年之后裙盾,中國(guó)一定是全世界最大最好的人才庫(kù)实胸。 他得出這個(gè)觀點(diǎn)的原因就是,現(xiàn)在...
    松球42閱讀 275評(píng)論 0 1