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.exe 和protoc-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.gp和login_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 ,到目前为止,简单的一个登录服务就实现了......