客户端模板代码

现在,让我们编写客户端的模板代码。这将与编写服务器模板代码非常相似,但我们不需要在 IP 和端口上创建监听器,而是要调用 grpc.Dial 函数并传递连接选项。

同样,我们不会将要连接的地址硬编码。我们将把地址作为参数传递:

args := os.Args[1:]

if len(args) == 0 {
    log.Fatalln("usage: client [IP_ADDR]")
}

addr := args[0]

接下来,我们将创建一个 DialOption 实例,并为了保持模板的通用性,我们将使用 insecure.NewCredentials() 函数与服务器建立不安全的连接。不要担心,稍后我们会讨论如何建立安全连接:

opts := []grpc.DialOption{
    grpc.WithTransportCredentials(insecure.NewCredentials()),
}

最后,我们可以调用 grpc.Dial 函数来创建一个 grpc.ClientConn 对象。这个对象稍后将用于调用 API 接口。最后,由于这是一个连接对象,因此在客户端生命周期结束时,我们需要关闭它:

conn, err := grpc.Dial(addr, opts...)
if err != nil {
    log.Fatalf("did not connect: %v", err)
}
defer func(conn *grpc.ClientConn) {
    if err := conn.Close(); err != nil {
        log.Fatalf("unexpected error: %v", err)
    }
}(conn)

客户端的代码就是这么简单。完整的代码如下(client/main.go):

package main

import (
    "log"
    "os"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    args := os.Args[1:]
    if len(args) == 0 {
        log.Fatalln("usage: client [IP_ADDR]")
    }
    addr := args[0]

    opts := []grpc.DialOption{
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    }

    conn, err := grpc.Dial(addr, opts...)
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer func(conn *grpc.ClientConn) {
        if err := conn.Close(); err != nil {
            log.Fatalf("unexpected error: %v", err)
        }
    }(conn)
}

显然,现在它什么也不做;不过我们可以通过先运行服务器来测试它:

$ go run server/main.go 0.0.0.0:50051

然后,运行我们的客户端:

$ go run client/main.go 0.0.0.0:50051

服务器应该无限期地等待,而客户端应该在终端上没有任何错误地返回。如果是这种情况,那么你就准备好编写一些 gRPC API 端点了。

Bazel

这次,Bazel 的设置不会像服务器那样复杂。主要是因为我们已经有了 deps.bzl 文件,并且可以在客户端中重复使用它。我们需要做的只是使用 Gazelle 生成 BUILD.bazel 文件,然后就完成了:

$ bazel run //:gazelle

现在,我们应该在 client 目录下看到一个 BUILD.bazel 文件。最重要的是,在这个文件中,我们可以看到 Bazel 将 gRPC 链接到了 client_lib。我们应该看到类似这样的内容:

go_library(
    name = "client_lib",
    srcs = ["main.go"],
    deps = [
        "@org_golang_google_grpc//:go_default_library",
        "@org_golang_google_grpc//credentials/insecure",
    ],
    #...
)

现在,我们可以像运行 go run 命令一样运行我们的客户端:

$ bazel run //client:client 0.0.0.0:50051

现在,我们已经有了服务器和客户端。到目前为止,它们什么也不做,但这正是预期的目的。稍后在本书中,我们只需复制它们,就能专注于真正重要的部分——API。在做任何事情之前,我们先快速了解一下服务器和客户端设置中一些最重要的选项。