使用Golang搭建gRPC服務提供給.NetCore客戶端呼叫

韓小超發表於2021-10-04

gRPC概述

RPC

說到gRPC就不得不提RPC,所謂RPC(remote procedure call 遠端過程呼叫)框架實際是提供了一套機制,使得應用程式之間可以進行通訊,簡單點來說就是我A機器上寫的函式可以在B機器上通過RPC協議直接呼叫。

它與http不同的是:

  • RPC是基於TCP實現的,RESTFUL是基於HTTP來實現的。
  • 從傳輸速度上來看,因為HTTP封裝的資料量更多所以資料傳輸量更大,所以RPC的傳輸速度是比RESTFUL更快的。

為什麼內部(約定情況下的服務與服務)使用rpc,而外部(to customer)使用http

  • 因為HTTP協議是各個框架都普遍支援的。在toC情況下,因為不知道情況來源的框架、資料形勢是什麼樣的,所以在閘道器可以使用Restful利用http來接受。而在微服務內部的各模組之間因為各協議方案是公司內部自己定的,所以知道各種資料方式,可以使用TCP傳輸以使各模組之間的資料傳輸更快。所以可以閘道器和外界的資料傳輸使用RESTFUL,微服務內部的各模組之間使用RPC。

gRPC又是什麼呢

gRPC是一個高效能、開源的通用RPC框架,最初是由谷歌建立的,十多年來谷歌一直使用一個稱為Stubby的通用RPC基礎設施來連線在其資料中心內和跨資料中心執行的大量微服務。2015年3月,Google決定構建下一個版本的Stubby並使其開源。結果就是 gRPC,它現在被谷歌以外的許多組織用於支援從微服務到“最後一英里”計算(移動、網路和物聯網)的用例。

gRPC官方文件:https://www.grpc.io/docs/what-is-grpc/

在 gRPC 中,客戶端應用程式可以直接呼叫不同機器上的伺服器應用程式上的方法,就像它是本地物件一樣,使您可以更輕鬆地建立分散式應用程式和服務。與許多 RPC 系統一樣,gRPC 基於定義服務的思想,指定可以通過引數和返回型別遠端呼叫的方法。在伺服器端,伺服器實現了這個介面並執行一個 gRPC 伺服器來處理客戶端呼叫。在客戶端,客戶端有一個存根(在某些語言中簡稱為客戶端),它提供與伺服器相同的方法。

image

安裝

Golang IDE(Goland)

Goland詳細步驟安裝:https://blog.csdn.net/u014374975/article/details/120387180

Golang語法詳解請:https://blog.csdn.net/u014374975/article/details/120463448

Protocol Buffer

可以通過Go、C++、C#、Java、Python等語言輕鬆的建立gRPC服務,同時也意味著它可以跨語言進行遠端過程呼叫,那麼就需要一門中間語言(IDL)來約束和定義遠端過程呼叫之間通訊的介面約束。Google官方推薦並且使用人數最多的就是Protocol Buffer,它具有稍微簡化的語法,一些有用的新功能,並支援更多語言。Proto3 目前可用於 Java、C++、Dart、Python、Objective-C、C#等

Protocol Buffer官方文件:https://developers.google.com/protocol-buffers

下載Protocal Buffer

在GitHub的protocolbuffers專案下,下載ProtocalBuffer編譯器
image

配置Protocal Buffer編譯器環境變數

將下載好的Protocal Buffer壓縮包解壓,並將執行目錄配置到環境變數
image

環境變數新增完畢後,開啟cmd視窗執行protoc --version,出現版本號,表示配置成功
image

protoc-gen-go

protoc-gen-go是protobuf編譯外掛系列中的Go版本,protoc-gen-go可以將Protocol Buffer寫的介面定義轉換封裝為Golang程式碼

在下載protoc-gen-go之前,我們先設定下Golang代理地址

# 七牛雲
go env -w GOPROXY=https://goproxy.cn,direct

下載protoc-gen-go

go get -u github.com/golang/protobuf/protoc-gen-go

image

如果GOPATH的bin目錄下有這個檔案即表示下載成功
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-2TCBYJOO-1633354844949)(FDAE29AC35E14B0E87E0DBE7A84CAAF1)]

定義Protocal Buffer

開啟IDE,在GOPATH的src目錄下建立一個pbfiles資料夾,並在資料夾中新增一個product.proto檔案,用於描述介面定義

syntax = "proto3";
package pbfiles;

option go_package = "../services";

message Product{
    int32 id = 1;
    string name = 2;
    int32 count = 3;
    string description = 4;
}

service pdService{
    rpc GetProduct(Product) returns(Product);
}

新增完成後,通過protoc編譯該檔案為Golang程式碼

protoc --go_out=plugins=grpc:./ *.proto

此時我們發現,protoc會在src目錄下自動建立一個services目錄,並將上面寫的proto檔案自動編譯為product.pb.go
image

由於編譯出的Golang程式碼使用了google.golang.org等包,所以我們新增一個mod檔案,拉取缺少的模組。

進入src目錄執行go mod init gRPCdemo命令
image

建立完畢後輸入命令go mod tidy,此時上面生成的product.pb.go程式碼就不會缺少引用和標紅了
image

新增gRPC服務

Golang服務端實現

在src目錄下建立grpc-server.go檔案,並輸入程式碼:

package main

import (
	"context"
	"fmt"
	"gRPCdemo/services"
	"google.golang.org/grpc"
	"net"
)

type productRequest struct {
}

func (this *productRequest) GetProduct(context context.Context, request *services.Product) (*services.Product, error) {
	var product services.Product
	switch request.Id {
	case 1:
		product.Id = 1
		product.Name = "蘋果"
		product.Description = "小蘋果"
	case 2:
		product.Id = 1
		product.Name = "西瓜"
		product.Description = "大西瓜"
	default:
		product.Id = 3
		product.Name = "香蕉"
		product.Description = "香蕉"
	}
	return &product, nil
}

func main() {
	grpcService := grpc.NewServer()
	services.RegisterPdServiceServer(grpcService, new(productRequest))

	listen, err := net.Listen("tcp", ":5000")
	if err != nil {
		fmt.Println("listen err:", err)
		return
	}
	grpcService.Serve(listen)
}

在程式碼目錄下開啟終端輸入go run grpc-server.go執行程式,開始監聽

Golang客戶端實現

在src目錄下建立grpc-client.go檔案,並輸入程式碼:

package main

import (
	"context"
	"fmt"
	"gRPCdemo/services"
	"google.golang.org/grpc"
)

func main() {
	grpcCnn, err := grpc.Dial(":5000", grpc.WithInsecure())
	if err != nil {
		fmt.Println("grpc.dial err:", err)
		return
	}

	grpcClient := services.NewPdServiceClient(grpcCnn)

	var request services.Product
	request.Id = 1

	res, err := grpcClient.GetProduct(context.TODO(), &request)
	if err != nil {
		fmt.Println("GetProduct err:", err)
		return
	}

	fmt.Println(res)
}

在程式碼目錄下開啟終端輸入go run grpc-client.go執行程式

image
此時可以看到,已成功訪問gRPC服務

.NetCore訪問Golang搭建的gRPC服務

  1. 開啟vs建立控制檯專案grpcClient
  2. 右鍵專案管理Nuget程式包,新增如下包
    image
    • Grpc.Net.Client,其中包含 .NET Core 客戶端。
    • Google.Protobuf 包含適用於 C# 的 Protobuf 訊息
    • Grpc.Tools 包含適用於 Protobuf 檔案的 C# 工具支援。 執行時不需要工具包,因此依賴項標記為 PrivateAssets="All"。
  3. 在 gRPC 客戶端專案中建立Protos資料夾
  4. 將剛剛在Golang專案中建立的proto(pbfiles/product.proto)文複製到當前.NetCore客戶端專案的Protos中。
  5. product.proto檔案中的名稱空間更新為.NetCore專案的名稱空間:
syntax = "proto3";
package pbfiles;

option csharp_namespace = "grpcClient";

message Product{
    int32 id = 1;
    string name = 2;
    int32 count = 3;
    string description = 4;
}

service pdService{
    rpc GetProduct(Product) returns(Product);
}
  1. 編輯grpcClient.csproj專案檔案
  2. 新增具有引用greet.proto檔案的<Protobuf>元素的項組:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.18.0" />
    <PackageReference Include="Grpc.Net.Client" Version="2.39.0" />
    <PackageReference Include="Grpc.Tools" Version="2.41.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    
    <!--新增Protobuf元素組-->
    <Protobuf Include="Protos\product.proto" GrpcServices="Client" />
  </ItemGroup>

</Project>

  1. 構建客戶端專案,以在grpcClient名稱空間中建立型別。GrpcGreeter型別是由生成程式自動生成的。
  2. 使用以下程式碼更新grpcClient客戶端的 Program.cs 檔案:
using Grpc.Net.Client;
using System;

namespace grpcClient
{
    class Program
    {
        static void Main(string[] args)
        {
            // 只有 .NET Core 3.x 需要 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 開關。 .NET 5 中不需要任何額外配置,也沒有這項要求。
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
            
            using var channel = GrpcChannel.ForAddress("http://localhost:5000");
            var client = new pdService.pdServiceClient(channel);
            var reply = client.GetProduct(new Product { Id = 1 });
            Console.WriteLine(reply);

            Console.ReadKey();
        }
    }
}

image
啟動程式後,.NetCore完美訪問Golang搭建的gRPC服務

參考

gRPC官方文件:https://www.grpc.io/docs/what-is-grpc/

Protocol Buffer官方文件:https://developers.google.com/protocol-buffers

MSDN:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-5.0&tabs=visual-studio

相關文章