Protobuf文件实现
若需要采用grpc相关,需要在proto文件中以service定义,例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
syntax = "proto3";
package auth;
import "user.proto";
option go_package = "./auth;auth";
// 定义服务
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
rpc Register(RegisterRequest) returns (RegisterResponse);
}
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
user.User user = 2;
}
message RegisterRequest {
string username = 1;
string password = 2;
string email = 3;
}
message registerresponse {}使用编译命令编译后,除了普通的.pb.go文件,还会生成xx_grpc.pb.go文件,里面实现了客户端和服务端需要的一些方法
1
2
protoc -I. -I/usr/include --go_out=. --go_opt=module=grpc-study --go-grpc_out=. --go-grpc_opt=module=grpc-study ./au
th.proto实现一个server
首先在/internal/grpc下创建一个server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package grpc
import (
"fmt"
"net"
"google.golang.org/grpc"
)
func StartGrpcServer() {
ln, err := net.Listen("tc p", ":8000")
if err != nil {
panic(err)
}
s := grpc.NewServer()
err = s.Serve(ln)
if err != nil {
fmt.Println(err)
return
}
}接下来需要实现方法,在service文件夹下创建auth.go,实现一个登录方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package service
import (
"context"
userProto "grpc-study/auth"
"grpc-study/model"
)
type AuthService struct {
}
func NewAuthService() *AuthService {
return &AuthService{}
}
func (s *AuthService) Login(ctx context.Context, req *userProto.LoginRequest) (*model.UserModel, error) {
return &model.UserModel{
Id: 1,
Username: req.Username,
Password: req.Password,
Email: "",
},nil
}Model层实现如下:
1
2
3
4
5
6
7
8
package model
type UserModel struct {
Id int64
Username string
Password string
Email string
}接下来需要将业务的注册到服务端,需要实现注册方法,ctrl建立文件auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package ctrl
import (
"context"
userProto "grpc-study/auth"
"grpc-study/service"
userModelProto "grpc-study/user"
"log"
)
type AuthController struct {
userProto.UnimplementedAuthServiceServer
}
func validateLoginRequest(req *userProto.LoginRequest) error {
return nil
}
func (c *AuthController) Login(ctx context.Context, req *userProto.LoginRequest) (*userProto.LoginResponse, error) {
// 验证参数
if err := validateLoginRequest(req); err != nil {
return nil, err
}
// 业务逻辑
log.Println("Login request received:", req.Username, req.Password)
userModel, err := service.NewAuthService().Login(ctx, req)
if err != nil {
return nil, err
}
// 组装响应数据
resp := &userProto.LoginResponse{
Token: "123",
User: &userModelProto.User{
Id: userModel.Id,
},
}
// 返回参数
return resp, nil
}
func (c *AuthController) Register(ctx context.Context, req *userProto.RegisterRequest) (*userProto.RegisterResponse, error) {
return &userProto.RegisterResponse{}, nil
}RegisterAuthServiceServer用于方法的内部注册,即将我们自己定义的AuthController交给gRPC服务器,让将来发入的auth服务请求交给这个实例处理,其中AuthController是我们自己定义的,实现了AuthServiceServer接口要求的方法的实例
最后在server端添加注册:
1
2
3
4
// 注册服务到服务端中
authCtrl := &ctrl.AuthController{}
s := grpc.NewServer()
userProto.RegisterAuthServiceServer(s, authCtrl)实现一个Client
开一个文件夹放客户端,/authclient下的main.go文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main
import (
"context"
userProto "grpc-study/auth"
"fmt"
"log"
"sync"
"sync/atomic"
"google.golang.org/grpc"
)
func main() {
cc := NewUserClientPool("localhost:8000", 5)
resp,err := cc.GetClient().Login(context.Background(), &userProto.LoginRequest{
Username: "admin",
Password: "admin",
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Login response received:", resp.Token, resp.User.Id)
}
// 多路复用
type userClientPool struct {
clients []userProto.AuthServiceClient
mu sync.Mutex
index int64
}
func NewUserClientPool(addr string, size int) *userClientPool {
cc, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
panic(err)
}
var clients []userProto.AuthServiceClient
for i := 0; i < size; i++ {
clients = append(clients, userProto.NewAuthServiceClient(cc))
}
return &userClientPool{
clients: clients,
index: 0,
}
}
func (p *userClientPool) GetClient() userProto.AuthServiceClient {
// 新增index
index := atomic.AddInt64(&p.index, 1)
return p.clients[int(index)%len(p.clients)]
}- 这里采用了多路复用技术,即开一个用户池,可以开启多个连接
- 这里实现了负载均衡,每次调用后悔从池里挑一个client发送请求
- 请求再发送给服务端,服务端执行业务逻辑,返回响应
整体链路
上面实现的是一个标准的gRPC调用闭环:
客户端生成stub发起RPC→
服务端启动时把controller注册到gRPC的服务端→
收到请求后由controller调用业务层→
把业务结果组装为proto响应返回
入口
服务端主程序仅仅调用了server.go的开启服务端函数,这个函数通过net.Listen()监听tcp,创建gRPCserver,再把AuthController通过通过RegisterAuthServiceServer注册进去
这样,proto里定义的auth服务就和本地实现的auth业务逻辑绑定起来了,服务器知道如何处理rpc请求
controller
请求进来以后,会落到ctrl/auth.go,进入controller层
先进行参数校验,再调用方法AuthService.Login,实现后返回内部模型model.UserModel,ctrl再把返回的数据转换成proto的数据类型并组装成响应返回
因此service层只管业务数据,ctrl层负责把内部模型翻译为对外rpc协议
客户端
客户端在main.go中先使用grpc.Dial()建立连接,通过NewUserClientPool创建用户池,并从中GetClient()获取一个用户创建客户端stub
后面调用方法Login()会直接把你的LoginRequest序列化后发送到服务端,服务端处理后再把LoginResponse反序列化后返回客户端,拿到想要的东西