gRPC

安装

方法1:
go get -u -v google.golang.org/grpc
方法2:

git clone https://github.com/grpc/grpc-go.git $GOPATH/pkg/mod/google.golang.org/grpc

git clone https://github.com/golang/net.git $GOPATH/pkg/mod/google.golang.org/x/net

git clone https://github.com/golang/text.git $GOPATH/pkg/mod/google.golang.org/x/text

git clone https://github.com/google/go-genproto.git $GOPATH/pkg/mod/google.golang.org/genproto

cd $GOPATH/pkg/mod
go install google.golang.org/grpc

模式

一元模式

(Simple RPC)

客户端发起一次请求,服务端响应一次数据

服务端
//protobuf定义的服务函数
type OutMsg struct{}
func (om *OutMsg) SayHello(ctx context.Context, p *pb.Person) (*pb.PhoneNumber, error) {
	p.Age = 18
	p.Name = "wang"
	var pn pb.PhoneNumber = pb.PhoneNumber{Number: "11", Type: 2}
	fmt.Println("rotem call")
	return &pn, nil
}
/**
 * @func: CreateGrpcSer
 * @msg: 创建grpc服务端
 * @param {*}
 * @return {*}
 */
func CreateGrpcSer() {
	//初始化对象
	grpcSer := gRPC.NewServer()
	//注册服务
	pb.RegisterHelloServer(grpcSer, new(OutMsg))
	//设置监听,ip,port
	listener, err := net.Listen("tcp", "0.0.0.0:38000")
	if err != nil {
		log.Fatal(err)
		return
	}
	defer listener.Close()
	//启动
	grpcSer.Serve(listener)
}
客户端
/**
 * @func: CreateGrpcCli
 * @msg: 创建grpc客户端
 * @param {*}
 * @return {*}
 */
func CreateGrpcCli() {
	//连接grpc服务
	grpcConn, err := gRPC.Dial("192.168.11.140:38000", gRPC.WithInsecure()) // gRPC.WithInsecure()以安全的方式操作
	if err != nil {
		log.Fatal(err)
		return
	}
	defer grpcConn.Close()
	//初始化grpc客户端
	grpcClient := pb.NewHelloClient(grpcConn)
	//调用远程服务
	pNumber, err := grpcClient.SayHello(context.TODO(), &pb.Person{})
	if err != nil {
		log.Fatal(err)
		return
	}
	fmt.Println(pNumber)
}

stream模式

流模式可以源源不断的推送数据,很适合传输一些大数据,或服务端和客户端长时间数据交互。

服务端数据流模式(Server-side streaming RPC)

客户端发起一次请求,服务端返回一段连续的数据流。
结束传输服务端会传送EOF

客户端数据流模式(Client-side streaming RPC)

与服务端数据流模式相反,客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应

双向数据流模式(Bidirectional streamin RPC)

客户端和服务端都可以同时向对方发送数据流

关键字 stream

stream.proto

syntax="proto3";

option go_package="../;pb";

//关键字 stream
service Greeter{
	rpc GetStream(StreamReqData) returns (stream StreamResData){} // 服务端流模式
	rpc PostStream(stream StreamReqData) returns (StreamResData); //客户端流模式
	rpc AllStream(stream StreamReqData)returns(stream StreamResData)//双向流模式
}
message StreamReqData{
	string data=1;
}
message StreamResData{
	string data=1;
}
server.go

package main

import (
	"fmt"
	"grpcStream/pb"
	"log"
	"net"
	"sync"
	"time"

	"google.golang.org/grpc"
)

const PORT = ":50052"

/*注意 服务端接口函数定义
// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
	GetStream(*StreamReqData, Greeter_GetStreamServer) error
	PostStream(Greeter_PostStreamServer) error
	AllStream(Greeter_AllStreamServer) error
}
*/
type Serv struct{}

/**
 * @func:
 * @msg: 向客户端推送数据流
 * @param {*pb.StreamReqData} req
 * @param {pb.Greeter_GetStreamServer} res
 * @return {error}
 */
func (s *Serv) GetStream(req *pb.StreamReqData, res pb.Greeter_GetStreamServer) (err error) {
	fmt.Println(req.Data)
	for i := 0; i < 10; i++ {
		err := res.Send(&pb.StreamResData{
			Data: fmt.Sprintf("%v,%d", time.Now().Unix(), i),
		})
		if err != nil {
			log.Fatalln(err)
			break
		}
		// time.Sleep(time.Second)
	}
	return err
}

/**
 * @func:
 * @msg:接收客户端推送数据
 * @param {pb.Greeter_PostStreamServer} cliStr
 * @return {*}
 */
func (s *Serv) PostStream(cliStr pb.Greeter_PostStreamServer) (err error) {
	for {
		if a, err := cliStr.Recv(); err != nil {
			fmt.Printf("err:%v", err)
			break
		} else {
			fmt.Println(a.Data)
		}
	}
	return err
}

/**
 * @func:
 * @msg: 双向推流服务端
 * @param {pb.Greeter_AllStreamServer} allStr
 * @return {*}
 */
func (s *Serv) AllStream(allStr pb.Greeter_AllStreamServer) (err error) {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		for {
			if a, err := allStr.Recv(); err != nil {
				fmt.Printf("err:%v", err)
				break
			} else {
				fmt.Println(a.Data)
			}
		}
	}()
	go func() {
		defer wg.Done()
		for {
			err = allStr.Send(&pb.StreamResData{
				Data: fmt.Sprintf("server_send:%v", time.Now().Unix()),
			})
			if err != nil {
				fmt.Printf("err:%v", err)
				break
			}
			time.Sleep(time.Second)
		}
	}()
	defer wg.Wait()
	return err
}
func main() {
	lis, err := net.Listen("tcp", PORT)
	if err != nil {
		panic(err)
	}
	defer lis.Close()
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, new(Serv))
	err = s.Serve(lis)
	if err != nil {
		panic(err)
	}
}
client.go

package main

import (
	"context"
	"fmt"
	"grpcStream/pb"
	"log"
	"sync"
	"time"

	"google.golang.org/grpc"
)

/**
 * @func: GetGrpcCli
 * @msg: 创建grpc客户端-服务端流模式
 * @param {*}
 * @return {*}
 */
func GetGrpcCli() error {
	//连接grpc服务
	grpcConn, err := grpc.Dial("192.168.11.140:50052", grpc.WithInsecure()) // gRPC.WithInsecure()以安全的方式操作
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
	defer grpcConn.Close()
	//初始化grpc客户端
	grpcClient := pb.NewGreeterClient(grpcConn)
	//调用远程服务
	res, err := grpcClient.GetStream(context.TODO(), &pb.StreamReqData{Data: "req"})
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
	for {
		a, err := res.Recv()
		if err != nil {
			fmt.Println(err)
			break
		}
		fmt.Println(a)
	}
	panic(err)
}

/**
 * @func:
 * @msg: 创建grpc客户端-客户端流模式
 * @param {*}
 * @return {*}
 */
func PostGrpcCli() error {
	//连接grpc服务
	grpcConn, err := grpc.Dial("192.168.11.140:50052", grpc.WithInsecure()) // gRPC.WithInsecure()以安全的方式操作
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer grpcConn.Close()
	//初始化grpc客户端
	grpcClient := pb.NewGreeterClient(grpcConn)
	//调用远程服务
	postS, err := grpcClient.PostStream(context.TODO())
	if err != nil {
		log.Fatal(err)
		return err
	}
	for i := 0; i < 5; i++ {
		err = postS.Send(&pb.StreamReqData{
			Data: fmt.Sprintf("%v,%d", time.Now().Unix(), i),
		})
		if err != nil {
			log.Fatalln(err)
			break
		}
		time.Sleep(time.Second)
	}
	return err
}

/**
 * @func:
 * @msg: 双向推流客户端
 * @param {*}
 * @return {*}
 */
func AllGrpcCli() error {
	grpcConn, err := grpc.Dial("192.168.11.140:50052", grpc.WithInsecure()) // gRPC.WithInsecure()以安全的方式操作
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer grpcConn.Close()
	//初始化grpc客户端
	grpcClient := pb.NewGreeterClient(grpcConn)
	//调用远程服务
	allStr, err := grpcClient.AllStream(context.Background())
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		for {
			if a, err := allStr.Recv(); err != nil {
				fmt.Printf("err:%v", err)
				break
			} else {
				fmt.Println(a.Data)
			}
		}
	}()
	go func() {
		defer wg.Done()
		for {
			err = allStr.Send(&pb.StreamReqData{
				Data: fmt.Sprintf("client_send:%v", time.Now().Unix()),
			})
			if err != nil {
				fmt.Printf("err:%v", err)
				break
			}
			time.Sleep(time.Second)
		}
	}()
	defer wg.Wait()
	return err
}
func main() {
	GetGrpcCli()
	PostGrpcCli()
	AllGrpcCli()
}

metadata

metadata.proto

syntax = "proto3";

option go_package = "./pb;metadata";

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest{
    string name = 1;
}

message HelloReply {
    string message = 1;
}
server.go

package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"

	mdata "grpctest/metadata/pb"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

var host = "127.0.0.1"

var (
	ServiceName = flag.String("ServiceName", "hello_service", "service name")
	Port        = flag.Int("Port", 50001, "listening port")
)

func main() {
	flag.Parse()

	lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *Port))
	if err != nil {
		log.Fatalf("failed to listen:%s", err)
	} else {
		fmt.Printf("listen at:%d\n", *Port)
	}
	defer lis.Close()

	s := grpc.NewServer()
	defer s.GracefulStop()

	mdata.RegisterGreeterServer(s, new(server))
	addr := fmt.Sprintf("%s,%d", host, *Port)
	fmt.Printf("server add:%s\n", addr)

	s.Serve(lis)
}

type server struct{}

func (s *server) SayHello(ctx context.Context, in *mdata.HelloRequest) (*mdata.HelloReply, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		fmt.Printf("get metadata error")
	}
	if t, ok := md["timestamp"]; ok {
		fmt.Printf("timestamp from metadata:\n")
		for i, e := range t {
			fmt.Printf("%d. %s\n", i, e)
		}
	}
	return &mdata.HelloReply{Message: "hello" + in.Name}, nil
}
client.go

package main

import (
	"context"
	"fmt"
	mdata "grpctest/metadata/pb"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

const (
	timestampFormat = time.StampNano // "Jan _2 xx:xx:xx.000"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:50001", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	client := mdata.NewGreeterClient(conn)
	md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
	ctx := metadata.NewOutgoingContext(context.Background(), md)
	resp, err := client.SayHello(ctx, &mdata.HelloRequest{Name: "hello"})
	if err != nil {
		fmt.Printf("call serve error:%s\n", err)
	} else {
		fmt.Printf("Reply is %s\n", resp.Message)
	}
}

拦截器

1. 一元模式

interceptor.proto

syntax = "proto3";
option go_package = ".;proto";
package proto;
service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}
server.go

package main

import (
	"context"
	"fmt"
	"net"
	"time"

	"google.golang.org/grpc"

	"grpctest/grpc_interpretor/proto"
)

type Server struct{}

func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,
	error) {
	time.Sleep(2 * time.Second)
	return &proto.HelloReply{
		Message: "hello " + request.Name,
	}, nil
}

func main() {
	interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		fmt.Println("接收到了一个新的请求")
		res, err := handler(ctx, req)
		fmt.Println("请求已经完成")
		return res, err
	}

	opt := grpc.UnaryInterceptor(interceptor)
	g := grpc.NewServer(opt)
	proto.RegisterGreeterServer(g, &Server{})
	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		panic("failed to listen:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}
client.go

package main

import (
	"context"
	"fmt"
	"grpctest/grpc_interpretor/proto"
	"time"

	"google.golang.org/grpc"
)

func main() {
	
	interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
		start := time.Now()
		err := invoker(ctx, method, req, reply, cc, opts...)
		fmt.Printf("耗时:%s\n", time.Since(start))
		return err
	}
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
	conn, err := grpc.Dial("127.0.0.1:50051", opts...)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Message)
}

2. stream模式

go-grpc-middleware
https://github.com/grpc-ecosystem/go-grpc-middleware

client-retry.go

package main

import (
	"context"
	"fmt"
	"time"

	"google.golang.org/grpc/codes"

	grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
	"google.golang.org/grpc"

	"grpctest/grpc_interpretor/proto"
)

func main() {
	
	interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
		start := time.Now()
		err := invoker(ctx, method, req, reply, cc, opts...)
		fmt.Printf("耗时:%s\n", time.Since(start))
		return err
	}
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	retryOpts := []grpc_retry.CallOption{
		grpc_retry.WithMax(3),
		grpc_retry.WithPerRetryTimeout(1 * time.Second),
		grpc_retry.WithCodes(codes.Unknown, codes.DeadlineExceeded, codes.Unavailable),
	}

	opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
	//这个请求应该多长时间超时, 这个重试应该几次、当服务器返回什么状态码的时候重试
	opts = append(opts, grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...)))
	conn, err := grpc.Dial("127.0.0.1:50051", opts...)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Message)
}

token_auth

server.go

package main

import (
	"context"
	"fmt"
	"net"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"

	"google.golang.org/grpc"

	"grpctest/grpc_token_auth_test/proto"
)

type Server struct{}

func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,
	error) {
	return &proto.HelloReply{
		Message: "hello " + request.Name,
	}, nil
}

func main() {
	interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		fmt.Println("接收到了一个新的请求")
		md, ok := metadata.FromIncomingContext(ctx)
		fmt.Println(md)
		if !ok {
			//grpc的错误处理了
			return resp, status.Error(codes.Unauthenticated, "无token认证信息")
		}

		var (
			appid  string
			appkey string
		)

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

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

		if appid != "101010" || appkey != "i am key" {
			return resp, status.Error(codes.Unauthenticated, "无token认证信息")
		}

		res, err := handler(ctx, req)
		fmt.Println("请求已经完成")
		return res, err
	}

	opt := grpc.UnaryInterceptor(interceptor)
	g := grpc.NewServer(opt)
	proto.RegisterGreeterServer(g, &Server{})
	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		panic("failed to listen:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}

拦截器+metadata

client.go

package main

import (
	"context"
	"fmt"
	"grpctest/grpc_interpretor/proto"
	"time"

	"google.golang.org/grpc"
)

func main() {
	interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error{
               start := time.Now()
		md := metadata.New(map[string]string{
			"appid":"10101",
			"appkey":"i am key",
		})
		ctx = metadata.NewOutgoingContext(context.Background(), md)
		err := invoker(ctx, method, req, reply, cc, opts...)
		fmt.Printf("耗时:%s\n", time.Since(start))
		return err
	}
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
	conn, err := grpc.Dial("127.0.0.1:50051", opts...)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Message)
}

grpc.WithPerRPCCredentials

client.go

package main

import (
	"context"
	"fmt"
	"grpctest/grpc_token_auth_test/proto"

	"google.golang.org/grpc"
)

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
}

// RequireTransportSecurity indicates whether the credentials requires
// transport security.
func (c customCredential) RequireTransportSecurity() bool {
	return false
}

func main() {
	grpc.WithPerRPCCredentials(customCredential{})
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	opts = append(opts, grpc.WithPerRPCCredentials(customCredential{}))
	conn, err := grpc.Dial("127.0.0.1:50051", opts...)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
	if err != nil {
		panic(err)
	}
	fmt.Println(r.Message)
}

验证器

install

linux

https://github.com/envoyproxy/protoc-gen-validate

第1种

git clone https://github.com/envoyproxy/protoc-gen-validate.git
cd protoc-gen-validate
go build
sudo cp protoc-gen-validate /bin

第2种

go get -d github.com/envoyproxy/protoc-gen-validate
cd $GOMODCACHE/github.com/envoyproxy/protoc-gen-validate@v0.6.1
make build  //安装在 $GOPATH/bin
cp $GOPATH/bin/protoc-gen-validate /bin

生成pb.go文件

  1. 将validate.proto导入项目
cp $GOMODCACHE/github.com/envoyproxy/protoc-gen-validate@v0.6.1/validate/validate.proto ./proto
  1. 在项目中的proto文件中import validate.proto
syntax = "proto3";

import "validate.proto";
option go_package="./;proto";

service Greeter {
    rpc SayHello (Person) returns (Person);
}

message Person {
    uint64 id    = 1 [(validate.rules).uint64.gt    = 999];
    string email = 2 [(validate.rules).string.email = true];
    string mobile  = 3 [(validate.rules).string = {
                      pattern:   "^1[3456789]\\d{9}$"}];
}
  1. 生成源码
protoc  --go_out=plugins=grpc:. --validate_out="lang=go:." helloworld.proto

另一种生成方法,不需要将validate.proto导入到项目

protoc \
  -I . \
  -I ${GOPATH}/src \
  -I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
  --go_out=":../generated" \
  --validate_out="lang=go:../generated" \
  example.proto
server.go

package main

import (
	"context"
	"net"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"google.golang.org/grpc"

	"grpcStream/grpc_validate/proto"
)

type Server struct{}

func (s *Server) SayHello(ctx context.Context, request *proto.Person) (*proto.Person,
	error) {
	return &proto.Person{
		Id: 32,
	}, nil
}
//req转换为Validator
type Validator interface {
	Validate() error
}

func main() {
        /*
          p := new(Person)
          err := p.Validate()
        */
	var interceptor grpc.UnaryServerInterceptor
	interceptor = func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		// 继续处理请求,验证参数是否合法
		//(proto.Person) 可以满足person的验证, 所有的接口 还有其他的接口那怎么办
		if r, ok := req.(Validator); ok {
			if err := r.Validate(); err != nil {
				return nil, status.Error(codes.InvalidArgument, err.Error())
			}
		}

		return handler(ctx, req)
	}
	var opts []grpc.ServerOption
	opts = append(opts, grpc.UnaryInterceptor(interceptor))

	g := grpc.NewServer(opts...)
	proto.RegisterGreeterServer(g, &Server{})
	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		panic("failed to listen:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}
client.go

package main

import (
	"context"
	"fmt"
	"grpcStream/grpc_validate/proto"

	"google.golang.org/grpc"
)

// type customCredential struct{}

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

	//opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
	opts = append(opts, grpc.WithInsecure())

	conn, err := grpc.Dial("localhost:50051", opts...)
	if err != nil {
		panic(err)
	}

	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	//rsp, _ := c.Search(context.Background(), &empty.Empty{})
	rsp, err := c.SayHello(context.Background(), &proto.Person{
		Id:     1000,
		Email:  "bobby@imooc.com",
		Mobile: "18888888888",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(rsp.Id)
}

异常处理

grpc的状态码

https://github.com/grpc/grpc/blob/master/doc/statuscodes.md

syntax = "proto3";
option go_package = ".;proto";
service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

服务端

st := status.New(codes.InvalidArgument,"invalid username")

package main

import (
	"context"
	"net"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"google.golang.org/grpc"

	"grpcStream/grpc_error/proto"
)

type Server struct{}

func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,
	error) {
        //底层使用New
        // return nil,status.New(codes.NotFound,"invalid username") 返回值*Status
	// 使用status.Error 或 status.Errorf
	return nil, status.Errorf(codes.NotFound, "记录未找到:%s", request.Name)
}

func main() {
	g := grpc.NewServer()
	proto.RegisterGreeterServer(g, &Server{})
	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		panic("failed to listen:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}

客户端

st,ok := status.FromError(err)
if !ok {
//Error was not a status error
}
st.Message()
st.Code()

package main

import (
	"context"
	"fmt"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/status"

	"grpcStream/grpc_error/proto"
)

func main() {
	//stream
	conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	//go语言推荐的是返回一个error和一个正常的信息
	ctx, _ := context.WithTimeout(context.Background(), time.Second*3)
	_, err = c.SayHello(ctx, &proto.HelloRequest{Name: "bobby"})
	if err != nil {
		st, ok := status.FromError(err)
		if !ok {
			// Error was not a status error
			panic("解析error失败")
		}
		fmt.Println(st.Message())
		fmt.Println(st.Code())
	}
}

超时机制

ctx,cancel := context.WithTimeout(context.TODO(),time.Second*3)
defer cancel()
r,err := c.SayHello(ctx,&pb.HelloRequest{Name: name})

posted @ 2021-09-10 21:46  wangzhilei  阅读(93)  评论(0编辑  收藏  举报