构建一个 Nex 服务

构建一个旨在通过 Nex 部署的服务,与构建任何其他服务并无不同。我们对您的应用程序或其依赖项没有任何特殊要求,只需满足运行该服务的节点的沙盒化需求即可。Nex 不提供专有的 SDK、自定义构建工具,也没有复杂的依赖关系树。

如果您正在构建“云原生”或符合 12 因素 的单二进制部署服务,那么在 Nex 中运行这些服务应该不会有任何问题。

按照 Nex 的理解,“服务”仅仅是一个长期运行的进程。一个服务需要启动(例如,它有一个 main 函数/入口点),并且需要持续运行,直到宿主环境通知它停止运行。

创建一个 NATS 服务

在此示例中,我们将使用 Go 来创建一个简单的可执行文件。我们将使用 NATS 服务框架 来暴露一个端点,以实现服务发现。

打开您喜欢的编辑器,并编辑一个名为 main.go 的文件,内容如下:

package main

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/nats-io/nats.go"
	services "github.com/nats-io/nats.go/micro"
)

func main() {
	ctx := context.Background()

	natsUrl := os.Getenv("NATS_URL")
	if len(strings.TrimSpace(natsUrl)) == 0 {
		natsUrl = nats.DefaultURL
	}
	fmt.Printf("Echo service using NATS url '%s'\n", natsUrl)
	nc, err := nats.Connect(natsUrl)
	if err != nil {
		panic(err)
	}

	// request handler
	echoHandler := func(req services.Request) {
		req.Respond(req.Data())
	}

	fmt.Println("Starting echo service")

	_, err = services.AddService(nc, services.Config{
		Name:    "EchoService",
		Version: "1.0.0",
		// base handler
		Endpoint: &services.EndpointConfig{
			Subject: "svc.echo",
			Handler: services.HandlerFunc(echoHandler),
		},
	})

	if err != nil {
		panic(err)
	}

	<-ctx.Done()
}

无论您选择如何解决依赖关系(例如,使用 go.mod 文件),您都需要安装 Go 的 NATS 客户端 SDK:

$ go get github.com/nats-io/nats.go

如果您已熟悉了基于 NATS 基础设施的应用程序开发,这段代码应该非常直观。我们首先根据环境变量 NATS_URL 指定的值建立与 NATS 服务器的连接。

接下来,我们创建一个名为 EchoService 的可发现服务,并在 svc.echo 主题上设置一个端点。这意味着我们定义的处理程序 (echoHandler) 将响应该主题上的请求。

运行我们的服务

为确保我们没有隐藏任何技巧,让我们完全不使用 Nex 来运行这个服务。在一个终端中,运行 go run main.go 命令;在另一个终端中,向 svc.echo 主题发送一个请求:

$ nats req svc.echo 'this is a test' 17:10:53 Sending request on "svc.echo" 17:10:53 Received with rtt 567.537µs this is a test

最后,我们还需要确认该服务是否符合 NATS 服务规范的要求:

$ nats service ls ╭──────────────────────────────────────────────────────────────╮ │ All Services │ ├─────────────┬─────────┬────────────────────────┬─────────────┤ │ Name │ Version │ ID │ Description │ ├─────────────┼─────────┼────────────────────────┼─────────────┤ │ EchoService │ 1.0.0 │ 8GGpcCv98xgoGb5VUDRAl8 │ │ ╰─────────────┴─────────┴────────────────────────┴─────────────╯

完美!我们的服务一切正常,而且我们无需 Nex 即可进行测试。这可能看起来是一个细微之处,但 Nex 服务 与部署方式及运行时调度机制 并非紧密耦合,这一点非常强大。

静态编译

虽然通过 go run ... 在本地测试我们的服务非常简单,但为了让我们的服务能够通过 Nex 部署,它必须是一个静态链接的可执行文件。幸运的是,Go 很容易实现这一点。

在包含 main.go 文件的同一目录下,运行以下 Go 命令:

$ CGO_ENABLED=0 go build -tags netgo -ldflags '-extldflags "-static"'

如果一切顺利,您将不会看到任何输出,我们可以验证下您的服务是否为正确的文件类型:

$ file echoservice echoservice: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=XXFUNOXfjIEOepi2cW-o/gnDMAXpM9aha9OAEsvLi/XPLmCOsZsF3NATJ_-Zkt/71yl5VaZNDY-jsIhJkcc, with debug_info, not stripped

除了 Go,许多其他语言同样能够生成静态链接的二进制文件。例如,如果您使用 Rust,只需将目标设置为 x86_64-unknown-linux-muslaarch64-unknown-linux-musl 即可达到相同效果。

有了静态编译后的服务,我们还需要做一件事,才能开始部署,那就是启动一个 Nex 节点进程。