毕设做网站些什么比较简单/优化网站建设
Dubbo 服务注册机制
Dubbo所支持的注册中心
-
Multicast
组网广播,只可以在局域网内使用,一般只是作为测试
-
Zookeeper (常用)
是Apache Hadoop 的子项目,是一个树形的目录服务
-
Redis
-
Simple
-
基于Dubbo 的服务实现(SimpleRegistryService),不支持集群
Redis注册中心
- 数据结构:如何存储服务的注册与订阅关系
- 状态更新:当服务状态改变,如何及时更新
数据结构:
redis 注册中心使用Map
存储服务发布,订阅等信息:
key:mapkey:valuekey:value...
发布订阅:
/dubbo/{serviceName}/providers{providerUrl1}:{periodTimestamp}{providerUrl2}:{periodTimestamp}...
订阅服务:
/dubbo/{serviceName}/consumers{consumerUrl1}:{periodTimestamp}{consumerUrl2}:{periodTimestamp}...
有效期:
这个value值(periodTimestamp)是一种有效期:
provider和consumer发给redis的数据会有一个当前时间+30秒的时间戳作为有效期。要是provider活consumer还“活着”,就会不断“续命”,每次都是+3# Dubbo 服务注册机制
Dubbo所支持的注册中心
-
Multicast
组网广播,只可以在局域网内使用,一般只是作为测试
-
Zookeeper (常用)
是Apache Hadoop 的子项目,是一个树形的目录服务
-
Redis
-
Simple
-
基于Dubbo 的服务实现(SimpleRegistryService),不支持集群
Redis注册中心
- 数据结构:如何存储服务的注册与订阅关系
- 状态更新:当服务状态改变,如何及时更新
数据结构:
redis 注册中心使用Map
存储服务发布,订阅等信息:
key:mapkey:valuekey:value...
发布订阅:
/dubbo/{serviceName}/providers{providerUrl1}:{periodTimestamp}{providerUrl2}:{periodTimestamp}...
订阅服务:
/dubbo/{serviceName}/consumers{consumerUrl1}:{periodTimestamp}{consumerUrl2}:{periodTimestamp}...
有效期:
这个value值(periodTimestamp)是一种有效期:
provider和consumer发给redis的数据会有一个当前时间+30秒的时间戳作为有效期。要是provider活consumer还“活着”,就会不断“续命”,每次都是+30秒。
相关源码:
在RedisRegistry
这个类中的有个线程专门来干这个活expireExecutor
ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true));
this.expirePeriod = url.getParameter(SESSION_TIMEOUT_KEY, DEFAULT_SESSION_TIMEOUT); //默认是60s
this.expireFuture = expireExecutor.scheduleWithFixedDelay(() -> {try {deferExpired(); // Extend the expiration time} catch (Throwable t) { // Defensive fault tolerancelogger.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);}
}, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS); //定时每30执行一次
DEFAULT_SESSION_TIMEOUT = 60 * 1000;
可以看到有效周期默认是60s, 但是dubbo会给自动在有效期的二分之一的时间就发送一次“续命”
if (jedis.hset(key, url.toFullString(), String.valueOf(System.currentTimeMillis() + expirePeriod)) == 1) {jedis.publish(key, REGISTER);
}
奇怪?不应该是value不应该是+30s吗?
是的,没错。但是你想想每次都是30s执行一次,每次加60s,是不是距离上次的推送的value差值就是30s呢。确实优点绕,但是还是给聪明的我发现了。
发布订阅的流程
Consumer
事件:类似topic
相关代码:
-
RedisRegistry
中的doRegister
方法注册接口消费者信息;-
创建hash表:
key (接口的url
/dubbo/接口名/consumers
): value(完整的url
dubbo://ip:端口名?参数
): expire(有效期expire); -
推送消息
"register"
字符串
-
-
doSubscribe
方法创建了Notifier
线程来调用redis的subscribe方法来阻塞监听事件
jedis.psubscribe(new NotifySub(jedisPool), service); // blocking
-
收到消息时,回调
onMessage
,调用doNotify
进行接收消息 -
停止注册消费者信息
doUnregister
-
停止订阅线程
doUnsubscribe
,redis注册可以不用处理,但是别的注册中心需要;然后进行destroy
Provider
相关代码:
-
RedisRegistry
中的doRegister
方法注册接口提供者信息:-
创建hash表:
key (接口的url
/dubbo/接口名/providers
): value(完整的url
dubbo://ip:端口名?参数
): expire(有效期expire); -
推送消息
"register"
字符串
jedis.hset(key, value, expire); jedis.publish(key, REGISTER);
-
-
RedisRegistry
中的doSubscribe
中创建一个Notifier
线程订阅上面创建的key
if (first) {first = false;doNotify(jedis, service);resetSkip();}jedis.psubscribe(new NotifySub(jedisPool), service + PATH_SEPARATOR + ANY_VALUE); // blocking
第一次的直接调用
doNotify
以后的doNotify
都是在onMessage
方法被调用来处理通知了 -
doUnregister
jedis.hdel(key, value); jedis.publish(key, UNREGISTER);
-
doUnsubscribe
缺点:
每个服务接口都需要创建一个Notifier线程,因此服务接口数量庞大的时候就需要个更多的线程了.
未完:还差zookeeper注册中心分析,还要再把redis的分析再整理下0秒。
相关源码:
在RedisRegistry
这个类中的有个线程专门来干这个活expireExecutor
ScheduledExecutorService expireExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryExpireTimer", true));
this.expirePeriod = url.getParameter(SESSION_TIMEOUT_KEY, DEFAULT_SESSION_TIMEOUT); //默认是60s
this.expireFuture = expireExecutor.scheduleWithFixedDelay(() -> {try {deferExpired(); // Extend the expiration time} catch (Throwable t) { // Defensive fault tolerancelogger.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);}
}, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS); //定时每30执行一次
DEFAULT_SESSION_TIMEOUT = 60 * 1000;
可以看到有效周期默认是60s, 但是dubbo会给自动在有效期的二分之一的时间就发送一次“续命”
if (jedis.hset(key, url.toFullString(), String.valueOf(System.currentTimeMillis() + expirePeriod)) == 1) {jedis.publish(key, REGISTER);
}
奇怪?不应该是value不应该是+30s吗?
是的,没错。但是你想想每次都是30s执行一次,每次加60s,是不是距离上次的推送的value差值就是30s呢。确实优点绕,但是还是给聪明的我发现了。
发布订阅的流程
Consumer
事件:类似topic
相关代码:
-
RedisRegistry
中的doRegister
方法注册接口消费者信息;-
创建hash表:
key (接口的url
/dubbo/接口名/consumers
): value(完整的url
dubbo://ip:端口名?参数
): expire(有效期expire); -
推送消息
"register"
字符串
-
-
doSubscribe
方法创建了Notifier
线程来调用redis的subscribe方法来阻塞监听事件jedis.psubscribe(new NotifySub(jedisPool), service); // blocking
-
收到消息时,回调
onMessage
,调用doNotify
进行接收消息 -
停止注册消费者信息
doUnregister
-
停止订阅线程
doUnsubscribe
,redis注册可以不用处理,但是别的注册中心需要;然后进行destroy
Provider
相关代码:
-
RedisRegistry
中的doRegister
方法注册接口提供者信息:-
创建hash表:
key (接口的url
/dubbo/接口名/providers
): value(完整的url
dubbo://ip:端口名?参数
): expire(有效期expire); -
推送消息
"register"
字符串
jedis.hset(key, value, expire); jedis.publish(key, REGISTER);
-
-
RedisRegistry
中的doSubscribe
中创建一个Notifier
线程订阅上面创建的key
if (first) {first = false;doNotify(jedis, service);resetSkip();}jedis.psubscribe(new NotifySub(jedisPool), service + PATH_SEPARATOR + ANY_VALUE); // blocking
第一次的直接调用
doNotify
以后的doNotify
都是在onMessage
方法被调用来处理通知了 -
doUnregister
jedis.hdel(key, value); jedis.publish(key, UNREGISTER);
-
doUnsubscribe
缺点:
每个服务接口都需要创建一个Notifier线程,因此服务接口数量庞大的时候就需要个更多的线程了.
Zookeeper注册中心
数据结构:
在zookeeper每一个元素都是节点(znode)呈树形结构,每个znode由一个名称标识,用/路径符号标识。
znode的分类:
-
持久节点 - 当创建znode的客户端断开之后,该znode仍然存在
-
临时节点 - 客户端活跃时,znode有效;当客户端断开后,znode自动删除。所以临时节点不允许有子节点
-
顺序节点 - 即可使持久也可以是临时。
会话sessions
注:dubbo 采用的 客户端zkclient
和 curator
当客户端连接到zk,将建立会话并且给客户端分配个会话id.
客户端在特定时间间隔发送心跳保持会话.当会话超时,就判定客户端挂掉,并删除其创建的临时节点
监视watches
客户端向某一个znode设置watches.当znode数据变更了,watches会发送给客户端.这里的watches和redis的sub不同,它不是阻塞的.
但是watches是一次的,通知过一次就没了,还想监听就需要再次wathes.
当连接会话过期时,客户端将与服务器断开连接,相关的watches也将被删除。
流程说明:
Consumer:
服务消费者启动时: 订阅
/dubbo/com.foo.BarService/providers
目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers
目录下写入自己的 URL 地址
Provider:
服务提供者启动时: 向
/dubbo/com.foo.BarService/providers
目录下写入自己的 URL 地址
zookeeper的流程相比redis就简单很多了.
相关源码:
相关类是ZookeeperRegistry
.
流程和redis的流程一样:
doRegister
doSubscribe
doUnregister
doUnsubscribe
Zookeeper比Redis好的地方:
- Zookeeper不会产生"脏数据",Redis当客户端宕机(进程被kill)会导致数据一直在Redis上
- 监听到/dubbo/service/provider的数据,redis都要进行一次大遍历:遍历接口Map看是哪个consumer或provider的消息
- Redis会产生大量的阻塞线程