Go语言中gRpc操作

175 阅读3分钟

grpc服务端

首先安装环境,这里以mac os系统为例

Protoc安装

brew install protobuf

Protoc-gen-go的安装,然后在终端执行下方命令

go install github.com/golang/protobuf/protoc-gen-go@latest

安装grpc插件

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

编写proto代码

先定义一个hello.proto文件

syntax = "proto3";
option go_package = "./;proto";
package pb;

// 定义输入消息结构体
message HelloRequest {
  string name = 1;
}

// 定义输出消息结构体
message HelloResponse {
  string message = 1;
}

/*
  定义服务接口(注意这里service后面的名字HelloService要跟下面service端里面的结构体的名字一样)
  否者客户端调用的时候就会报错:rpc error: code = Unimplemented desc = unknown method SayHello for service pb.HelloService
*/
service HelloService {
  rpc SayHello(HelloRequest) returns (HelloResponse);
}

开始写grpc服务端

  • 服务端代码

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "log"
    "net"
    pb "test-logus/demo15-grpc/server/pb"
)

/*
这个结构体的名字要跟proto文件中定义服务接口的service名字相同,
否者客户端调用就会出现报错:rpc error: code = Unimplemented desc = unknown method SayHello for service pb.HelloService
*/
type HelloService struct {
    pb.UnimplementedHelloServiceServer
}

// 这就是所谓的远程函数
func (s *HelloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {

    return &pb.HelloResponse{Message: "Hello " + in.Name}, nil
}


// 示例的验证函数
func validateRequest(ctx context.Context) bool {
    // 在这里执行你的验证逻辑
    // 返回 true 表示验证通过,返回 false 表示验证失败
    return true
}

// 添加一个自定义的拦截器函数
func myInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    fmt.Println("拦截器:请求已经到达,正在处理...")
    
    // 进行验证,如果验证不通过,返回错误
    if !validateRequest(ctx) {
        return nil, status.Errorf(codes.PermissionDenied, "权限验证失败")
    }

    // 在请求处理之前的操作
    resp, err := handler(ctx, req)

    // 在请求处理之后的操作
    fmt.Println("拦截器:请求处理完成")
    return resp, err
}

func main() {
    aa := true
    if aa {

       // 创建带有TLS证书的gRPC服务器
       //creds := credentials.NewTLS(tlsConfig)
       creds, errs := credentials.NewServerTLSFromFile("demo15-grpc/server/ling.evilangel.vip.cer", "demo15-grpc/server/ling.evilangel.vip.key")
       if errs != nil {
          log.Fatalf("Failed to setup TLS: %v", errs)
       }
       // 创建监听器
       lis, err := net.Listen("tcp", ":8972")
       if err != nil {
          fmt.Printf("监听本地端口失败: %v\n", err)
          return
       }

       s := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(myInterceptor))
       // 在gRPC服务端注册服务
       pb.RegisterHelloServiceServer(s, &HelloService{})
       fmt.Println("gRPC服务启动成功!!!")
       // 启动服务
       errss := s.Serve(lis)
       if errss != nil {
          fmt.Printf("启动服务失败: %v", errss)
          return
       }

    } else {
       //监听本地端口8972
       lis, err := net.Listen("tcp", ":8972")
       if err != nil {
          fmt.Printf("监听本地端口失败: %v\n", err)
          return
       }
       //创建grpc服务
       s := grpc.NewServer(grpc.UnaryInterceptor(myInterceptor))
       //在gRpc服务端注册服务
       pb.RegisterHelloServiceServer(s, &HelloService{})

       fmt.Println("grpc服务启动成功!!!")
       //启动服务
       errs := s.Serve(lis)
       if errs != nil {
          fmt.Printf("启动服务失败: %v", errs)
          return
       }
    }

}

用下面的命令生成代码,这样生成就有grpc.pb.go文件了

  • 注意最后面跟的如果是proto文件名的话·那么此时你要进入这个文件所在的终端,才能执行下面这个命令
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto

编译启动

go build

./service

编写client端

把上面生成的proto文件拷贝一份给客户端文件夹下的pb文件夹下面

  • grpc客户端代码

package main

import (
    "context"
    "crypto/tls"
    "flag"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    pb "test-logus/demo15-grpc/client/pb"
    "time"
)

const (
    defaultName = "world"
)

var (
    addr = flag.String("addr", "127.0.0.1:8972", "the address to connect to")
    name = flag.String("name", defaultName, "this is service name")
)

func main() {
    flag.Parse()

    // 忽略证书验证(仅用于开发和测试,不建议在生产环境使用)
    creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})

    // 连接到 gRPC 服务器
    conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds))
    if err != nil {
       fmt.Printf("连接到 gRPC 服务器失败:%v\n", err)
       return
    }
    defer func(conn *grpc.ClientConn) {
       err := conn.Close()
       if err != nil {
          // 如果需要,在连接关闭时处理错误
       }
    }(conn)

    // 创建 gRPC 客户端
    client := pb.NewHelloServiceClient(conn)

    go func() {
       // 调用服务方法
       for {
          _, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
          if err != nil {
             fmt.Printf("调用服务方法失败:%v\n", err)
             return
          }

          fmt.Println("心跳成功:")
          time.Sleep(15 * time.Second)
       }

    }()

    for {
       // 调用服务方法
       reply, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
       if err != nil {
          fmt.Printf("调用服务方法失败:%v\n", err)
          return
       }

       fmt.Println("服务器回复:", reply.Message)
       time.Sleep(180 * time.Second)
    }

}

编译执行

go build

./client -name="梅梅"

最后看下服务端和客户端的截图

  • 服务端截图 image.png

  • 客户端截图 image.png