本文摘自网络,作者,侵删。
使用 docker 创建的三个 Zookeeper 服务端组成的集群,其 ip 地址分别为:
- 172.17.0.2
- 172.17.0.3
- 172.17.0.4
一、增删改查
1 增 / create
创建新节点一共有四种:
- 持久节点
- 临时节点
- 持久时序节点
- 临时时序节点
代码:
package main
import (
...
"github.com/go-zookeeper/zk"
)
func main() {
conn, _, err := zk.Connect([]string{"172.17.0.2", "172.17.0.3", "172.17.0.4"}, time.Second)
if err != nil {
panic(err)
}
defer conn.Close()
// 创建持久节点
path, err := conn.Create("/hello", []byte("world"), 0, zk.WorldACL(zk.PermAll))
if err != nil {
panic(err)
}
println("Created", path)
// 创建临时节点,创建此节点的会话结束后立即清除此节点
ephemeral, err := conn.Create("/ephemeral", []byte("1"), zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
if err != nil {
panic(err)
}
println("Ephemeral node created:", ephemeral)
// 创建持久时序节点
sequence, err := conn.Create("/sequence", []byte("1"), zk.FlagSequence, zk.WorldACL(zk.PermAll))
if err != nil {
panic(err)
}
println("Sequence node created:", sequence)
// 创建临时时序节点,创建此节点的会话结束后立即清除此节点
ephemeralSequence, err := conn.Create("/ephemeralSequence", []byte("1"), zk.FlagEphemeral|zk.FlagSequence, zk.WorldACL(zk.PermAll))
if err != nil {
panic(err)
}
println("Ephemeral-Sequence node created:", ephemeralSequence)
}
2 查 / get
增完了节点,接下来当然是查看一下节点信息:
package main
import (
...
"github.com/go-zookeeper/zk"
)
func main() {
...
result, state, err := conn.Get("/hello")
if err != nil {
panic(err)
}
fmt.Println("result: ", string(result))
fmt.Println("state ->")
fmt.Printf("cZxid=%d\nctime=%d\nmZxid=%d\nmtime=%d\npZxid=%d\ncversion=%d\ndataVersion=%d\naclVersion=%d\nephemeralOwner=%v\ndataLength=%d\nnumChildren=%d\n", state.Czxid, state.Ctime, state.Mzxid, state.Mtime, state.Pzxid, state.Cversion, state.Version, state.Aversion, state.EphemeralOwner, state.DataLength, state.NumChildren)
}
结果:
result: world
state ->
cZxid=4294967345
ctime=1618569545037
mZxid=4294967345
mtime=1618569545037
pZxid=4294967345
cversion=0
dataVersion=0
aclVersion=0
ephemeralOwner=0
dataLength=5
numChildren=0
3 改 / set
代码:
package main
import (
...
"github.com/go-zookeeper/zk"
)
func main() {
...
path := "/hello"
_, state, _ := conn.Get(path)
state, err = conn.Set(path, []byte("girl"), state.Version)
if err != nil {
panic(err)
}
fmt.Println("state ->")
fmt.Printf("cZxid=%d\nctime=%d\nmZxid=%d\nmtime=%d\npZxid=%d\ncversion=%d\ndataVersion=%d\naclVersion=%d\nephemeralOwner=%v\ndataLength=%d\nnumChildren=%d\n", state.Czxid, state.Ctime, state.Mzxid, state.Mtime, state.Pzxid, state.Cversion, state.Version, state.Aversion, state.EphemeralOwner, state.DataLength, state.NumChildren)
data, _, err := conn.Get(path)
if err != nil {
panic(err)
}
fmt.Println("\nnew value: ", string(data))
}
结果:
state ->
cZxid=4294967345
ctime=1618569545037
mZxid=4294967372
mtime=1618575729297
pZxid=4294967345
cversion=0
dataVersion=1
aclVersion=0
ephemeralOwner=0
dataLength=4
numChildren=0
new value: girl
4 删 / delete
代码:
package main
import (
...
"github.com/go-zookeeper/zk"
)
func main() {
...
path := "/hello"
exists, state, err := conn.Exists(path)
fmt.Printf("\npath[%s] exists: %v\n", path, exists)
err = conn.Delete(path, state.Version)
if err != nil {
panic(err)
}
fmt.Printf("path[%s] is deleted.", path)
exists, _, err = conn.Exists(path)
fmt.Printf("\npath[%s] exists: %v\n", path, exists)
}
结果:
path[/hello] exists: true
path[/hello] is deleted.
path[/hello] exists: false
二、权限 / ACL
zk 的节点有 5 种权限:CREATE、READ、WRITE、DELETE 和 ADMIN。
ACL 权限由 scheme:id:permissions
组成。
scheme
有 4 种方式:
- world
- auth
- digest
- ip
下面对这 4 种方式都测试一遍。
1 world
默认方式,相当于全世界都能访问。
将/test
节点的权限修改为 crwa
后尝试删除其子节点 /1
:
func main() {
...
// get acl
acl, state, err := conn.GetACL("/test")
if err != nil {
panic(err)
}
fmt.Println("\nget acl:")
fmt.Println("scheme =", acl[0].Scheme)
fmt.Println("id =", acl[0].ID)
fmt.Println("permissions =", acl[0].Perms)
// set acl
perms := zk.PermCreate | zk.PermRead | zk.PermWrite | zk.PermAdmin // crwa 权限
state, err = conn.SetACL("/test", zk.WorldACL(int32(perms)), state.Version)
if err != nil {
panic(err)
}
fmt.Println("SetAcl successful.")
// create child node
_, err = conn.Create("/test/1", []byte("1"), 0, zk.WorldACL(zk.PermAll))
if err != nil {
panic(err)
}
// get child node
_, state, err = conn.Get("/test/1")
if err != nil {
panic(err)
}
// delete child node /1
err = conn.Delete("/test/1", state.Version)
if err != nil {
fmt.Println("delete failed: ", err.Error())
os.Exit(1)
}
}
结果:
get acl:
scheme = world
id = anyone
permissions = 31
SetAcl successful.
delete failed: zk: not authenticated
exit status 1
可以看见,因为权限问题无法删除子节点/1
,即使子节点/1
赋予的是全部权限。
第 1 小节除了测试 world 外,还对 ACL 权限的设置方法进行了初步的探索。
2 auth
auth 用来授予用户权限,所以需要先创建用户。
为不存在的用户授权
func main() {
...
_, state, err := conn.Get("/test")
if err != nil {
panic(err)
}
acl := zk.ACL{
Perms: 31, // cdrwa
Scheme: "auth",
ID: "user:123456", // 不存在的用户
}
// 为不存在的用户授权
_, err = conn.SetACL("/test", []zk.ACL{acl}, state.Version)
if err != nil {
panic(err)
}
}
$ go run main.go
panic: zk: invalid ACL specified
节点对指定用户授权
func main() {
...
_, state, err := conn.Get("/test")
if err != nil {
panic(err)
}
// 用户授权,用户不存在的话会新建
err = conn.AddAuth("digest", []byte("user1:123456"))
if err != nil {
panic(err)
}
acl := zk.ACL{
Perms: 31, // cdrwa,也可以用zk权限位或计算
Scheme: "auth",
ID: "user1:123456", // 用户名和密码,密码明文或密文皆可
}
// 为用户授权
_, err = conn.SetACL("/test", []zk.ACL{acl}, state.Version)
if err != nil {
panic(err)
}
fmt.Println("节点[/test]已对用户 user1 授权")
}
$ go run main.go
节点[/test]已对用户 user1 授权
授权完成后,需要验证用户认证信息才能进行下一步操作。
使用未授权用户(如 world)访问
func main() {
data, _, err := conn.Get("/test")
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
$ go run main.go
panic: zk: not authenticated
如果使用不正确的用户名和密码,得到的会是同样的用户认证失败的结果。
使用指定用户访问
func main() {
// 每个会话都要验证授权
err = conn.AddAuth("digest", []byte("user1:123456"))
if err != nil {
panic(err)
}
// 查询节点内容
data, _, err := conn.Get("/test")
if err != nil {
panic(err)
}
fmt.Println("节点[/test] 存储的内容:", string(data))
// 查询节点 acl
acl, _, err := conn.GetACL("/test")
if err != nil {
panic(err)
}
fmt.Println("acl 信息:")
fmt.Println("scheme =", acl[0].Scheme)
fmt.Println("id =", acl[0].ID)
fmt.Println("permissions =", acl[0].Perms)
}
}
$ go run main.go
节点[/test] 存储的内容: test
acl 信息:
scheme = digest
id = user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=
permissions = 31
可以看到,在查询节点的 acl 信息时返回的用户密码是加密过的密文。
3 digest
digest
与auth
基本相同,唯一的区别在于设置权限时,密码需要使用密文。
相关阅读 >>
“python太慢了、Golang糟透了、monGodb是最好的”:那些关于软件工程的“宗教”辩论
Go cloud项目开源发布:Go语言将成为云端应用开发的首选语言?
手撸Golang 基本数据结构与算法 图的搜索 深度优先/广度优先
更多相关阅读请进入《Go》频道 >>
Go语言101
一个与时俱进的Go编程知识库。