1. 程式人生 > 其它 >go+protobuf+grpc+consul簡單的服務發現模型

go+protobuf+grpc+consul簡單的服務發現模型

一 環境準備

  • windows64 (此處我用的windows,建議大家linux)
  • 安裝consul,請自行搜尋
  • 安裝protobuf,請自行搜尋

二 概念梳理

  • consul 是服務發現工具,簡單地說,就是各個server將自己註冊到consul上,client不再記住各個server的ip+port,而是去consul上獲取想要連線的server。使得server對client不再需要暴露,可以動態伸縮。
  • protobuf 可以拿來和xml、json對比,都是資料格式,只是protobuf更加輕量級,適合做微服務或者tcp通訊。使用起來相對繁瑣一點點。
  • rpc 可以對比ipc(程序間通訊),它是各個服務間的通訊,重點在r和i的區別,rpc是遠端呼叫。
  • grpc 是rpc的一種,它是進階版,更高效,基於http2(可以雙工通訊),預設採用protobuf格式的資料

三 實現過程

  3.1目錄結構

      

  • 專案名稱test_grpc_consul
  • pb目錄為存放protobuf檔案
  • person.proto為源生protobuf程式碼
  • person.pb.go為編譯打包出的能為go呼叫的程式碼
  • consul-server.go為server程式,實現了往consul的註冊和服務的監聽
  • consul-client.go為client程式,實現了從consul查詢server併發起呼叫
  • consul-deregister為一個管理程式,實現了consul中server的登出

  3.2 protobuf的生成

    person.proto

 1 syntax = "proto3";
 2 
 3 package pb;
 4 option go_package = "./;pb";   //此處我也不知道幹嘛用的,不加他報錯,網上搜索的解決方案
 5
 6 message Person{
 7     string name = 1;
 8     int32 age = 2;
 9 }
10
11 //新增rpc服務
12 service hello{
13     rpc sayHello(Person) returns (Person);
14 
15 }

    將person.proto編譯打包

先cd到pb目錄,再執行以下命令    

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

生成person.pb.go檔案

其實golang中,一般的rpc中proto的打包為以下命令:

protoc --go_out=./ *.proto

  3.3server程式

package main

import (
    "context"
    "fmt"
    "/test_grpc_consul/pb"    //你們自己的路徑
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "net"
)
type Children struct {

}
func (this *Children)SayHello(ctx context.Context,p *pb.Person) (*pb.Person,error){
    p.Name = "hello "+p.Name
    return p,nil
}
func main(){
    //把grpc服務註冊到consul上
    //初始化consul配置
    consulConfig := api.DefaultConfig()
    //建立consul物件
    consulClient,err := api.NewClient(consulConfig)
    if err!=nil{
        fmt.Println("server, api.newclient err:",err)
        return
    }
    //告訴consul,即將註冊的服務的配置資訊
    reg := api.AgentServiceRegistration{
        Kind:              "",
        ID:                "wbw1_id",
        Name:              "wbw001_grpc_consul",
        Tags:              []string{"wbw1","asa1"},
        Port:              8800,
        Address:           "127.0.0.1",
        TaggedAddresses:   nil,
        EnableTagOverride: false,
        Meta:              nil,
        Weights:           nil,
        Check:             &api.AgentServiceCheck{
            CheckID:                        "wbw1_id_check",
            Name:                           "",
            Args:                           nil,
            DockerContainerID:              "",
            Shell:                          "",
            Interval:                       "5s",
            Timeout:                        "1s",
            TTL:                            "",
            HTTP:                           "",
            Header:                         nil,
            Method:                         "",
            Body:                           "",
            TCP:                            "127.0.0.1:8800",
            Status:                         "",
            Notes:                          "",
            TLSServerName:                  "",
            TLSSkipVerify:                  false,
            GRPC:                           "",
            GRPCUseTLS:                     false,
            AliasNode:                      "",
            AliasService:                   "",
            SuccessBeforePassing:           0,
            FailuresBeforeCritical:         0,
            DeregisterCriticalServiceAfter: "",
        },
        Checks:            nil,
        Proxy:             nil,
        Connect:           nil,
        Namespace:         "",
    }
    //註冊gprc服務到consul上
    consulClient.Agent().ServiceRegister(&reg)
    grpcServer := grpc.NewServer()
    pb.RegisterHelloServer(grpcServer,new(Children))
    listener,err := net.Listen("tcp", "127.0.0.1:8800")
    if err != nil{
        fmt.Println("listen err:",err)
    }
    defer listener.Close()
    grpcServer.Serve(listener)
}

  3.4 client程式

package main

import (
    "context"
    "fmt"
    "/test_grpc_consul/pb"   //同server此處
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "strconv"
)
func main(){
    consulConfig := api.DefaultConfig()
    conculClient,err := api.NewClient(consulConfig)
    if err!=nil{
        fmt.Println("client, api.newclient err:",err)
        return
    }
    services,_,err := conculClient.Health().Service("wbw001_grpc_consul","wbw1",true,nil )
    addr := services[0].Service.Address+":"+strconv.Itoa(services[0].Service.Port)

    //grpcConn,err := grpc.Dial("127.0.0.1:8800",grpc.WithInsecure())
    grpcConn,err := grpc.Dial(addr,grpc.WithInsecure())
    if err != nil{
        fmt.Println("dial err:",err)
    }
    defer grpcConn.Close()

    grpcClient := pb.NewHelloClient(grpcConn)

    var person pb.Person
    person.Name = "wbw"
    person.Age = 18
    p,err := grpcClient.SayHello(context.TODO(),&person)
    fmt.Println(p,err)
}

  3.5 server登出程式

package main

import (
    "fmt"
    "github.com/hashicorp/consul/api"
)

func main(){
    consulConfig := api.DefaultConfig()
    consulClient,err := api.NewClient(consulConfig)
    if err != nil{
        fmt.Println("deregister, api.newclient err:",err)
        return
    }
    consulClient.Agent().ServiceDeregister("wbw1_id") 
}

四 呼叫說明

  4.1 啟動 consul

//本文請用
consul agent -dev

//但是生產環境建議用
// consul agent -server -bootstrap-expect 1 -data-dir D:\tools\consul_1.10.0_windows_amd64\data_dir\ -node=n1 -bind=10.10.10.18 -ui -rejoin -config-dir=D:\tools\consul_1.10.0_windows_amd64\config_dir\ -client 0.0.0.0

我們進入網址http://localhost:8500/
可以發現我們的consul啟動完成

  4.2啟動server

先cd到test_grpc_consul目錄,再執行以下命令    

go run consul-server.go

可以看到我們的服務已經執行了

然後進入網址http://localhost:8500/可以看到我們的服務已經成功註冊到consul了,點進去還能看到健康檢測正常

  4.3啟動client

先cd到test_grpc_consul目錄,再執行以下命令    

go run consul-client.go

呼叫成功!!!

  4.4啟動登出程式

先cd到test_grpc_consul目錄,再執行以下命令    

go run consul-deregister.go

我們可以看到該server已經不再consul中了,同時我們呼叫client也會失敗。

注意:

如果我們把client直連server,依然能成功,因為我們的server並沒有退出,僅僅是在consul中抹掉了痕跡。

四 補充說明

  • 建議開啟go mod,這樣不依賴於gopath等路徑,且不需要go get等操作,能利用goland自動拉取