5分钟实现一个gRPC

209 阅读3分钟

RPC

如果我们将服务拆分,但是通常来讲服务直接一般会有依赖,因为两个服务之间需要通信以进行数据交换,而这个过程称为远程调用。

在分布式计算中,远程过程调用Remote Procedure Call,RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client-Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用远程方法调用

RPC是一种进程间通信的模式,程序分布在不同的地址空间里。如果在同一主机里,RPC可以通过不同的虚拟地址空间(即便使用相同的物理地址)进行通讯,而在不同的主机间,则通过不同的物理地址进行交互。许多技术(通常是不兼容)都是基于这种概念而实现的。

简单的来说是一个节点请求另一个节点提供的服务,并且不需要知道底层的网络技术

gRPC 的简单使用

前提条件

下载这两个库(在modules模式下)。

go get -u google.golang.org/grpc
go get -u google.golang.org/protobuf

protobuf release,选择适合自己操作系统的压缩包文件

将解压后得到的protoc.exe二进制文件移动到$GOPATH/bin里。

然后再通过以下命令在工作目录下的bin ($GOPATH/bin)下面 生成`protoc-gen-go.exeprotoc-gen-go-grpc.exe

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
//注意你的GOOS不能错误

PS: 如果你是用的Goland 的话,你可以下载给Protocol 的插件。

编写Protobuf

首先要编写login.proto文件

syntax = "proto3";  // 版本声明
package grpcinclass; // 包名,包名可以避免对message类型之间的名字冲突,同名的message可以通过package进行区分
option go_package = "/proto"; // 生成的go文件的package名(基于一定规则生成)
​
​
//这个是返回参数
message LoginByPWResp{
  bool OK = 1;
}
//这是参数
message LoginByPWReq{
  string loginAccount = 1;
  string PassWord = 2;
}
//message 相当于struct结构体
​
service UseCenter{
  rpc LoginByPW(LoginByPWReq)   returns (LoginByPWResp){}  //所提供的服务方法(函数)
}

然后再通过以下两个命令生成login.pd.gplogin_grpc.pd.go两个文件。

protoc --go_out=. ./login.proto
protoc --go-grpc_out=. ./login.proto

Sever 端

package main
​
import (
   "context"
   "doubna_userinfo/proto"
   "doubna_userinfo/server"
   "fmt"
   "google.golang.org/grpc"
   "log"
   "net"
)
​
const (
   address = "localhost:50051"
)
func main() {
   // 监听端口
   lis, err := net.Listen("tcp", address)
   if err != nil {
      log.Fatalf("failed to listen: %v", err)
   }
​
   s := grpc.NewServer() //获取新服务示例
   proto.RegisterUseCenterServer(s,&UserServer{})
   // 开始处理
   if err := s.Serve(lis); err != nil {
      log.Fatalf("failed to serve: %v", err)
   }
​
}
​
type UserServer struct {
   proto.UnimplementedUseCenterServer  
}
​
​
​
func  (s *UserServer) LoginByPW(ctx context.Context,req *proto.LoginByPWReq)(*proto.LoginByPWResp, error){
​
   resp := &proto.LoginByPWResp{}
​
   _, flag, err := server.JudgePasswordCorrect(req.LoginAccount, req.PassWord) //换成你自己的
​
   if err != nil {
      resp.OK=false
      fmt.Println("UserLogin is", err)
      return  resp,err
   }
​
   if flag {
      resp.OK=true
      return  resp,err
   }else {
      resp.OK=false
      return  resp,err
   }
}

Client 端

调用Sever的一端

package main
​
import (
   "context"
   "doubna_userinfo/proto"
   "doubna_userinfo/tool"
   "github.com/gin-gonic/gin"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials/insecure"
   "log"
   "net/http"
)
​
const (
   address = "localhost:50051"
)
​
var  Conn  *grpc.ClientConn
var UserClient   proto.UseCenterClient
​
func main() {
   //建立链接
   Conn,err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
   if err != nil {
      log.Fatalf("did not connect: %v", err)
   }
​
   UserClient= proto.NewUseCenterClient(Conn)
   defer Conn.Close()
​
   engine:=gin.Default()
   engine.POST("/api/user/login/pw",login)
   engine.Run(":8080")
}
​
func login(ctx *gin.Context) {
​
   loginAccount := ctx.PostForm("loginAccount")
   password := ctx.PostForm("password")
​
​
   if loginAccount == "" {
      tool.RespErrorWithData(ctx, "请输入注册时用的邮箱或者手机号")
      return
   }
​
   if password == "" {
      tool.RespErrorWithData(ctx, "请输入密码")
      return
   }
​
​
   loginResp,_:= UserClient.LoginByPW(context.Background(), &proto.LoginByPWReq{
      LoginAccount: loginAccount,
      PassWord: password,
   })
​
​
   if loginResp.OK {
      ctx.JSON(http.StatusOK, gin.H{
         "access_token":  "",
         "refresh_token": "",
         "token":         "",
         "status":        "ture",
         "data":          "",
      })
      return
   }else {
      ctx.JSON(http.StatusOK, gin.H{
         "access_token":  "",
         "refresh_token": "",
         "token":         "",
         "status":        "false",
         "data":          "",
      })
   }
}

项目大概目录

​
  grpc_demo/
    |__config/
    |       |_app.json  
    |__server/
    |       |__login_server.go  (main)
    |__client/
            |_ login_client.go  (main)
    |__proto/
           |_login.pd.go
           |_login_grpc.pd,go
    |__tool/
    |    |__...
    |    |__...
    |__login.proto
​
​

ok ,到目前为止,简单的一个登录服务就实现了......