1. 程式人生 > >go grpc protobuf 安裝 及 用法簡單示例

go grpc protobuf 安裝 及 用法簡單示例

grpc安裝

mkdir -p $GOPATH/src/google.golang.org/grpc
cd $GOPATH/src/google.golang.org/grpc
git clone https://github.com/grpc/grpc-go.git

// 以下是為了 把 grpc/grpc-go --> grpc 目錄

mv grpc-go ..
cd ..
rm -r grpc
mv grpc-go grpc
安裝依賴包, 注意路徑必須完全對的上
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/net.git
git clone https://github.com/golang/text.git
cd $GOPATH/src/google.golang.org
git clone https://github.com/google/go-genproto.git
mv go-genproto/ genproto

安裝protobuf

按照github提示

For non-C++ users, the simplest way to install the protocol compiler is to

download a pre-built binary from our release page:

我下了個 all的 protobuf-all-3.6.0.tar.gz

還以為已經編譯好了,沒想是份原始碼

那就練練手,原始碼構建個出來吧

切換到src目錄下 開啟 README.md

sudo apt-get install autoconf automake libtool curl make g++ unzip

貌似我原來就裝好了

    $ ./configure
    $ make
    $ make check
    $ sudo make install
    $ sudo ldconfig # refresh shared library cache.

make check 非常慢 耐心等待


裝好後可以看看版本號

~/go/gopath $ protoc --version
libprotoc 3.6.0 

安裝 protoc-gen-go

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





go hello 示例

提供2個介面  一個SayHello 一個 Ls (ls 當前目錄)

syntax = "proto3";
 
package hello;
 
// The greeting service definition.
service Hello {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

然後新建 hello.go 

//go:generate protoc -I . --go_out=plugins=grpc:. hello.proto
package hello

之後 go generate 生成 hello.pb.go  go介面檔案

這個是 ser.go

package main
  
import (
    log "github.com/sirupsen/logrus"
    "net"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "hellogw"
)
  
const (
    port = ":50051"
)
  
type server struct {}
  
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
  
func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatal("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterHelloServer(s, &server{})
    s.Serve(lis)
}

下面是 client.go

ppackage main
  
import (
    log "github.com/sirupsen/logrus"
    "os"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "hellogw"
)
  
const (
    address     = "localhost:50051"
    defaultName = "world wjs"
)
  
func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatal("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewHelloClient(conn)
  
    name := defaultName
    if len(os.Args) >1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatal("could not greet: %v", err)
    }
    log.Printf("%s", r.Message)
}

---------------------------------------------------------------華麗的分割線---------------------------------------------------------------

如何即提供 grpc 又提供 RESTfull 介面?

安裝

go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go get -u -v github.com/golang/protobuf/protoc-gen-go

編寫proto檔案

hellogateway.proto

syntax = "proto3";
 
package hello;

import "google/api/annotations.proto";
 
// The greeting service definition.
service Hello {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
    post: "/v1/example/echo"
    body: "*"
    };
  }
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

關鍵性改動如下

import "google/api/annotations.proto";
rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
    post: "/v1/example/echo"
    body: "*"
    };
  }

生成rpc對應go檔案

生成 hello.go 檔案,內容如下。 這個檔案啥程式碼沒有,就給 go generate 留了個命令:執行 gen.sh 

~/go/gopath/src/hellogw $ cat hello.go
//go:generate sh gen.sh

package hello

編寫 gen.sh

#!/usr/bin/env bash

# Since GOPATH can be a path, we can't just use it as a variable.  Split it up 
# to the various paths, and append the subpaths.
GOSUBPATHS="/src:/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis"
GOPATHLIST=""
OIFS=$IFS
IFS=':' 
for GOBASEPATH in $GOPATH; do
    for GOSUBPATH in $GOSUBPATHS; do
    	if [ -e ${GOBASEPATH}${GOSUBPATH} ]; then
        	GOPATHLIST="${GOPATHLIST} -I${GOBASEPATH}${GOSUBPATH}"
        fi
    done
done
IFS=$OIFS

# generate the gRPC code
protoc -I/usr/local/include -I. ${GOPATHLIST} --go_out=plugins=grpc:. \
    hellogateway.proto

# generate the JSON interface code
protoc -I/usr/local/include -I. ${GOPATHLIST} --grpc-gateway_out=logtostderr=true:. \
    hellogateway.proto


# generate the swagger definitions
# protoc -I/usr/local/include -I. ${GOPATHLIST} --swagger_out=logtostderr=true:./swagger \
#    hellogateway.proto

# merge the swagger code into one file
# go run swagger/main.go swagger > ../static/swagger/api.swagger.json

命令列下執行  go generate 即可生成

~/go/gopath/src/hellogw $ go generate
~/go/gopath/src/hellogw $ ls
gen.sh   hello.go   hellogateway.pb.go     hellogateway.proto
hellogateway.pb.gw.go  

gw.go

package main
 
import (
    "net/http"
 
    "github.com/golang/glog"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
 
    gw "hellogw"
)
 
func run() error {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    mux := runtime.NewServeMux()
    opts := []grpc.DialOption{grpc.WithInsecure()}
    err := gw.RegisterHelloHandlerFromEndpoint(ctx, mux, "localhost:50051", opts)
    if err != nil {
        return err
    }
 
    return http.ListenAndServe(":8080", mux)
}
 
func main() {
    defer glog.Flush()
 
    if err := run(); err != nil {
        glog.Fatal(err)
    }
}

執行

go run ser.go &
go run gw.go

試試看命令

curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world wjs"}'

{"message":"Hello  world wjs"}

當然也可以寫個小小的go程式 

package main

import (
	"net/http"
	"encoding/json"
	"bytes"
	
	log "github.com/sirupsen/logrus"
	pb "hellogw"	
)

const (
	url = "http://localhost:8080/v1/example/echo"
)

func SayHello() error {
	msg := pb.HelloRequest{Name:"wjs"}
	js, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	log.Printf("%q", js)
	
	req,_ := http.NewRequest("POST", url, 	bytes.NewReader(js))
	res,err := http.DefaultClient.Do(req)
	defer res.Body.Close()

	reply := pb.HelloReply{}
	err = json.NewDecoder(res.Body).Decode(&reply)
	log.Print(reply.Message, err)
	return err
}

func main() {
	SayHello()
}

前面的方法要執行2個伺服器,好麻煩啊

我把它們寫到一個

package main

import (
	log "github.com/sirupsen/logrus"
	"net"
	"net/http"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "hellogw"
)

const (
	port = ":50051"
)

type server struct{}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := pb.RegisterHelloHandlerFromEndpoint(ctx, mux, "localhost"+port, opts)
	if err != nil {
		return err
	}

	return http.ListenAndServe(":8080", mux)
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterHelloServer(s, &server{})
	go s.Serve(lis)

	defer glog.Flush()

	if err := run(); err != nil {
		glog.Fatal(err)
	}
}