什么是rpc以及rpc的原理就不加以阐述了,wiki-rpc对其进行了说明。本文 以登录过程为例,使用go作为开发语言,使用grpc库实现了登录接口。具体过程及代码如下所示。
app
的同级目录创建appGoPath
目录,在goland
的File
->Settings
中配置GOPATH
为appGoPath
目录所在路径,然后执行下列命令准备grpc环境go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
app/cmd/componentTest/pb
,存放登录相关的rpc相关的文件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);
}
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接口
pb
目录所在路径下执行build.sh
文件,会自动生成以下两个文件
login.pb.go
login_grpc.pb.go
Cannot resolve symbol 'protobuf'
,使用goland
的Sync dependency of
机制进行同步即可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\n", 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\n", 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\n", err)
} else {
log.Printf("login response: %v\n", 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\n", err)
//} else {
// log.Printf("login response: %v\n", rpcReply)
//}
}
以上
func (l LoginServiceServerImpl) Login(ctx context.Context, request *pb.LoginRequest) (*pb.LoginResponse, error)
是
需要实现login_grpc.pb.go
中的接口type LoginServiceServer interface
,
TestRpcLoginService
方法启动rpc服务程序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