博客中代码地址:https://github.com/farliu/farpc.git
dubbo架构

开始进入主题,本文主要介绍的是服务的注册和发现,也就是图片中的第1,2,3步,既然要实现服务治理,那么我们需要一个统一管理服务东西,也就是注册中心。我们需要选择的注册中心是zookeeper。
这里多说一句,图中的2,3很明显是分两步来处理。如果只是从注册中心拿到provider而已,那为什么要分两步呢?而且我所认识的单词也有限,要是我取名的话我可能会给它取名叫做get、return。那它为什么要叫subscribe、notify呢?这里是为了多个服务的动态发现。当你有一个provider宕机的时候,zookeeper肯定不能给一个宕机的provider给你,那么这时就需要notify了,而不get。我不知道我说清楚没。这里可以等看完本章内容后,再细细思考。

分析实现方案

按照开发惯例,先确定实现方案。要实现服务注册和发现,就相当于管理服务,管理意味着存储,我们可以先确定存储服务的结构。

假如现在有一个服务为Dog.walk(),我们尝试使用Map来管理它,例如Map<service, provider>,当服务端启动时,将自己所有的服务put到这个Map中,consumer要使用某一个服务时,只需从这个Map中get就可以拿到provider对象,而这个provider对象是什么样的数据呢?我们只需要保存consumer连接provider的数据就行了,例如ip+port。事实上,dubbo保存的数据还不止这些,包括超时时间、是否异步调用等。

听起来好像上述完全可以解决我们的服务治理的功能。但是这只是对于单个provider来说。假如每一个服务有多个provider,那么我们需要对Map进行改变一下,例如Map<service, List>。consumer在获得provider的时候,拿到一个list,然后可以根据不同的负载均衡取得具体的provider。

这一切听起来很完美。不管我们接下来的实现方式是使用Map或者zookeeper,又或者是redis,只要是能按照以上存储结构都能实现我们需求。事实上dubbo也是这样干的。

zookeeper介绍

这里简单说一下zookeeper的三个特性,因为我们都要用到。三个特性包含:存储结构、临时节点,监听器,也正因为这三个特性,zookeeper才多了许多玩法。比如可以用来做:统一配置管理、统一命名服务、分布式锁、集群管理。

存储结构

zookeeper的存储结构,zookeeper中管理的是一个类似文件目录的树。它是一层一层目录结构,每一个文件夹都是一个znode,如下

1
2
3
4
5
6
[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper, faregistrys]
[zk: localhost:2181(CONNECTED) 2] ls /faregistrys
[com.ofcoder.farpc.demo.api.IWelcome]
[zk: localhost:2181(CONNECTED) 3] ls /faregistrys/com.ofcoder.farpc.demo.api.IWelcome
[]

zookeeper的根节点路径为/,ls是列出该路径下的所有的节点。上述zookeeper存储的结构用图表示可能更能理解,如下
zk存储结构
这也是为什么zookeeper可以做为分布式锁的原因,因为目录路径只会存在唯一。每一次新建一个节点就相当于获得锁。这个后续会在另外的文章中讲述。

临时节点

其中zookeeper,它区分为临时节点(EPHEMERAL)和永久节点(PERSISTENT)。dubbo使用它来管理provider。

  • 短暂/临时(Ephemeral):当客户端和服务端断开连接后,所创建的Znode(节点)会自动删除
  • 临时顺序编号目录节点(EPHEMERAL_SEQUENTIAL):客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
  • 持久(Persistent):当客户端和服务端断开连接后,所创建的Znode(节点)不会删除
  • 持久化顺序编号目录节点(PERSISTENT_SEQUENTIAL):客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

监听机制

我们已经简单知道了zoKeeper的数据结构了,zooKeeper还配合了监听器才能够做那么多事的。客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。例如:集群管理。

集群管理无非在乎两点:机器的加入和退出。所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都会收到通知:某个兄弟目录被删除。对于机器加入也是一样。

dubbo如何使用zookeeper作为注册中心

了解了zookeeper的特性,我们现在就可以确定如何用它来完成注册中心的功能,这里我们借鉴dubbo。

dubbo在使用zookeeper作为注册中心时。它将每一个服务作为一个节点,服务的provider作为该节点的子节点。这个结构相当于之前所说的Map<service, List>。其provider的节点为临时节点。当provider宕机或者断开zookeeper连接时,该节点也将销毁。

dubbo的consumer,使用zookeeper的监听机制,监听所有服务的节点,当某一个服务下的子节点也更新或者删除时,dubbo的consumer能及时收到信息,并更新provider列表。