grpc

本文最后更新于:1 年前

grpc入门

准备工作

1.安装 protobuf

https://github.com/protocolbuffers/protobuf/releases
选择对应版本下载后解压到GOPATH路径下即可

2.在go.mod中引入grpc

go get google.golang.org/grpc

3.安装GO协议编译器的插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

4.编写proto文件

创建client和service目录,在两目录中创建proto目录,分别创建client.proto和server.proto文件
proto文件相当于一层约束,规定了在rpc调用时,有哪些api,api接口需要传递哪些参数,返回值类型,以及服务名称等信息

syntax = "proto3";  
  
// 生成go文件时的包名  
option go_package = ".;service";  
  
// 服务定义  
service SayHello {  
    // 定义一个方法 方法的形参和返回值类型在下面定义  
    rpc SayHello(HelloRequest) returns (HelloResponse){}  
}  
  
// message 就像是go中struct,java中的class  
message HelloRequest{  
    string requestName = 1; // 数字作为标识号 放在函数具体位置  
    int64 age = 2;          // //    repeated string ids = 3;// repeated为切片类型  
}  
  
  
message HelloResponse{  
    string responseMsg = 1;  
}

5.初始化项目文件

创建client和service目录,在两目录中创建proto目录,之后在cmd中执行

protoc --go_out=. .\server.proto
protoc --go-grpc_out=. .\server.proto

命令执行成功后会创建hello.pb.go和hello_grpc.pb.go,可以写一个shell脚本进行建立。后面再加

文件目录为
Pasted image 20230207235146

编写代码

1.服务端

对grpc_pb.go文件中的UnimplementedSayHelloServer函数进行重写

type server struct {  
   pb.UnimplementedSayHelloServer  //导入包 pb "grpc_demo/server/proto" 
}  
// 实现业务逻辑  
func (s *server) SayHello(ctx context.Context,req *pb.HelloRequest) (*pb.HelloResponse, error) {  
   return &pb.HelloResponse{ResponseMsg: "hello, " + req.RequestName}, nil  
}

启动服务端

  • 监听端口
  • 启动grpc服务
  • 在grpc服务器中注册服务,即上面的server
  • 启动grpc服务
    func main() {  
       // 监听端口  
       listen, _ := net.Listen("tcp", ":8081")  
       // 开启grpc服务  
       grpcServer := grpc.NewServer()  
       // 在grpc服务器中注册我们自己写的服务  
       pb.RegisterSayHelloServer(grpcServer, &server{})  
       // 启动服务  
       err := grpcServer.Serve(listen)  
       if err != nil {  
          panic(err)  
       }  
    }

2.客户端

服务端代码相对简单

  • 连接服务端
  • 创建请求服务的客户端
  • 执行rpc调用
  • 获取结果
    func main() {   
       // 连接到server 此处不使用安全连接  
       conn, err := grpc.Dial("127.0.0.1:8081", grpc.WithTransportCredentials(insecure.NewCredentials()))  
      
       if err != nil {  
          log.Fatal(err)  
       }  
       defer conn.Close()  
      
       // 建立连接  
       client := pb.NewSayHelloClient(conn)  
       // 执行rpc调用,从server端获取执行结果  
       helloResponse, err := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "sunzyclient", Age: 10})  
       if err != nil {  
          log.Fatal(err)  
       }  
      
       fmt.Println(helloResponse.GetResponseMsg())  
    }

通过上面的步骤,即可完成最简单的grpc调用

3.增加token认证

server中main.go

package main  
import (  
   "context"  
   "errors"   
   "fmt"   
   "google.golang.org/grpc"       "google.golang.org/grpc/credentials/insecure"   "google.golang.org/grpc/metadata"   
   pb "grpc_demo/server/proto"  
   "net"
   )  
  
type server struct {  
   pb.UnimplementedSayHelloServer  
}  
// 实现业务逻辑  
func (s *server) SayHello(ctx context.Context,req *pb.HelloRequest) (*pb.HelloResponse, error) {  
   // 获取元数据  
   md, ok := metadata.FromIncomingContext(ctx)  
   if !ok {  
      return nil, errors.New("token does not exist")  
   }  
  
   var appId string  
   var appKey string  
   if v, ok := md["appid"]; ok { // 这里所有字母都为小写  
      appId = v[0]  
   }  
   if v, ok := md["appkey"]; ok {  
      appKey = v[0]  
   }  
   fmt.Printf("appId: %v, appKey: %v", appId, appKey)  
   if appId != "sunzy" || appKey != "123456"{  
      return nil, errors.New("token error")  
   }  
  
   return &pb.HelloResponse{ResponseMsg: "hello, " + req.RequestName}, nil  
}  
  
func main() {  
   // 监听端口  
   listen, _ := net.Listen("tcp", ":8081")  
   // 开启grpc服务  
   grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))  
   // 在grpc服务器中注册我们自己写的服务  
   pb.RegisterSayHelloServer(grpcServer, &server{})  
   // 启动服务  
   err := grpcServer.Serve(listen)  
   if err != nil {  
      panic(err)  
   }  
}

client中的main.go

package main  
  
import (  
   "context"  
   "fmt"   
   "google.golang.org/grpc"    "google.golang.org/grpc/credentials/insecure"   
   pb "grpc_demo/server/proto"  
   "log"
   )  
  
  
// 自定义token认证  
type ClientAuthToken struct {  
  
}  
  
func (c *ClientAuthToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error){  
   return map[string]string{  
      "appId" : "sunzy",  
      "appKey" : "123456",  
   }, nil  
}  
  
func (c *ClientAuthToken) RequireTransportSecurity() bool{  
   return false  // 不使用ssl认证  
}  
  
func main() {  
   var ops []grpc.DialOption  
   ops = append(ops, grpc.WithTransportCredentials(insecure.NewCredentials()))  
   ops = append(ops, grpc.WithPerRPCCredentials(new(ClientAuthToken)))  
   // 连接到server 此处不使用安全连接  
   conn, err := grpc.Dial("127.0.0.1:8081", ops...)  
  
   if err != nil {  
      log.Fatal(err)  
   }  
   defer conn.Close()  
  
   // 建立连接  
   client := pb.NewSayHelloClient(conn)  
   // 执行rpc调用,从server端获取执行结果  
   helloResponse, err := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "sunzyclient", Age: 10})  
   if err != nil {  
      log.Fatal(err)  
   }  
  
   fmt.Println(helloResponse.GetResponseMsg())  
}

grpc拦截器

上面代码中token是在服务函数中验证的,显然不合理,grpc与gin一样也有拦截器,可以添加一个拦截器进行token的验证

创建grpc的拦截器

func tokenHandler() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		log.Print("tokenHandler Interceptor...")
		// 获取元数据
		md, ok := metadata.FromIncomingContext(ctx)
		if !ok {
			return nil, errors.New("token does not exist")
		}
		var appId string
		var appKey string
		if v, ok := md["appid"]; ok { // 这里所有字母都为小写
			appId = v[0]
		}
		if v, ok := md["appkey"]; ok {
			appKey = v[0]
		}
		log.Printf("appId: %v, appKey: %v\n", appId, appKey)
		if appId != "sunzy" || appKey != "123456"{
			return nil, errors.New("token error")
		}
		log.Print("token is valid")
		resp, err = handler(ctx,req)
		return resp, err
	}
}

在main函数中添加拦截器

grpcServer := grpc.NewServer(grpc.Creds(insecure.NewCredentials()),grpc.ChainUnaryInterceptor(tokenHandler())) //添加拦截器

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

 目录

Copyright © 2020 my blog
载入天数... 载入时分秒...