/dubbo/{服务名}/providers {提供者url1}: {有效期} {提供者url2}: {有效期}
/dubbo/{服务名}/consumers {订阅者url1}: {有效期} {订阅者url2}: {有效期}
⼀个注册中⼼存储了多个服务、每个服务对应多个提供者和订阅者。每个服务的提供者的URL 作为接⼝map中的⼀个Key,与之对应的value 就是该提供者的有效期。有效期通常只有30 秒,dubbo通过⼀个维护线程,每隔30更新该时间。
Dubbo使⽤Redis的发布订阅特性实现 提供者与消费者之前数据实时同步其原理如下: Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。 Redis 客户端可以订阅任意数量的频道。 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关 系:
下面演示下这个过程
#创建一个channel,就是key
127.0.0.1:6379> set name lk
OK
#订阅该channel,此时客户端处于阻塞状态,等待接收消息
127.0.0.1:6379> subscribe name
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "name"
3) (integer) 1
#打开一个新的客户端
127.0.0.1:6379> publish name cxylk
(integer) 1
#此时第一个客户端就会收到该信息
1) "message"
2) "name"
3) "cxylk"
在提供者和消费者上线上时,分别会进⾏发布与订阅事件,其主体流程如下:
1.消费端
a. 启动:注册消费者信息
b. 启动:启动⼀个阻塞线程,订阅(subscribe)该接⼝事件
c. 停⽌:删除接⼝消费信息
d. 停⽌:停⽌订阅线程
2.提供端
a. 启动:注册提供者信息
b. 启动:推送(publish) 接⼝注册事件
c. 停⽌:删除接⼝提供者信息
d. 停⽌:推送(publish) 接⼝注销事件
相关源码: org.apache.dubbo.registry.redis.RedisRegistry#doSubscribe// 订阅 org.apache.dubbo.registry.redis.RedisRegistry.Notifier#run// 订阅线程,阻塞读取 org.apache.dubbo.registry.redis.RedisRegistry.NotifySub#onMessage// 变更通知 org.apache.dubbo.registry.redis.RedisRegistry#doRegister // 注册 org.apache.dubbo.registry.redis.RedisRegistry#destroy // 消费
Zookeper是⼀个树型的⽬录服务,本身⽀持变更推送相⽐redis的实现Publish/Subscribe 功能更稳定。
zookeeper 存储结构是采⽤树级⽬录形式,其层级分别是 dubbo\接⼝名\提供者、消费者\URL. 都是以节点名称的形式存储。其中的URL作为临时节点 存在,当应⽤与Zokkepper会话断开后, 其会被⾃动删除。
启动两个服务,通过prettyzoo可视化界面可以看到当前zk的节点信息
第一个提供者:
第二个提供者:
通过ephermeralOwner可以看出这两个节点是临时节点,key就是该服务的url,key就是url,通过解码可以得到该服务的各种信息,而value就是ip。
另外,临时节点下面是不能有节点的。
进入zk客户端zkCli.sh。
查看当前节点状态
[zk: localhost:2181(CONNECTED) 1] ls /
[dubbo, zookeeper]
[zk: localhost:2181(CONNECTED) 2]
创建一个非临时节点
[zk: localhost:2181(CONNECTED) 2] create /names 1
Created /names
[zk: localhost:2181(CONNECTED) 3] ls /
[dubbo, names, zookeeper]
#查询并开启监听模式
[zk: localhost:2181(CONNECTED) 4] ls /names watch
'ls path [watch]' has been deprecated. Please use 'ls [-w] path' instead.
[]
[zk: localhost:2181(CONNECTED) 5]
打开一个新客户端,创建临时节点
#-e表示临时节点
[zk: localhost:2181(CONNECTED) 2] create -e /names/lk 11
Created /names/lk
此时第一个客户端状态
[zk: localhost:2181(CONNECTED) 5]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/names
#按下enter键,并不会阻塞
[zk: localhost:2181(CONNECTED) 5]
此时在创建一个临时节点
[zk: localhost:2181(CONNECTED) 3] create -e /names/lk1 111
Created /names/lk1
就不会再监听了,需要重新watch
dubbo正是利用zk的监听机制,去监听/dubbo/{接口名}/providers的子节点信息,从而达到订阅发布的目的。流程如下
1.消费端
a.启动:注册消费者信息(创建临时节点)
b.启动:订阅提供者信息(添加providers子节点监听事件)
c.触发订阅:更新提供者列表,重新订阅
d.停止:注销消费者信息(删除临时节点),取消订阅
2.提供端
a.启动:注册提供者信息(创建临时节点)
b.停止:注销提供者信息(删除临时节点)
1.不会产生脏数据
2.redis需要每隔30s进行一次大查询
3.redis每个服务都需要一个线程来监听数据(阻塞)
dubbo更推荐使用zk作为注册中心
相关源码
org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doRegister// 注册 org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doUnregister// 注销 org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe// 订阅 org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doUnregister // 取消订阅 注:doRegister注册包括消费者和提供者的信息。
完整代码:github,码云