今日学习grpc通信,做一个简单的小demo来验证效果
如果没有装环境,简单的装一下
https://juejin.cn/post/7137709231624421412
go get github.com/golang/protobuf/protoc-gen-go
新建一个A项目
建立一个目录用于存proto文件,并新建一个proto文件,这个随便命名
AService.proto
// 指定当前proto版本
// proto的语法注意分号,写go写习惯的话,会经常遗漏分号
syntax = "proto3";
// 指定生成proto生成的go文件的目录和包名,本身是一个路径,如果没有配置包名,最后的一个目录会被当做包名
option go_package = "../service";
// 定义request model,假设A服务有个User的一些操作的model
// 假设当前功能是传入userName 和 userPassword
message UserRequest{
string user_name = 1; // 这里“=”后面的数字代表顺序,与真实值无关
string user_password = 2;
}
// 定义response model
// 假设当前功能是返回userID和Token
message UserResponse{
string user_id = 1;
string user_token= 2;
}
// 定义服务能提供的服务
service UserService{
// 定义具体的方法,此定义更像一个接口(interface),他不做具体实现,只是声明该Grpc能调用的方法
rpc GetUserToken(UserRequest) returns(UserResponse);
}
进入到protobufFile目录下(cd protobufFile),执行生成
protoc --go_out=plugins=grpc:./ .\AServer.proto
这时候可以看到已经生成了service目录和一个pb.go文件
这个文件里可以看到刚才我们定义的service UserService,连proto文件里的注释也一并被拿了进来
所以下一部就是按照这个“接口”进行方法实现,当然,真正的业务拆分环境当中,都是先实现了方法,然后按服务拆分的时候做响应的接口编写,通常,我们称之为“暴露”,就是给其他服务暴露出来某某方法以供调用。
在根目录下新建目录model,并新建user.go
package model
import (
"context"
"errors"
"go-grpc-test-A/service"
)
var UserServiceServer = &userServiceServer{}
type userServiceServer struct {
}
func (u userServiceServer) GetUserToken(ctx context.Context, request *service.UserRequest) (*service.UserResponse, error) {
// 模拟过程
if request.UserName != "" && request.UserPassword != "" {
response := service.UserResponse{
UserId: "123",
// 验证请求是否成功瞎写的,请不要在意
UserToken: "你传入的名字和密码是:" + request.UserName + "," + request.UserPassword,
}
return &response, nil
} else {
err := errors.New("参数不对")
return &service.UserResponse{}, err
}
}
package main
import (
"go-grpc-test-A/model"
"go-grpc-test-A/service"
"google.golang.org/grpc"
"log"
"net"
)
func main() {
grpcServer := grpc.NewServer()
// 这里第二个参数写我们已经实现接口的model包下的UserServiceServer
service.RegisterUserServiceServer(grpcServer, model.UserServiceServer)
listener, err := net.Listen("tcp", ":8001")
if err != nil {
log.Fatal("服务监听失败", err)
}
_ = grpcServer.Serve(listener)
}
新建项目后,把A项目的service目录和pb.go文件一起copy过来,保持两个项目一致(真实工程当中当然不是手动保持一致),记得运行一下 go mod tidy 把依赖都拉上
编写B服务的main方法
package main
import (
"context"
"fmt"
"go-grpc-test-B/service"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
)
func main() {
// 建立一个链接,请求A服务
// 真实项目里肯定是通过配置中心拿服务名称,发给注册中心请求真实的A服务地址,这里都是模拟
// 第二个参数是配置了一个证书,因为没有证书会报错,但是我们目前没有配置证书,所以需要insecure.NewCredentials()返回一个禁用传输安全的凭据
connect, err := grpc.Dial(":8001", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
// 记得关闭链接
defer connect.Close()
// 调用ASerer.pb.go里面的NewUserServiceClient
client := service.NewUserServiceClient(connect)
// 直接调用 A服务中的GetUserToken方法,注意,这是B服务,本地并没有该方法的实现
response, err := client.GetUserToken(context.Background(), &service.UserRequest{UserName: "我是萝卜", UserPassword: "123456"})
if err != nil {
log.Fatal("调用gRPC方法错误: ", err)
}
fmt.Printf("调用gRPC方法成功,user_id = %s, user_token = %s", response.UserId, response.UserToken)
}
先启动A服务,启动成功后,启动B服务来调用A服务
B服务已经能成功访问A服务下的GetToken方法,至此,Grpc调用成功