创建服务器与客户端
获取一个自动生成 gRPC 服务的定义看起来很酷,但是我们仍需要将其注册到某个 gRPC 服务器,此服务器监听相同 TCP 端口的流量并可以响应 RPC 请求。
我们决定不自动生成此部分内容,因为它一般会由一些团队或组织在特定行为中调用,比如写入不同中间件。 这在未来可能会发生变化。目前本部分描述如何创建一个简单的 gRPC 服务器来提供服务代码。
创建服务器
创建名为 cmd/server/main.go 的新文件并写入:
package main
import (
"context"
"log"
"net"
_ "github.com/mattn/go-sqlite3"
"ent-grpc-example/ent"
"ent-grpc-example/ent/proto/entpb"
"google.golang.org/grpc"
)
func main() {
// Initialize an ent client.
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
// Run the migration tool (creating tables, etc).
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
// Initialize the generated User service.
svc := entpb.NewUserService(client)
// Create a new gRPC server (you can wire multiple services to a single server).
server := grpc.NewServer()
// Register the User service with the server.
entpb.RegisterUserServiceServer(server, svc)
// Open port 5000 for listening to traffic.
lis, err := net.Listen("tcp", ":5000")
if err != nil {
log.Fatalf("failed listening: %s", err)
}
// Listen for traffic indefinitely.
if err := server.Serve(lis); err != nil {
log.Fatalf("server ended: %s", err)
}
}
请注意我们添加了 github.com/mattn/go-sqlite3 的导入内容,所以我们必须将其添加到我们的模块:
go get -u github.com/mattn/go-sqlite3
下一步我们运行服务器,同时我们写一个客户端与它进行通讯:
go run -mod=mod ./cmd/server
创建客户端
让我们创建一个简单的客户端向服务器发送一些请求。创建名为 cmd/client/main.go 的新文件并写入:
package main
import (
"context"
"fmt"
"log"
"math/rand"
"time"
"ent-grpc-example/ent/proto/entpb"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Open a connection to the server.
conn, err := grpc.Dial(":5000", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("failed connecting to server: %s", err)
}
defer conn.Close()
// Create a User service Client on the connection.
client := entpb.NewUserServiceClient(conn)
// Ask the server to create a random User.
ctx := context.Background()
user := randomUser()
created, err := client.Create(ctx, &entpb.CreateUserRequest{
User: user,
})
if err != nil {
se, _ := status.FromError(err)
log.Fatalf("failed creating user: status=%s message=%s", se.Code(), se.Message())
}
log.Printf("user created with id: %d", created.Id)
// On a separate RPC invocation, retrieve the user we saved previously.
get, err := client.Get(ctx, &entpb.GetUserRequest{
Id: created.Id,
})
if err != nil {
se, _ := status.FromError(err)
log.Fatalf("failed retrieving user: status=%s message=%s", se.Code(), se.Message())
}
log.Printf("retrieved user with id=%d: %v", get.Id, get)
}
func randomUser() *entpb.User {
return &entpb.User{
Name: fmt.Sprintf("user_%d", rand.Int()),
EmailAddress: fmt.Sprintf("user_%d@example.com", rand.Int()),
}
}
我们的客户端向 5000 端口创建了一个连接,这是我们的服务器监听的端口,紧接着发送一个 Create 请求来创建一个新的用户,
然后发送第二个 Get 请求从数据库获取它。让我们运行客户端代码:
go run ./cmd/client
可以看到输出内容:
2021/03/18 10:42:58 user created with id: 1
2021/03/18 10:42:58 retrieved user with id=1: id:1 name:"user_730811260095307266" email_address:"user_7338662242574055998@example.com"
很好!我们成功创建了一个真实的 gRPC 客户端与真实的 gRPC 服务器进行沟通! 在下一部分我们将会看到 ent/gRPC 集成如何处理更高级的 ent 模式定义。