您现在的位置是:首页 >学无止境 >go grpc实战网站首页学无止境

go grpc实战

小沙弥爱吃肉 2023-07-19 12:00:02
简介go grpc实战

go rpc实战

什么是rpc以及rpc的原理就不加以阐述了,wiki-rpc对其进行了说明。本文 以登录过程为例,使用go作为开发语言,使用grpc库实现了登录接口。具体过程及代码如下所示。

环境准备

  1. app的同级目录创建appGoPath目录,在golandFile->Settings中配置GOPATHappGoPath目录所在路径,然后执行下列命令准备grpc环境
    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
    
  2. 创建目录:app/cmd/componentTest/pb,存放登录相关的rpc相关的文件
  3. pb目录下创建登录服务的grpc服务文件login.proto
    syntax = "proto3";
    
    package login;
    
    option go_package = "../pb";
    
    message LoginRequest{
    string username=1;
    string password=2;
    }
    
    message LoginResponse{
    int32 code=1;
    string msg=2;
    }
    
    service LoginService{
    rpc Login(LoginRequest)returns(LoginResponse);
    }
    
  4. pb目录下创建build.sh文件,增加如下内容
    protoc --go_out=. --go_opt=paths=source_relative 
        --go-grpc_out=require_unimplemented_servers=false:. --go-grpc_opt=paths=source_relative 
        login.proto
    

    require_unimplemented_servers=false作用: 在LoginServiceServer中不生成mustEmbedUnimplementedLoginServiceServer接口

  5. pb目录所在路径下执行build.sh文件,会自动生成以下两个文件
    • login.pb.go
    • login_grpc.pb.go
  6. 生成的上述两个文件在goland中提示Cannot resolve symbol 'protobuf',使用golandSync dependency of机制进行同步即可
  7. app/cmd/componentTest目录下创建test目录,创建rpc_login_test.go文件,添加代码如下:
    package test
    
    import (
       "context"
       "google.golang.org/grpc"
       "google.golang.org/grpc/credentials/insecure"
       "log"
       "app/cmd/componentTest/pb"
       "net"
       "os"
       "os/signal"
       "syscall"
       "testing"
       "time"
    )
    
    type LoginServiceServerImpl struct{}
    
    func (l LoginServiceServerImpl) Login(ctx context.Context, request *pb.LoginRequest) (*pb.LoginResponse, error) {
       username := request.Username
       password := request.Password
       if username == "leebai" && password == "123456" {
          return &pb.LoginResponse{
             Code: 200,
             Msg:  "success",
          }, nil
       } else {
          return &pb.LoginResponse{
             Code: 201,
             Msg:  "fail",
          }, nil
       }
    }
    
    func StartRpcServer() {
       s := grpc.NewServer()
       pb.RegisterLoginServiceServer(s, new(LoginServiceServerImpl))
    
       port := ":8081"
       listener, err := net.Listen("tcp", port)
       if err != nil {
          log.Printf("listen port %s failed,err=%v
    ", port, err)
       }
    
       s.Serve(listener)
    }
    
    // TestRpcLoginService 启动Rpc登录服务
    func TestRpcLoginService(t *testing.T) {
       go StartRpcServer()
    
       ch := make(chan os.Signal, 1)
       signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
       exitCode := <-ch
       log.Printf("Exit Code:%v", exitCode)
    }
    
    // TestRpcClientLogin Rpc登录客户端,连接启动的Rpc登录服务,发送请求,获取响应
    func TestRpcClientLogin(t *testing.T) {
       targetHost := "localhost:8081"
       conn, err := grpc.Dial(targetHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
       if err != nil {
          log.Printf("Dial %s failed,err=%v
    ", targetHost, err)
       }
       defer conn.Close()
    
       // 构建一次用户名和密码匹配的rpc请求
       c := pb.NewLoginServiceClient(conn)
       t1 := time.Now()
       rpcReply, err := c.Login(context.Background(), &pb.LoginRequest{
          Username: "leebai",
          Password: "123456",
       })
       log.Printf("spend time:%v", time.Now().Sub(t1).Milliseconds())
       if err != nil {
          log.Printf("login failed,err=%v
    ", err)
       } else {
          log.Printf("login response: %v
    ", rpcReply)
       }
    
        构建一次用户名和密码不匹配的rpc请求
       //c2 := pb.NewLoginServiceClient(conn)
       //rpcReply, err = c2.Login(context.Background(), &pb.LoginRequest{
       //	Username: "zhangsan",
       //	Password: "123456",
       //})
       //if err != nil {
       //	log.Printf("login failed,err=%v
    ", err)
       //} else {
       //	log.Printf("login response: %v
    ", rpcReply)
       //}
    }
    

    以上func (l LoginServiceServerImpl) Login(ctx context.Context, request *pb.LoginRequest) (*pb.LoginResponse, error)
    需要实现login_grpc.pb.go中的接口type LoginServiceServer interface

  8. 先运行TestRpcLoginService方法启动rpc服务程序
  9. 再运行TestRpcClientLogin方法启动rpc客户端程序,向启动的rpc服务程序发起一次rpc调用,结果如下所示:
    === RUN   TestRpcClientLogin
    2023/05/05 14:15:08 spend time:11
    2023/05/05 14:15:08 login response: code:200 msg:"success"
    --- PASS: TestRpcClientLogin (0.03s)
    PASS
    
  10. 至此就完成了一个登录请求的远程rpc服务调用
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。