本人旨在先跑代码,回头再看理论,所有文章代码根据步骤即可运行,前提是环境无误
grpc相关的环境需要自行搭建,示例有简单的 proto消息内嵌示例,grpc传值,服务端及客户端请求拦截等功能
### 一 新建一个pb目录
step1: cd pb step2: touch user.proto
user.proto内容如下
syntax = "proto3";
package pb;
option go_package="./pb";
enum Gender { Male = 0; Female=1;
} message Res{ string url=1; string title=2; }
message UserInfoReq{ string name=1; int64 age =2; Gender gender=3; repeated string Class_study =4; Res res = 5; }
message UserInfoResp{ string name=1; int64 age=2; Gender gender=3; Res res= 4; repeated string study=5; }
service UserService{ rpc Register(UserInfoReq)returns(UserInfoResp); }
运行命令如下
protoc --go_out=. --go-grpc_out=. user.proto
参数说明:
--go_out 生成go的pb文件路径 # 其他语言就是 ex:java_out....
--go-grpc_out 生成go的grpc.pb.go文件的路径
--go_opt=paths=source_relative # 默认是import
--go-grpc_opt=paths=source_relative # 默认是import
默认的imort 路径 就是指option go_package='/pb'路径
imort模式完整的目录关系如下:
go_out/$go_package/pb_filename.pb.go
source_relative模式完整的目录关系如下:
go_out/$pb_filedir/$pb_filename.pb.go
二 grpc server
在pb同级目录新建server文件夹
setp1 cd server
step2 touch server.go
server.go
package main
import ( "context" "errors" "fmt" "grpc_pkg/simple/pb" "log" "net" "time"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc" "google.golang.org/grpc/reflection" )
type User struct { pb.UnimplementedUserServiceServer }
func (t *User) Register(ctx context.Context, req *pb.UserInfoReq) (*pb.UserInfoResp, error) { fmt.Println(req.Gender.String()) return &pb.UserInfoResp{ Name: req.Name, Gender: req.Gender, Res: &pb.Res{ Url: req.Res.Url, Title: req.Res.Title, }, Age: req.Age, Study: req.ClassStudy, }, nil }
//2023/04/27 11:03:46 calling "/pb.UserService/Register" with request name:"HQ" age:13 res:{url:"这是url" title:"这是标题"} //Male //2023/04/27 11:03:46 finished "/pb.UserService/Register" in 9.578µs
func serverInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { log.Printf("calling %q with request %v", info.FullMethod, req.(*pb.UserInfoReq).Res) start := time.Now() resp, err = handler(ctx, req) // 获取传递的值 md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, errors.New("没有数据") } os := md.Get("client-os") fmt.Println("客户端的系统是:", os) log.Printf("finished %q in %v", info.FullMethod, time.Since(start)) // 拦截条件示例 //if os[0] == "darwin" { // return resp, errors.New("系统不对") //} return resp, err }
func main() { listen, err := net.Listen("tcp", ":8081") if err != nil { fmt.Println(err) return }
gServer := grpc.NewServer( grpc.UnaryInterceptor(serverInterceptor)) // 注册server 服务 pb.RegisterUserServiceServer(gServer, &User{})
//注册反射服务 reflection.Register(gServer)
if err := gServer.Serve(listen); err != nil { log.Fatalf("failed to serve: %v", err) } }
<font color='red'> 注意: </font>
> user结构体中内嵌了 pb.UnimplementedUserServiceServer结构体
> 原因在grpc.pb.go中有说明:
UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
简单的翻译一下就是,你不内嵌,这个结构体就没办法实现对应的接口
### 三 grpc client
在pb同级目录新建client文件夹
> setp1 cd client
> step2 touch client.go
client.go
package main
import ( "context" "fmt" "grpc_pkg/simple/pb" "log" "runtime" "time"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc" )
func clientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { log.Printf("calling %q with request %v", method, req) start := time.Now() client_t := runtime.GOOS // grpc 传值 ctx = metadata.AppendToOutgoingContext(ctx, "client-os", client_t) err := invoker(ctx, method, req, reply, cc, opts...) log.Printf("finished %q in %v", method, time.Since(start)) // 拦截条件 // if client_t !="sss"{ // return errors.new("系统有误") //} return err }
func main() { conn, err := grpc.Dial(":8081", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(clientInterceptor)) if err != nil { fmt.Println(err) return } defer conn.Close()
c := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()
res, err := c.Register(ctx, &pb.UserInfoReq{ Name: "HQ", Gender: pb.Gender_Male, Age: 13, Res: &pb.Res{ Title: "这是标题", Url: "这是url", }, })
if err != nil { fmt.Println(err) return } fmt.Println(res.Gender) fmt.Println(res.String())
}
四 调用
先启动server
在启动client
client运行结果