Golang grpc入門例


grpcは多くの言語をサポートし、Golangもサポートしています.

インストール:


対応するパッケージを直接入手できないため、インストールに手間がかかる
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

#   grpc-go
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text

#   
cd $GOPATH/src/
go install google.golang.org/grpc

コメント:
1.github.com/golang/protobuf/{proto,protoc-gen-go}直接ダウンロードアクセスgithub.com/golang/protobuf対応ファイルのダウンロード
2.net/textパッケージはダウンロードしなくてもいいです.デフォルトはあるはずです.もしないなら、ダウンロードして、golang.org/x/の下に入れなくてもいいです.引用してもいいです.
3.インストールプロセスがうまくいかない場合は、手動でclone githubファイルを$GOPATH/src/golangに入れることをお勧めします.org対応ディレクトリの下
4.protoはpathを加入する必要があり、コマンドの実行を容易にする
net/context   $GOPATH/src/golang.org/x
proto,protoc-gen-go    $GOPATH/src/google.golang.org/genproto
grpc    $GOPATH/src/google.golang.org/grpc

protoファイルを定義するには:

syntax = "proto3";

option java_package = "io.grpc.examples";

package helloworld;

// The greeter service definition.
service Greeter {
  // Sends a greeting
  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;
}

goファイルを変換するには:
protoc --go_out=plugins=grpc:. helloworld.proto

ハローワード入門
サービスサーバgo:
package main

import (
	"log"
	"net"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "sr/helloworld"
)

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.RegisterGreeterServer(s, &server{})
	s.Serve(lis)
}

カスタマーサービスクライアントgo:
package main

//client.go

import (
	"log"
	"os"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "sr/helloworld"
)

const (
	address     = "localhost:50051"
	defaultName = "world"
)

func main() {
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Fatal("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(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("Greeting: %s", r.Message)
}

serverを実行します.goとclient.go
クライアント出力:
2019/08/19 21:46:04 Greeting: Hello world

計算関数をリモートで呼び出すには、次の手順に従います.
サービス:
package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
)

//        
type Arith struct {
}

//          
type ArithRequest struct {
	A int
	B int
}

//          
type ArithResponse struct {
	Pro int //   
	Quo int //  
	Rem int //   
}

//       
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
	res.Pro = req.A * req.B
	return nil
}

//       
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
	if req.B == 0 {
		return errors.New("divide by zero")
	}
	res.Quo = req.A / req.B
	res.Rem = req.A % req.B
	return nil
}

func resp(conn net.Conn) {
	fmt.Fprintf(os.Stdout, "%s", "new client in coming
") //jsonrpc.ServeConn(conn) newcode := jsonrpc.NewServerCodec(conn) //newcode.ReadRequestHeader(n) rpc.ServeCodec(newcode) } func main() { rpc.Register(new(Arith)) // rpc rpc.HandleHTTP() lis, err := net.Listen("tcp", "127.0.0.1:8096") if err != nil { log.Fatalln("fatal error: ", err) } fmt.Fprintf(os.Stdout, "%s", "start connection") for { conn, err := lis.Accept() // fmt.Println(conn.RemoteAddr().String()) if err != nil { continue } //rpc.ServeConn(conn) go resp(conn) //go func(conn net.Conn) { // // fmt.Fprintf(os.Stdout, "%s", "new client in coming
") // jsonrpc.ServeConn(conn) //}(conn) //go jsonrpc.ServeConn(conn) } }

クライアント:
package main

import (
	"fmt"
	"log"
	"net/rpc/jsonrpc"
)

//          
type ArithRequest struct {
	A int
	B int
}

//          
type ArithResponse struct {
	Pro int //   
	Quo int //  
	Rem int //   
}


func main() {
	conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
	if err != nil {
		log.Fatalln("dailing error: ", err)
	}

	req := ArithRequest{9, 2}
	var res ArithResponse

	err = conn.Call("Arith.Multiply", req, &res) //     
	if err != nil {
		log.Fatalln("arith error: ", err)
	}
	fmt.Printf("%d * %d = %d
", req.A, req.B, res.Pro) err = conn.Call("Arith.Divide", req, &res) if err != nil { log.Fatalln("arith error: ", err) } fmt.Printf("%d / %d, quo is %d, rem is %d
", req.A, req.B, res.Quo, res.Rem) }

出力:
サービス:
start connection127.0.0.1:49582
new client in coming
127.0.0.1:49583
new client in coming

クライアント:
9 * 2 = 18
9 / 2, quo is 4, rem is 1

暗号化通信をサポート
SSL/TSL暗号化:
サービス:
package main

// server.go

import (
	"fmt"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/peer"
	"log"
	"net"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	pb "sr/helloworld"
)

const (
	port = ":50051"
)

type server struct {}


func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	fmt.Println("replay ... ")
	pr, ok := peer.FromContext(ctx)
	fmt.Println(pr.Addr.String())

	md, ok := metadata.FromIncomingContext(ctx)
	fmt.Println(md)
	if !ok {
		return nil, grpc.Errorf(codes.Unauthenticated, " Token    ")
	}

	var (
		appid  string
		appkey string
	)

	if val, ok := md["appid"]; ok {
		appid = val[0]
	}

	if val, ok := md["appkey"]; ok {
		appkey = val[0]
	}

	if appid != "101010" || appkey != "i am key" {
		return nil, grpc.Errorf(codes.Unauthenticated, "Token      : appid=%s, appkey=%s", appid, appkey)
	}
	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)
	}
	creds, err := credentials.NewServerTLSFromFile("./key/svr.pem", "./key/svr.key")
	if err != nil {
		grpclog.Fatalf("Failed to generate credentials %v", err)
	}
	s := grpc.NewServer(grpc.Creds(creds))
	pb.RegisterGreeterServer(s, &server{})
	s.Serve(lis)
}


クライアント:
package main

//client.go

import (
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"log"
	"os"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "cl/helloworld"
)

const (
	address     = "localhost:50051"
	defaultName = "world"
	OpenTLS = true
)

// customCredential      
type customCredential struct{}

func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  "101010",
		"appkey": "i am key",
	}, nil
}


func (c customCredential) RequireTransportSecurity() bool {
	if OpenTLS {
		return true
	}
	return false
}

func main() {
	var opts []grpc.DialOption

	if OpenTLS {
		creds, err := credentials.NewClientTLSFromFile("./key/svr.pem", "test.aaa.com")
		if err != nil {
			grpclog.Fatalf("Failed to create TLS credentials %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}else {
		opts = append(opts, grpc.WithInsecure())
	}
	opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
	//creds, err := credentials.NewClientTLSFromFile("./key/svr.pem", "test.aaa.com")
	//if err != nil {
	//	grpclog.Fatalf("Failed to create TLS credentials %v", err)
	//}
	//conn, err := grpc.Dial(address, grpc.WithInsecure())
	//conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
	conn, err := grpc.Dial(address, opts...)
	if err != nil {
		log.Fatal("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(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("Greeting: %s", r.Message)
}


出力:
サービス:
replay ... 
[::1]:49638
map[:authority:[test.aaa.com] content-type:[application/grpc] user-agent:[grpc-go/1.20.0-dev] appkey:[i am key] appid:[101010]]

クライアント:
2019/08/19 22:01:42 Greeting: Hello world

証明書が異常または期限切れの場合は、次のエラーが表示されます.2019/08/19 20:03:48 could not greet: %vrpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate has expired or is not yet valid"
証明書を更新する必要があります