如果你开发的是一个小系统,那么完全没有必要应用这种技术,因为,你面临的问题不是如何发现服务。对于那些大型的,需要强大处理能力的系统来说,有一个讨厌的问题,那就是如何有效的管理这些服务。
举一个简单的例子,你写了一个服务,它接收一个user_id,返回这个用户的姓名电话等信息,这看起来很简单,然后你启动这个服务对外提供服务。可是很快,你的服务不堪重负,因为请求太多了,注意,这些请求都是整个大系统内部的其他模块发出来的。一个服务不够用,那我们就起多个服务,用nginx做负载均衡,这样做的确可以,但仍然有问题,那就是添加新的服务和撤掉一个服务时都需要去修改nginx的配置,如果nginx也挂了呢,或者一个nginx也不够用,又新增了一个nginx服务器,这个时候,该如何告知那些发请求的模块呢?
这就是一个复杂系统可能面临的问题,服务需要水平扩展,但扩展的时候需要一种方法告知那些请求的发起者,zookeeper就可以胜任这份工作。
你把zookeeper理解为一棵树,你可以在树上创建节点,在新的节点上仍然可以创建节点,zookeeper有一种节点叫做临时节点,通俗的讲,就是创建这个临时节点的进程如果挂掉了,zookeeper就会把这个节点删掉,服务发现就是要利用这个性质。首先,我们在zookeeper上创建一个名为kwsy的节点,这名字我乱起的,让所有的向服务发送请求的模块都来监视这个节点,一旦这个节点下的子节点发生了变化,比如新增,删除,那么这些监视的模块都会收到消息,这时,他们就知道是不是有新的服务被启动了或者某个服务挂掉了。而每个服务在启动时,都在kwsy节点下创建一个新的节点,顺便自己的一些信息,例如IP地址,端口号,url等信息写入节点中。
先来一段代码来模拟服务的注册过程
#coding=utf-8
from kazoo.client import KazooClient
import time
zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()
def test_ensure():
zk.ensure_path('/my/favorite')
def test_create():
nodename = '/kwsy/service'
index = 1
while index < 10:
zk.create(nodename,'123.56.190.151_{index}'.format(index=index),ephemeral=True,sequence=True)
time.sleep(10)
index += 1
if __name__ == '__main__':
#zk.delete('/kwsy')
test_create()
zk.stop()
创建节点时,指定ephemeral和sequence都是真,前者表示节点时一个临时节点,后者表示是一个有序节点,综合起来就是一个有序临时节点。指定为临时节点,如果服务崩溃了,那么服务启动时注册的节点会被zookeeper·删除,这样,请求模块就会知道。创建节点时需要指定节点的名称,指定sequence为真,zookeeper就会自己给节点起名字,而且是有序的,这样我们就不用担心节点的名称冲突了。
再看模拟请求模块监视的过程
#coding=utf-8
from kazoo.client import KazooClient
import time
zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()
service_lst = []
@zk.ChildrenWatch('/kwsy')
def my_func(children):
global service_lst
service_lst = children
children = zk.get_children('/kwsy',watch=my_func)
for child in children:
service_lst.append(child)
while True:
print service_lst
time.sleep(10)
zk.stop()