生成 gRPC 服务
从 ent.Schema 生成 Protobuf 结构体很有用,但我们真正需要的是获得一个能够从真实数据库创建、读取、更新和删除实体的实际服务器。要实现这一点,我们只需更新一行代码!当我们使用 entproto.Service 注解模式时,我们告诉 entproto 代码生成器我们希望生成 gRPC 服务定义,protoc-gen-entgrpc 将读取我们的定义并生成服务实现。编辑 ent/schema/user.go 并修改模式的 Annotations:
ent/schema/user.go
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
entproto.Service(), // <-- 添加此行
}
}
现在重新运行代码生成:
go generate ./...
观察 ent/proto/entpb 中一些有趣的变化:
ent/proto/entpb
├── entpb.pb.go
├── entpb.proto
├── entpb_grpc.pb.go
├── entpb_user_service.go
└── generate.go
首先,entproto 向 entpb.proto 添加了服务定义:
ent/proto/entpb/entpb.proto
service UserService {
rpc Create ( CreateUserRequest ) returns ( User );
rpc Get ( GetUserRequest ) returns ( User );
rpc Update ( UpdateUserRequest ) returns ( User );
rpc Delete ( DeleteUserRequest ) returns ( google.protobuf.Empty );
rpc List ( ListUserRequest ) returns ( ListUserResponse );
rpc BatchCreate ( BatchCreateUsersRequest ) returns ( BatchCreateUsersResponse );
}
此外,还创建了两个新文件。第一个文件 entpb_grpc.pb.go 包含 gRPC 客户端存根和接口定义。如果打开该文件,您会发现其中包含(除其他内容外):
ent/proto/entpb/entpb_grpc.pb.go
// UserServiceClient 是 UserService 服务的客户端 API。
//
// 关于上下文使用及关闭/结束流式 RPC 的语义,请参阅
// https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream。
type UserServiceClient interface {
Create(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*User, error)
Get(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
Update(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error)
Delete(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
List(ctx context.Context, in *ListUserRequest, opts ...grpc.CallOption) (*ListUserResponse, error)
BatchCreate(ctx context.Context, in *BatchCreateUsersRequest, opts ...grpc.CallOption) (*BatchCreateUsersResponse, error)
}
第二个文件 entpub_user_service.go 包含该接口的生成实现。例如,Get 方法的实现:
ent/proto/entpb/entpb_user_service.go
// Get 实现 UserServiceServer.Get
func (svc *UserService) Get(ctx context.Context, req *GetUserRequest) (*User, error) {
var (
err error
get *ent.User
)
id := int(req.GetId())
switch req.GetView() {
case GetUserRequest_VIEW_UNSPECIFIED, GetUserRequest_BASIC:
get, err = svc.client.User.Get(ctx, id)
case GetUserRequest_WITH_EDGE_IDS:
get, err = svc.client.User.Query().
Where(user.ID(id)).
Only(ctx)
default:
return nil, status.Error(codes.InvalidArgument, "invalid argument: unknown view")
}
switch {
case err == nil:
return toProtoUser(get)
case ent.IsNotFound(err):
return nil, status.Errorf(codes.NotFound, "not found: %s", err)
default:
return nil, status.Errorf(codes.Internal, "internal error: %s", err)
}
}
不错!接下来,让我们创建一个能够为我们服务处理请求的 gRPC 服务器。