使用Facebook/Ent框架快速生成gRPC服务端
ent 项目地址: https://gitcode.com/gh_mirrors/ent4/ent
前言
在现代微服务架构中,gRPC因其高效的二进制协议和跨语言支持而广受欢迎。而Ent作为Go语言中强大的实体框架,为数据访问层提供了优雅的解决方案。本文将介绍如何利用Ent框架快速生成一个功能完整的gRPC服务端,实现数据的CRUD操作。
准备工作
首先确保你的开发环境已经安装以下工具:
- Go 1.16+
- Protocol Buffers编译器(protoc)
- protoc-gen-go插件
- protoc-gen-go-grpc插件
项目初始化
创建一个新项目并初始化Go模块:
mkdir ent-grpc-demo
cd ent-grpc-demo
go mod init ent-grpc-demo
定义Ent Schema
- 首先使用Ent CLI工具初始化User实体:
go run -mod=mod entgo.io/ent/cmd/ent new User
- 添加entproto依赖:
go get -u entgo.io/contrib/entproto
- 编辑
ent/schema/user.go
文件,定义User实体的结构:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
"entgo.io/contrib/entproto"
)
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Unique().
Annotations(
entproto.Field(2),
),
field.String("email_address").
Unique().
Annotations(
entproto.Field(3),
),
}
}
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
entproto.Service(),
}
}
关键点说明:
entproto.Message()
注解表示要为该实体生成Protobuf消息entproto.Service()
注解表示要生成gRPC服务- 每个字段都需要通过
entproto.Field()
指定Protobuf字段编号
代码生成
- 修改
ent/generate.go
文件,添加生成指令:
package ent
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
//go:generate go run -mod=mod entgo.io/contrib/entproto/cmd/entproto -path ./schema
- 执行代码生成:
go generate ./...
这个命令会生成:
- Ent的基础CRUD操作代码
- Protobuf消息定义(.proto文件)
- gRPC服务接口
- gRPC服务实现
实现gRPC服务端
创建服务端主程序cmd/server/main.go
:
package main
import (
"context"
"log"
"net"
_ "github.com/mattn/go-sqlite3"
"ent-grpc-demo/ent"
"ent-grpc-demo/ent/proto/entpb"
"google.golang.org/grpc"
)
func main() {
// 初始化Ent客户端
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("数据库连接失败: %v", err)
}
defer client.Close()
// 执行数据库迁移
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("数据库迁移失败: %v", err)
}
// 创建gRPC服务实例
svc := entpb.NewUserService(client)
// 创建gRPC服务器
server := grpc.NewServer()
entpb.RegisterUserServiceServer(server, svc)
// 监听5000端口
lis, err := net.Listen("tcp", ":5000")
if err != nil {
log.Fatalf("监听失败: %s", err)
}
// 启动服务
log.Println("gRPC服务启动,监听端口5000...")
if err := server.Serve(lis); err != nil {
log.Fatalf("服务异常终止: %s", err)
}
}
创建gRPC客户端
创建客户端程序cmd/client/main.go
测试服务:
package main
import (
"context"
"fmt"
"log"
"math/rand"
"time"
"ent-grpc-demo/ent/proto/entpb"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
)
func main() {
rand.Seed(time.Now().UnixNano())
// 连接gRPC服务端
conn, err := grpc.Dial(":5000", grpc.WithInsecure())
if err != nil {
log.Fatalf("连接服务端失败: %s", err)
}
defer conn.Close()
// 创建User服务客户端
client := entpb.NewUserServiceClient(conn)
ctx := context.Background()
// 创建随机用户
user := &entpb.User{
Name: fmt.Sprintf("用户_%d", rand.Int()),
EmailAddress: fmt.Sprintf("user_%d@example.com", rand.Int()),
}
created, err := client.Create(ctx, &entpb.CreateUserRequest{User: user})
if err != nil {
se, _ := status.FromError(err)
log.Fatalf("创建用户失败: 状态码=%s 消息=%s", se.Code(), se.Message())
}
log.Printf("用户创建成功,ID: %d", created.Id)
// 查询刚创建的用户
get, err := client.Get(ctx, &entpb.GetUserRequest{Id: created.Id})
if err != nil {
se, _ := status.FromError(err)
log.Fatalf("查询用户失败: 状态码=%s 消息=%s", se.Code(), se.Message())
}
log.Printf("查询到用户: ID=%d, 名称=%s, 邮箱=%s",
get.Id, get.Name, get.EmailAddress)
}
运行演示
- 启动服务端:
go run cmd/server/main.go
- 在另一个终端运行客户端:
go run cmd/client/main.go
你将看到类似以下输出,表示gRPC调用成功:
用户创建成功,ID: 1
查询到用户: ID=1, 名称=用户_123456, 邮箱=user_123456@example.com
当前限制
需要注意的是,当前版本的entproto还存在一些限制:
- 仅支持唯一性边关系(O2O, O2M)
- 创建/更新操作会设置所有字段,不考虑零值
- 缺少列表查询等常用方法
总结
通过Ent框架的entproto扩展,我们可以快速将数据模型转换为gRPC服务,极大地提升了开发效率。这种模式特别适合需要快速构建微服务原型的场景。随着entproto功能的不断完善,它将成为构建gRPC服务的强大工具。
对于生产环境使用,建议结合Ent的隐私策略和钩子机制,实现更精细的访问控制和业务逻辑。
ent 项目地址: https://gitcode.com/gh_mirrors/ent4/ent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考