2.1 master-worker架构的znode tree模型
单从语法的角度讲解这四种模式,读者不好理解其实际用途,因此通过在典型的master-workers分布式架构来讲解,在实际中,如何使用这四种znode来实现这种分布式架构。
在传统的master-worker架构中,服务的组成一般如下所示,
其中master也是一个worker,是众多worker中推选出来的一个leader。master负责监控workers的状态、接受任务(tasks),并将任务分配(assign)给worker。所以,在这里展示的master-worker架构涉及到了四个概念:master、worker、task、assign。
在zookeeper中,集群的架构是通过数据模型目录结构中的znode tree来描述的。所以我们需要创建一个合适的目录结构,选择正确的znode模式,来表述这种架构,将master、worker、task、assign都转换为zookeeper中的znode。以下展示一个已经建立好的描述上述描述master-worker架构的znode tree:
其中圆圈和空心矩形表示的是持久节点,其他表示临时节点,矩形框中的内容是临时序列节点的data。
从图中可以看出,/master节点是临时节点;/workers、/tasks、/assign这种固定的目录一般是持久节点,而他们的子节点都是临时序列节点。
下面详细解释为什么要使用这样的目录结构和znode类型来描述master-worker架构。
1、master为什么是临时节点
在master-worker架构中,master只有一个,且是由众多的worker中选举出来的。如果master是持久znode,那么即使master宕机了,在znode tree中,/master节点依然存在。由于worker是根据/master是否存在判断leader是否宕机的,如果是持久节点的话,就算leader宕机了worker也无法判断。如果主节点是临时节点,宕机后,/master znode就会自动删除,此时worker发现/master不存在,就会重新选举出leader,因此/master必须是临时节点。
2、/workers、/tasks、/assign为什么是持久节点
这三个节点其实是为了znode tree的目录结构更加清晰而创建的,因为在实际中,我们可能会有很多个worker,客户端也会提交很多个task,我们将这些worker、task以及任务分配的状态放置与固定的path下,便于查看和管理。因为path下还必须要存放其他的znode,因为临时节点是没有子node的,所以对于这三个目录我们只能使用持久node。
3、/workers、/tasks的子节点为什么是临时节点
对于workers,如果某个worker宕机了,其对应的znode也应该自动删除,否则leader检测到这个worker对应的znode还存在,会认为这个worker还没有宕机。
4、/assign的子znode空心矩形为什么是持久节点
因为这个节点表示的是给分配给某个worker的任务,所有分配给这个worker的task都在这个目录下,所以针对每个worker,我们在assign目录下,设置一个对应的持久目录,然后将分配给这个worker的任务设置为其子node,这样我们就能很容易的统计出,当前worker已经分配了那些task。就算这个worker宕机,也不删除这个node,因为重启之后,我们还是要创建这个目录。
master-worker架构的znode-tree实现
上面的介绍中,我们已经知道master-worker架构的znode tree应该是什么样子的了,我们需要创建对应的目录结构,并且每个znode选择正确的模式。不过,仅仅创建这样的一个znode tree,还不行,我们在之前提到:
1、master要监控workers的状态,因为其master要知道worker是否宕机,如果宕机了,则不能将任务分配给它,同时如果有新的worker存在时,master也要能检测到,可以将task分配给新的worker。
2、workers要监控master的状态,因为如果master如果宕机了,就不能接受和分配任务了,此时应该在workers中重新选举出一个leader。
所以我们在实现znode tree的时候,还要注意设置相应的监控关系。
master应该监控的目录包括:/workers、/tasks、/assign,当设置过监控之后,worker宕机或者新增worker、新的task或者task执行完成,某个任务assign给那个worker,master就都可以知道。
每个worker都应该监控master的状态,如果宕机,则进行新的选举。
下面通过zkCli.sh来实现master-worker架构,在实际开发中,我们都是通过Java API来实现的。在这里我们通过伪分布式的zookeeper集群,来演示这个案例。
所谓Zookeeper的伪分布式搭建,就是在同一台机器上的不同端口,启动多个zookeeper Server实例,并通过多个配置文件来实现Server之间的通信。
新建以下目录和文件:
[root@www zookeeper-3.4.5-cdh5.4.7]# tree $ZOOKEEPER_HOME/pseudo_conf/ /usr/local/zookeeper-3.4.5-cdh5.4.7/pseudo_conf/ ├── zk1 │ ├── data │ │ └── myid │ └── zk1.cfg ├── zk2 │ ├── data │ │ └── myid │ └── zk2.cfg └── zk3 ├── data │ └── myid └── zk3.cfg
创建脚本
mkdir $ZOOKEEPER_HOME/pseudo_conf mkdir $ZOOKEEPER_HOME/pseudo_conf/zk1 mkdir $ZOOKEEPER_HOME/pseudo_conf/zk2 mkdir $ZOOKEEPER_HOME/pseudo_conf/zk3 mkdir $ZOOKEEPER_HOME/pseudo_conf/zk1/data mkdir $ZOOKEEPER_HOME/pseudo_conf/zk2/data mkdir $ZOOKEEPER_HOME/pseudo_conf/zk3/data touch $ZOOKEEPER_HOME/pseudo_conf/zk1/data/myid touch $ZOOKEEPER_HOME/pseudo_conf/zk2/data/myid touch $ZOOKEEPER_HOME/pseudo_conf/zk3/data/myid echo 1 > $ZOOKEEPER_HOME/pseudo_conf/zk1/data/myid echo 2 > $ZOOKEEPER_HOME/pseudo_conf/zk2/data/myid echo 3 > $ZOOKEEPER_HOME/pseudo_conf/zk3/data/myid touch $ZOOKEEPER_HOME/pseudo_conf/zk1/zk1.cfg touch $ZOOKEEPER_HOME/pseudo_conf/zk2/zk2.cfg touch $ZOOKEEPER_HOME/pseudo_conf/zk3/zk3.cfg
zk1.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=./data clientPort=2181 server.1=127.0.0.1:2222:2223 server.2=127.0.0.1:3333:3334 server.3=127.0.0.1:4444:4445
zk2.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=./data clientPort=2182 server.1=127.0.0.1:2222:2223 server.2=127.0.0.1:3333:3334 server.3=127.0.0.1:4444:4445
zk3.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=./data clientPort=2183 server.1=127.0.0.1:2222:2223 server.2=127.0.0.1:3333:3334 server.3=127.0.0.1:4444:4445
启动zookeeper服务:
启动zk1
cd $ZOOKEEPER_HOME/pseudo_conf/zk1
#第一个参数值可以使start/stop 第二个参数指的是配置文件的配置
$ZOOKEEPER_HOME/bin/zkServer.sh start ./zk1.cfg
启动zk2
cd $ZOOKEEPER_HOME/pseudo_conf/zk2
$ZOOKEEPER_HOME/bin/zkServer.sh start ./zk2.cfg
启动zk3
cd $ZOOKEEPER_HOME/pseudo_conf/zk3
$ZOOKEEPER_HOME/bin/zkServer.sh start ./zk3.cfg
启动以后,使用zkCli连接服务:
${ZOOKEEPER_HOME}/bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
登录成功之后,执行
#创建主节点 create -e /master "master1.example.com:2223" #监控临时节点使用stat命令 stat /master true create /workers "" create /tasks "" create /assign "" #监控workers目录 ls /workers true #监控tasks目录 ls /tasks true 创建worker create -e /workers/worker1.example.com "worker1.example.com:2224" create /assign/worker1.example.com "" #监控持久节点使用ls命令 ls /assign/worker1.example.com true create -s /tasks/task- "cmd" create /assign/worker1.example.com/task-0000000000 ""