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

helloword入门

服务端server.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)
}
客服端client.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\n")
	//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\n")
		//	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\n", 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\n", 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"

则需要更新证书

你可能感兴趣的:(常用组件)