Spring Cloud 对 ZooKeeper 的支持只有两件事:服务注册发现(基于 Curator ServiceDiscovery)和配置中心(监听 /config 下的节点)。这篇真起 Spring Boot 应用,看它写了哪些节点、数据长什么样、实例消失与配置变化各要多久。
services
demo-svc
cd71b2da-fbcf-4262-aec8-08914432476a [临时节点]
/services/demo-svc/<uuid> 的数据: {"name":"demo-svc","id":"cd71b2da-fbcf-4262-aec8-08914432476a","address":"<本机局域网IP>","port":7971,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"demo-svc","name":"demo-svc","metadata":{"instance_status":"UP"}},"registrationTimeUTC":1790834215398,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}}
应用自己看到的 /services: ["demo-svc"] | /instances/demo-svc: ["<本机局域网IP>:7971 id=cd71b2da-fbcf-4262-aec8-08914432476a"]节点格式是 Curator 的 ServiceDiscovery 约定,和Dubbo 的 /dubbo、/services 布局不是一回事(Dubbo 3 的应用级注册也用 /services 路径,但 JSON 的 payload 是 Dubbo 自己的类)。IP 已替换成 <本机局域网IP>。
两个应用与 ZooKeeper 之间会话的协商超时(cons 的 to=): ['10000', '20000'] (tickTime=1000 时服务端 maxSessionTimeout = 20000) 7971 看到: ["<本机局域网IP>:7971 id=cd71b2da-fbcf-4262-aec8-08914432476a","<本机局域网IP>:7972 id=7f792f17-8cf2-41ea-a0f7-5846b3309456"] 7972 看到: ["<本机局域网IP>:7971 id=cd71b2da-fbcf-4262-aec8-08914432476a","<本机局域网IP>:7972 id=7f792f17-8cf2-41ea-a0f7-5846b3309456"] kill -9 后:ZooKeeper 里的节点 20.6 s 后消失;7971 的 DiscoveryClient 在 20.6 s 后只剩 1 个实例
原因与服务发现那篇相同:没有主动注销就靠会话过期。需要更快,要缩短 spring.cloud.zookeeper.session-timeout(我没有改它,没有验证改后的效果)。
建之前 /cfg = <unset>
新建节点后 /cfg 变成 v1:27 ms
改成 v2:31 ms
应用运行中再建 /config/application/demo.msg=GLOBAL 后 /cfg = v2 (不能说明优先级:这个全局目录在应用启动时并不存在,可能根本没有被监听)
重启应用后(此时 /config/application/demo.msg=GLOBAL 与 /config/demo-svc/demo.msg=v2 同时存在)/cfg = v2 (优先级:应用专属配置压过全局)
/config 的树:
application
demo.msg
demo-svc
demo.msg推送机制就是配置中心那篇的 watch;Spring 在这之上做了属性源合并。