dotnet core 之 gRPC

dotnet core gRPC


原文在本人公衆號中,歡迎關注我,時不時的會分享一些心得前端

HTTP和RPC是現代微服務架構中很經常使用的數據傳輸方式,二者有不少類似之處,可是又有很大的不一樣。HTTP是一種規範性、通用性、很是標準的傳輸協議,幾乎全部的語言都支持,若是要確保各平臺無縫銜接,能夠考慮使用HTTP協議,例如如今常規的RestFUL,整個傳輸過程一般使用Json數據格式。以致於不論是前端仍是後端均可以很好的對接。
RPC協議不只僅是服務間通訊協議,甚至是進程間也存在。能夠下降諸多微服務之間調用成本,屏蔽了通信細節,調用遠程方法處理數據時像調用本地方法同樣絲滑順暢。可是對前端和瀏覽器不是特別友好。
GRPC是google制定實現的一種Rpc形式,官網解釋是:web

gRPC是能夠在任何環境中運行的現代開源高性能RPC框架。它能夠經過可插拔的支持來有效地鏈接數據中心內和跨數據中心的服務,以實現負載平衡,跟蹤,運行情況檢查和身份驗證。它也適用於分佈式計算的最後一英里,以將設備,移動應用程序和瀏覽器鏈接到後端服務。後端

gRPC主要包括四個特色

  1. 簡單的服務定義且不侷限於語言:gRPC基於ProtoBuf協議構建,該協議提供了強大的系列化工具和語言定義服務。
  2. 跨語言和平臺:可自動爲多語言或平臺生成客戶端和服務端內容。
  3. 快速啓動和擴展性:只需極少代碼就能夠生成整個通信環境,能夠擴展數百萬rpc請求。
  4. 雙向流和集成身份驗證:基於HTTP/2的傳輸機制以及雙向流和而徹底可集成的插入式身份驗證機制。
    ProtoBuf協議
    該協議是一種序列化結構化數據的靈活,高效,自動化的機制,比xml更小,更快,更簡單。您定義要一次構造數據的方式,而後可使用生成的特殊源代碼輕鬆地使用各類語言在各類數據流中寫入和讀取結構化數據。您甚至能夠更新數據結構,而不會破壞已針對「舊」格式編譯的已部署程序。
    協議文件定義方式:
    此處輸入圖片的描述
    這是一個簡單的定義,此文件在.proto文件中定義,與語言無關。每一個消息類型都有一個或多個惟一編號的字段。每一個字段都有一個名稱和一個值類型,其中值類型能夠是數字(整數或浮點數),布爾值,字符串等。定義好後可使用編譯器生成對應語言的操做對象。
    更多協議內容請參閱:https://developers.google.com/protocol-buffers/docs/overview。

.net Core3.0 中的Grpc

  1. 使用Visual Stduio 2019 建立一個gRPC服務
    此處輸入圖片的描述
    請注意:此處建立的是使用ASP.NET Core的Grpc服務。也就是說,有WebHost去託管服務的。
    此處輸入圖片的描述
    後面,咱們使用Host通用主機進行託管。
  2. 先來看看Startup類
    此處輸入圖片的描述
    圖中這兩行代碼是關鍵,services.AddGrpc();將rpc注入到應用程序服務中,
    endpoints.MapGrpcService ();則是在中間件啓用監聽rpc請求。
    默認狀況下此時生成項目後,proto文件只會生成Serve的代碼。客戶端代碼生成須要單獨作配置。
    微軟文檔中是將proto文件拷貝到client中,去生成client代碼。此時服務和客戶端生成配置就不同了:
<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
 </ItemGroup>

我本人習慣將生成的server和client放在一塊兒,打包後供client端和server端使用,只須要這樣設置:瀏覽器

<ItemGroup>
    <Protobuf Include="Protos\greet.proto" GrpcServices="Server;Client" />
 </ItemGroup>

配置好後從新生成項目,grpctool會自動生成服務端,客戶端代碼。數據結構

client調用方式(asp.net core gRPC):架構

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHello(
    new HelloRequest { Name = "World" });
Console.WriteLine(response.Message);

接下來,咱們建立一個簡單的示例

  1. 建立兩個個Core控制檯項目並添加文件夾Protos和Services,分別新增一個demo.proto文件和DemoService.cs文件
  2. 引入nuget包:
<PackageReference Include="Google.Protobuf" Version="3.10.1" />
    <PackageReference Include="Grpc" Version="2.24.0" />
    <PackageReference Include="Grpc.Tools" Version="2.24.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
  1. 編輯csproj文件:使得支持同時生成client和server代碼(做爲demo,server端 應該只做爲server。生成client和server的代碼的應該單獨抽出來)
  2. 服務端:重寫GrpcExampleService.GrpcExampleServiceBase中的rpc方法(也就是.proto文件生成的代碼內容中的rpc服務方法)
public class DemoService: GrpcExampleService.GrpcExampleServiceBase
    {
        public override Task<AskResponse> Ask(AskRequest request, ServerCallContext context)
        {
            return Task.FromResult(new AskResponse {Content = "Hello from Ask"});
        }

        public override Task<ResponseModel> GetName(RequestModel request, ServerCallContext context)
        {
            return Task.FromResult(new ResponseModel { Name = "Hello  Pluto" });
        }
    }
  1. 接下來就是建立啓動項(HOST)了(GrpcServer):
    此次使用通用主機,不適用asp.net core webhost。
public class GrpcServer:IHostedService
    {
        private readonly GrpcExampleService.GrpcExampleServiceBase _sampleServiceBase;


        public GrpcServer(GrpcExampleService.GrpcExampleServiceBase sampleServiceBase)
        {
            _sampleServiceBase = sampleServiceBase;
        }

        /// <summary>
        /// 啓動本身定義的rpc服務
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.Factory.StartNew(() =>
            {
                GrpcServiceManager.Start(GrpcExampleService.BindService(_sampleServiceBase));
            }, cancellationToken);
        }

        /// <summary>
        /// 中止
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.Factory.StartNew(() =>
            {
                GrpcServiceManager.Stop();
            }, cancellationToken);
        }
    }

GrpcServiceManager中就是真正的啓動和中止grpc服務:框架

public class GrpcServiceManager
    {
        static Server server;
        public static void Start(ServerServiceDefinition bindService)
        {
            if (bindService == null)
                throw new ArgumentNullException("bindService");
            var services = new List<ServerServiceDefinition>() { bindService };
            Start(services); //todo 添加配置,攔截器等等
        }
        private static void Start(List<ServerServiceDefinition> services)
        {
            try
            {
                server = new Server()
                {
                    Ports = { new ServerPort("0.0.0.0", 8890, ServerCredentials.Insecure) }
                };

                foreach (var service in services)
                {
                    server.Services.Add(service);
                }
                server.Start();
                //todo consul 註冊
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        public static void Stop()
        {
            try
            {
                server?.ShutdownAsync().Wait();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            
        }
    }

注意:這裏應該將proto協議 服務端客戶端端進行分離,協議生成的代碼打包後 被客戶端項目和服務端項目引用,這裏只是爲了簡便客戶端直接會引用服務,由於要用到GrpcExampleService.GrpcExampleServiceClientasp.net

上邊的todo 後再後續增長對應的功能。
這樣服務端就建立完了。接下來就是建立客戶端進行調用:分佈式

控制檯客戶端:
main中實現調用邏輯ide

static void Main(string[] args)
        {
            var  channel=new Grpc.Core.Channel("127.0.0.1",8890,ChannelCredentials.Insecure);
            var democlient=new GrpcExampleService.GrpcExampleServiceClient(channel);
            var res= democlient.Ask(new AskRequest
            {
                Cate = 0,
                Key = "1312"
            });
            var res2 = democlient.GetName(new RequestModel
            {
                Key = "1313"
            });
            Console.WriteLine($"Ask={res}.GetName={res2}");
        }

而後就能夠了,啓動服務端,而後再啓動客戶端,就能夠看到調用結果了。
此處輸入圖片的描述

相關文章
相關標籤/搜索