NATS多种连接Golang实践

2019-09-16 17:54:04 浏览数 (1)

作者 | 陌无崖

转载请联系授权

NATS重新连接

如果因为任何原因断开连接,大多数(如果不是全部)客户端库将重新连接到NATS系统。重新连接逻辑可能因库而异,因此请检查客户端库的文档。

通常,客户端将尝试通过connect调用中提供的URL或NATS系统本身提供的URL连接到它知道的所有服务器。NATS系统将通知客户端可用于重新连接的新端点。该库可能有几个选项来帮助控制重新连接行为。

重新连接期间使用的服务器列表取决于库,但通常由连接功能/选项和NATS系统本身提供的服务器列表构成。此功能允许NATS应用程序和NATS系统本身自我修复并重新配置,无需额外配置或干预。

禁用自动重新连接

代码语言:javascript复制
nc, err := nats.Connect("demo.nats.io", nats.NoReconnect())
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

设置重新连接尝试次数

应用程序可以设置最大重新连接尝试次数 通常,这将限制实际的总尝试次数,但请检查您的库文档。例如,在Java中,如果客户端知道大约3台服务器并且最大重新连接数设置为2,则它将不会尝试所有服务器。另一方面,如果最大值设置为6,它将尝试所有服务器两次,然后再考虑重新连接失败和关闭。

代码语言:javascript复制
// Set max reconnects attempts
nc, err := nats.Connect("demo.nats.io", nats.MaxReconnects(10))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


中止不停的重新连接尝试

尝试一遍又一遍地连接到同一台服务器没有多大意义。为了防止这种颠簸和浪费的重新连接尝试,库提供了等待设置。如果连续多次尝试同一服务器,此设置将暂停重新连接逻辑。在前面的示例中,如果您有3个服务器和6个尝试,则Java库将遍历这三个服务器。如果没有可连接的话,它将再次尝试所有三个。但是,Java客户端不会在每次尝试之间等待,只有在再次尝试同一服务器时才会等待,因此在该示例中,库可能永远不会等待。另一方面,如果您只提供单个服务器URL和6次尝试,则库将在每次尝试之间等待。

代码语言:javascript复制
// Set reconnect interval to 10 seconds
nc, err := nats.Connect("demo.nats.io", nats.ReconnectWait(10*time.Second))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

避免 Thundering Herd反模式

当服务器出现故障时,可能存在一种称为Thundering Herd的反模式,其中所有客户端都尝试立即重新连接,从而产生拒绝服务攻击。为了防止这种情况,大多数NATS客户端库会随机化他们尝试连接的服务器。如果仅使用单个服务器,则此设置无效,但在群集,随机化或随机播放的情况下,将确保没有任何一台服务器承受客户端重新连接尝试的冲击。

但是,如果要禁用随机化过程,以便始终以相同的顺序检查服务器,则可以使用连接选项在大多数库中执行此操作:

代码语言:javascript复制
servers := []string{"nats://127.0.0.1:1222",
	"nats://127.0.0.1:1223",
	"nats://127.0.0.1:1224",
}

nc, err := nats.Connect(strings.Join(servers, ","), nats.DontRandomize())
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

事件监听器

因为重新连接主要在幕后,许多库提供了一个事件监听器,您可以使用它来通知重新连接事件。此事件对于发送大量消息的应用程序尤为重要。

代码语言:javascript复制
// 异步调用连接事件处理程序
//并且连接状态可能已经改变
// /调用回调。
nc, err := nats.Connect("demo.nats.io",
	nats.DisconnectHandler(func(nc *nats.Conn) {
		// handle disconnect event
	}),
	nats.ReconnectHandler(func(nc *nats.Conn) {
		// handle reconnect event
	}))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


重新连接尝试期间缓冲消息

NATS客户端库尽可能地发射并忘记。您正在使用的库中可能包含的功能之一是能够在连接断开时缓冲传出消息。

在短暂的重新连接期间,这些客户端可以允许应用程序发布由于服务器脱机而将缓存在客户端中的消息。然后,库将在重新连接时发送这些消息。达到最大重新连接缓冲区时,客户端将不再发布消息。

请注意,虽然消息似乎已发送到应用程序,但可能永远不会发送消息,因为永远不会重新建立连接。您的应用程序应使用确认等模式来确保交付。

对于支持此功能的客户端,您可以使用字节,消息或两者来配置此缓冲区的大小。

代码语言:javascript复制
// Set reconnect buffer size in bytes (5 MB)
nc, err := nats.Connect("demo.nats.io", nats.ReconnectBufSize(5*1024*1024))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


安全连接

NATS提供了几种形式的安全性,身份验证,授权和隔离。您可以启用限制NATS系统访问权限的身份验证。帐户允许隔离主题空间和应用程序组。授权可用于限制个人用户对特定主题的访问以进行发布和订阅操作。TLS可用于加密客户端和NATS系统之间的所有流量。最后,TLS可用于使用客户端证书验证客户端身份。通过组合所有这些方法,您可以保护对系统和所有消息流的访问。

客户端无法控制访问控制,但客户端确实提供了对系统进行身份验证,绑定到帐户以及要求TLS所需的配置。

使用用户和密码进行身份验证

对于本例,使用以下方法启动服务器:

nats-server --user myname --pass password

您可以使用服务器提供的简单工具加密密码以传递给nats-server:

代码语言:javascript复制
> go run mkpasswd.go -p
> password: password
> bcrypt hash: $2a$11$1oJy/wZYNTxr9jNwMNwS3eUGhBpHT3On8CL9o7ey89mpgo88VG6ba

并在服务器配置中使用散列密码。客户端仍使用纯文本版本。

代码使用localhost:4222,以便您可以在计算机上启动服务器以试用它们。

使用用户/密码连接

使用密码登录时,nats-server将使用纯文本密码或加密密码。

代码语言:javascript复制
// Set a user and plain text password
nc, err := nats.Connect("127.0.0.1", nats.UserInfo("myname", "password"))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

使用URL连接用户/密码

大多数客户端通过在服务器的URL中接受用户名和密码,可以轻松传递用户名和密码。这种标准格式是:

代码语言:javascript复制
nats://user:password@server:port

使用此格式,您可以像使用URL连接一样轻松地使用身份验证连接到服务器:

代码语言:javascript复制
// Set a user and plain text password
nc, err := nats.Connect("myname:password@127.0.0.1")
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


用令牌授权

令牌基本上是随机字符串,很像密码,并且在某些情况下可以提供简单的认证机制。但是,令牌只是秘密安全,因此其他身份验证方案可以在大型安装中提供更高的安全性。

对于此示例,使用以下命令启动服务器:

nats-server --auth mytoken

代码使用localhost:4222,以便您可以在计算机上启动服务器以试用它们。

用令牌(token)连接

代码语言:javascript复制
// Set a token
nc, err := nats.Connect("127.0.0.1", nats.Name("API Token Example"), nats.Token("mytoken"))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


在URL中连接令牌

某些客户端库允许您使用以下格式将令牌作为服务器URL的一部分传递:

nats://token@server:port

同样,一旦构建了此URL,就可以像连接到普通URL一样进行连接

代码语言:javascript复制
// Token in URL
nc, err := nats.Connect("mytoken@localhost")
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

授权使用NKey

2.0版本的NATS服务器引入了新的质询响应身份验证选项。此挑战响应基于我们称为使用Ed25519签名的NKeys的包装器。服务器可以通过多种方式使用这些密钥进行身份验证。最简单的方法是为服务器配置一个已知公钥列表,并让客户端通过使用私钥对其进行签名来响应挑战。此挑战响应通过确保客户端具有私钥来确保安全性,同时还保护私钥免受服务器的影响,而私钥从未实际看到过。

处理质询响应可能需要的不仅仅是连接选项中的设置,具体取决于客户端库。

代码语言:javascript复制
opt, err := nats.NkeyOptionFromSeed("seed.txt")
if err != nil {
	log.Fatal(err)
}
nc, err := nats.Connect("127.0.0.1", opt)
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

使用用户凭据文件进行身份验证

2.0版本的NATS服务器引入了基于JWT的身份验证的思想。客户端使用用户JWT和来自NKey对的私钥与此新方案交互。为了更轻松地与JWT建立连接,客户端库支持凭证文件的概念。此文件包含私钥和JWT,可以使用nsc工具生成。内容将如下所示,应该受到保护,因为它包含私钥。此creds文件未使用,仅用于示例目的。

代码语言:javascript复制
-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJUVlNNTEtTWkJBN01VWDNYQUxNUVQzTjRISUw1UkZGQU9YNUtaUFhEU0oyWlAzNkVMNVJBIiwiaWF0IjoxNTU4MDQ1NTYyLCJpc3MiOiJBQlZTQk0zVTQ1REdZRVVFQ0tYUVM3QkVOSFdHN0tGUVVEUlRFSEFKQVNPUlBWV0JaNEhPSUtDSCIsIm5hbWUiOiJvbWVnYSIsInN1YiI6IlVEWEIyVk1MWFBBU0FKN1pEVEtZTlE3UU9DRldTR0I0Rk9NWVFRMjVIUVdTQUY3WlFKRUJTUVNXIiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.6TQ2ilCDb6m2ZDiJuj_D_OePGXFyN3Ap2DEm3ipcU5AhrWrNvneJryWrpgi_yuVWKo1UoD5s8bxlmwypWVGFAA
------END NATS USER JWT------

************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.

-----BEGIN USER NKEY SEED-----
SUAOY5JZ2WJKVR4UO2KJ2P3SW6FZFNWEOIMAXF4WZEUNVQXXUOKGM55CYE
------END USER NKEY SEED------

*************************************************************

给定一个creds文件,客户端可以作为属于特定帐户的特定用户进行身份验证:

代码语言:javascript复制
nc, err := nats.Connect("127.0.0.1", nats.UserCredentials("path_to_creds_file"))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection

使用TLS加密连接

虽然身份验证限制了哪些客户端可以连接,但TLS可用于检查服务器的身份以及可选的客户端身份,并将加密两者之间的所有流量。使用NATS的最安全的TLS版本是使用经过验证的客户端证书。在此模式下,客户端可以检查它是否信任NATS系统发送的证书,但是单个服务器也将检查它是否信任客户端发送的证书。从应用程序的角度来看,连接到不验证客户端证书的服务器可能看起来相同。在幕后,禁用TLS验证会删除服务器端对客户端证书的检查。在TLS模式下启动时,nats-server将要求所有客户端与TLS连接。此外,如果配置为与TLS连接,客户端库将无法连接到没有TLS的服务器。

Java示例存储库包含用于以TLS模式启动服务器的证书。

代码语言:javascript复制
> nats-server -c /src/main/resources/tls.conf
 or
> nats-server -c /src/main/resources/tls_verify.conf

用TLS连接

使用TLS连接到服务器非常简单。当使用TLS连接到NATS系统时,大多数客户端将自动使用TLS。设置NATS系统以使用TLS主要是设置证书和信任管理器的练习。客户可能还需要其他信息,例如:

代码语言:javascript复制
nc, err := nats.Connect("localhost",
	nats.ClientCert("resources/certs/cert.pem", "resources/certs/key.pem"),
	nats.RootCAs("resources/certs/ca.pem"))
if err != nil {
	log.Fatal(err)
}
defer nc.Close()

// Do something with the connection


以上文章参考翻译自NATS官方文档 https://nats-io.github.io/docs/

0 人点赞