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脚本进行建立。后面再加
文件目录为
编写代码
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 协议 ,转载请注明出处!