Zookeeper

Zookeeper

Zookeeper Too many connections情况

大数据/云计算采菊篱下 回复了问题 • 2 人关注 • 1 个回复 • 249 次浏览 • 2016-11-03 12:05 • 来自相关话题

zookeeper客户端连接zookeeper服务器策略是什么?

大数据/云计算采菊篱下 回复了问题 • 2 人关注 • 1 个回复 • 273 次浏览 • 2016-10-11 12:29 • 来自相关话题

Python可用的分布式协调系统(ZooKeeper,Consul, etcd)介绍

大数据/云计算push 发表了文章 • 0 个评论 • 1033 次浏览 • 2016-06-19 22:52 • 来自相关话题

随着大数据时代的到来,分布式是解决大数据问题的一个主要手段,随着越来越多的分布式的服务,如何在分布式的系统中对这些服务做协调变成了一个很棘手的问题。今天我们就来看看如何使用Python,利用开源对分布式服务做协调。
 
在对分布式的应用做协调的时候,主要会碰到以下的应用场景:
[]业务发现(service discovery)[/]
           找到分布式系统中存在那些可用的服务和节点
[]名字服务 (name service)[/]
           通过给定的名字知道到对应的资源
[]配置管理 (configuration management)[/]
           如何在分布式的节点中共享配置文件,保证一致性。
[]故障发现和故障转移 (failure detection and failover)[/]
           当某一个节点出故障的时候,如何检测到并通知其它节点, 或者把想用的服务转移到其它的可用节点
[]领导选举(leader election)[/]
           如何在众多的节点中选举一个领导者,来协调所有的节点
[]分布式的锁 (distributed exclusive lock)[/]
           如何通过锁在分布式的服务中进行同步
[]消息和通知服务 (message queue and notification)[/]
           如何在分布式的服务中传递消息,以通知的形式对事件作出主动的响应
有许多的开源软件试图解决以上的全部或者部分问题,例如ZooKeeper,consul,doozerd等等,我们现在就看看它们是如何做的。

ZooKeeper

 
Zookeeper 是使用最广泛,也是最有名的解决分布式服务的协调问题的开源软件了,它最早和Hadoop一起开发,后来成为了Apache的顶级项目,很多开源的项目都在使用ZooKeeper,例如大名鼎鼎的Kafka。
 
Zookeeper本身是一个分布式的应用,通过对共享的数据的管理来实现对分布式应用的协调。

ZooKeeper使用一个树形目录作为数据模型,这个目录和文件目录类似,目录上的每一个节点被称作ZNodes。




ZooKeeper提供基本的API来操纵和控制Znodes,包括对节点的创建,删除,设置和获取数据,获得子节点等。

除了这些基本的操作,ZooKeeper还提供了一些配方(Recipe),其实就是一些常见的用例,例如锁,两阶段提交,领导选举等等。

ZooKeeper本身是用Java开发的,所以对Java的支持是最自然的。它同时还提供了C语言的绑定。
 
Kazoo 是一个非常成熟的Zookeeper Python客户端,我们这就看看如果使用Python来调用ZooKeeper。(注意,运行以下的例子,需要在本地启动ZooKeeper的服务)
 
基本操作
 
以下的例子现实了对Znode的基本操作,首先要创建一个客户端的连接,并启动客户端。然后我们可以利用该客户端对Znode做增删改,取内容的操作。最后退出客户端。from kazoo.client import KazooClient

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/zk1")

# Create a node with data
zk.create("/test/zk1/node", b"a test value")

# Determine if a node exists
if zk.exists("/test/zk1"):
print "the node exist"

# Print the version of a node and its data
data, stat = zk.get("/test/zk1")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))

# List the children
children = zk.get_children("/test/zk1")
print("There are %s children with names %s" % (len(children), children))

zk.stop()通过对ZNode的操作,我们可以完成一些分布式服务协调的基本需求,包括名字服务,配置服务,分组等等。
 
故障检测(Failure Detection)
在分布式系统中,一个最基本的需求就是当某一个服务出问题的时候,能够通知其它的节点或者某个管理节点。

ZooKeeper提供ephemeral Node的概念,当创建该Node的服务退出或者异常中止的时候,该Node会被删除,所以我们就可以利用这种行为来监控服务运行状态。

以下是worker的代码

from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/failure_detection")

# Create a node with data
zk.create("/test/failure_detection/worker",
value=b"a test value", ephemeral=True)

while True:
print "I am alive!"
time.sleep(3)

zk.stop()以下的monitor 代码,监控worker服务是否运行。
from kazoo.client import KazooClient

import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Determine if a node exists
while True:
if zk.exists("/test/failure_detection/worker"):
print "the worker is alive!"
else:
print "the worker is dead!"
break
time.sleep(3)

zk.stop()
领导选举
Kazoo直接提供了领导选举的API,使用起来非常方便。from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def leader_func():
print "I am the leader {}".format(str(my_id))
while True:
print "{} is working! ".format(str(my_id))
time.sleep(3)

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

election = zk.Election("/electionpath")

# blocks until the election is won, then calls
# leader_func()
election.run(leader_func)

zk.stop()你可以同时运行多个worker,其中一个会获得Leader,当你杀死当前的leader后,会有一个新的leader被选出。
 
分布式锁
锁的概念大家都熟悉,当我们希望某一件事在同一时间只有一个服务在做,或者某一个资源在同一时间只有一个服务能访问,这个时候,我们就需要用到锁。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def work():
print "{} is working! ".format(str(my_id))

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

lock = zk.Lock("/lockpath", str(my_id))

print "I am {}".format(str(my_id))

while True:
with lock:
work()
time.sleep(3)

zk.stop()当你运行多个worker的时候,不同的worker会试图获取同一个锁,然而只有一个worker会工作,其它的worker必须等待获得锁后才能执行。
 
监视
ZooKeeper提供了监视(Watch)的功能,当节点的数据被修改的时候,监控的function会被调用。我们可以利用这一点进行配置文件的同步,发消息,或其他需要通知的功能。
from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

@zk.DataWatch('/path/to/watch')
def my_func(data, stat):
if data:
print "Data is %s" % data
print "Version is %s" % stat.version
else :
print "data is not available"

while True:
time.sleep(10)

zk.stop()除了我们上面列举的内容外,Kazoo还提供了许多其他的功能,例如:计数,租约,队列等等,大家有兴趣可以参考它的文档。
 

Consul

Consul 是用Go开发的分布式服务协调管理的工具,它提供了服务发现,健康检查,Key/Value存储等功能,并且支持跨数据中心的功能。

Consul提供ZooKeeper类似的功能,它的基于HTTP的API可以方便的和各种语言进行绑定。自然Python也在列。

与Zookeeper有所差异的是Consul通过基于Client/Server架构的Agent部署来支持跨Data Center的功能。




Consul在Cluster伤的每一个节点都运行一个Agent,这个Agent可以使Server或者Client模式。Client负责到Server的高效通信,相对为无状态的。 Server负责包括选举领导节点,维护cluster的状态,对所有的查询做响应,跨数据中心的通信等等。
 
KV基本操作
 
类似于Zookeeper,Consul支持对KV的增删查改的操作。import consul

c = consul.Consul()

# set data for key foo
c.kv.put('foo', 'bar')

# poll a key for updates
index = None
while True:
index, data = c.kv.get('foo', index=index)
print data['Value']

c.kv.delete('foo')这里和ZooKeeper对Znode的操作几乎是一样的。
 
服务发现(Service Discovery)和健康检查(Health Check)
 
Consul的另一个主要的功能是用于对分布式的服务做管理,用户可以注册一个服务,同时还提供对服务做健康检测的功能。

首先,用户需要定义一个服务。
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "127.0.0.1",
"port": 8000,
"checks": [
{
"script": "/usr/local/bin/check_redis.py",
"interval": "10s"
}
]
}}其中,服务的名字是必须的,其它的字段可以自选,包括了服务的地址,端口,相应的健康检查的脚本。当用户注册了一个服务后,就可以通过Consul来查询该服务,获得该服务的状态。
 
Consul支持三种Check的模式:
[]调用一个外部脚本(Script),在该模式下,consul定时会调用一个外部脚本,通过脚本的返回内容获得对应服务的健康状态。[/][]调用HTTP,在该模式下,consul定时会调用一个HTTP请求,返回2XX,则为健康;429 (Too many request)是警告。其它均为不健康[/][]主动上报,在该模式下,服务需要主动调用一个consul提供的HTTP PUT请求,上报健康状态。[/]
 
Python API提供对应的接口,大家可以参考 http://python-consul.readthedocs.org/en/latest/
[]Consul.Agent.Service[/][]Consul.Agent.Check[/]
 
Consul的Health Check和Zookeeper的Failure Detection略有不同,ZooKeeper可以利用ephemeral Node来检测服务的状态,Consul的Health Check,通过调用脚本,HTTP或者主动上报的方式检查服务的状态,更为灵活,可以获得等多的信息,但是也需要做更多的工作。
 
故障检测(Failure Detection)
 
Consul提供Session的概念,利用Session可以检查服务是否存活。

对每一个服务我们都可以创建一个session对象,注意这里我们设置了ttl,consul会以ttl的数值为间隔时间,持续的对session的存活做检查。对应的在服务中,我们需要持续的renew session,保证session是合法的。
import consul
import time

c = consul.Consul()

s = c.session.create(name="worker",behavior='delete',ttl=10)

print "session id is {}".format(s)

while True:
c.session.renew(s)
print "I am alive ..."
time.sleep(3)Moniter代码用于监控worker相关联的session的状态,但发现worker session已经不存在了,就做出响应的处理。
import consul
import time

def is_session_exist(name, sessions):
for s in sessions:
if s['Name'] == name:
return True

return False

c = consul.Consul()

while True:
index, sessions = c.session.list()
if is_session_exist('worker', sessions):
print "worker is alive ..."
else:
print 'worker is dead!'
break
time.sleep(3)这里注意,因为是基于ttl(最小10秒)的检测,从业务中断到被检测到,至少有10秒的时延,对应需要实时响应的情景,并不适用。Zookeeper使用ephemeral Node的方式时延相对短一点,但也非实时。
 
领导选举和分布式的锁
 
无论是Consul本身还是Python客户端,都不直接提供Leader Election的功能,但是这篇文档介绍了如何利用Consul的KV存储来实现Leader Election,利用Consul的KV功能,可以很方便的实现领导选举和锁的功能。

当对某一个Key做put操作的时候,可以创建一个session对象,设置一个acquire标志为该 session,这样就获得了一个锁,获得所得客户则是被选举的leader。

代码如下:
import consul
import time

c = consul.Consul()

def request_lead(namespace, session_id):
lock = c.kv.put(leader_namespace,"leader check", acquire=session_id)
return lock

def release_lead(session_id):
c.session.destroy(session_id)

def whois_lead(namespace):
index,value = c.kv.get(namespace)
session = value.get('Session')
if session is None:
print 'No one is leading, maybe in electing'
else:
index, value = c.session.info(session)
print '{} is leading'.format(value['ID'])

def work_non_block():
print "working"

def work_block():
while True:
print "working"
time.sleep(3)

leader_namespace = 'leader/test'

[size=16] initialize leader key/value node[/size]
leader_index, leader_node = c.kv.get(leader_namespace)

if leader_node is None:
c.kv.put(leader_namespace,"a leader test")

while True:
whois_lead(leader_namespace)
session_id = c.session.create(ttl=10)
if request_lead(leader_namespace,session_id):
print "I am now the leader"
work_block()
release_lead(session_id)
else:
print "wait leader elected!"
time.sleep(3)利用同样的机制,可以方便的实现锁,信号量等分布式的同步操作。
 
监视
Consul的Agent提供了Watch的功能,然而Python客户端并没有相应的接口。
 
 

Etcd

Etcd 是另一个用GO开发的分布式协调应用,它提供一个分布式的Key/Value存储来进行共享的配置管理和服务发现。
 
同样的etcd使用基于HTTP的API,可以灵活的进行不同语言的绑定,我们用的是这个客户端https://github.com/jplana/python-etcd
 
基本操作
import etcd

client = etcd.Client()
client.write('/nodes/n1', 1)
print client.read('/nodes/n1').valueetcd对节点的操作和ZooKeeper类似,不过etcd不支持ZooKeeper的ephemeral Node的概念,要监控服务的状态似乎比较麻烦。
 
分布式锁
etcd支持分布式锁,以下是一个例子。
import sys

sys.path.append("../../")

import etcd

import uuid
import time

my_id = uuid.uuid4()

def work():
print "I get the lock {}".format(str(my_id))

client = etcd.Client()

lock = etcd.Lock(client, '/customerlock', ttl=60)

with lock as my_lock:
work()
lock.is_locked() # True
lock.renew(60)
lock.is_locked() # False老版本的etcd支持leader election,但是在最新版该功能被deprecated了,参见https://coreos.com/etcd/docs/0.4.7/etcd-modules/
 
其它
 
我们针对分布式协调的功能讨论了三个不同的开源应用,其实还有许多其它的选择,我这里就不一一介绍,大家有兴趣可以访问以下的链接:
[]eureka https://github.com/Netflix/eureka [/]
           Netflix开发的定位服务,应用于fail over和load balance的功能
[]curator http://curator.apache.org/[/]
           基于ZooKeeper的更高层次的封装
[]doozerd https://github.com/ha/doozerd[/]
           基于GO的高可靠,分布式的数据存储,过去两年已经不活跃
[]openreplica http://openreplica.org/[/]
           基于Python开发的,面向对象的接口的分布式应用协调的工具
[]serf http://www.serfdom.io/[/]
           serf提供轻量级的cluster成员管理,故障检测(failure detection)和协调。开发基于GO语言。Consul使用了serf提供的功能
[]noah https://github.com/lusis/Noah[/]
           基于ruby的ZooKeeper实现,过去三年不活跃
[]copy cat https://github.com/kuujo/copycat[/]
            基于日志的分布式协调的框架,使用Java开发
 

总结

ZooKeeper无疑是分布式协调应用的最佳选择,功能全,社区活跃,用户群体很大,对所有典型的用例都有很好的封装,支持不同语言的绑定。缺点是,整个应用比较重,依赖于Java,不支持跨数据中心。

Consul作为使用Go语言开发的分布式协调,对业务发现的管理提供很好的支持,他的HTTP API也能很好的和不同的语言绑定,并支持跨数据中心的应用。缺点是相对较新,适合喜欢尝试新事物的用户。

etcd是一个更轻量级的分布式协调的应用,提供了基本的功能,更适合一些轻量级的应用来使用。
 
参考
如果大家对于分布式系统的协调想要进行更多的了解,可以阅读一下的链接:
http://stackoverflow.com/questions/6047917/zookeeper-alternatives-cluster-coordination-service 

http://txt.fliglio.com/2014/05/encapsulated-services-with-consul-and-confd/ 

http://txt.fliglio.com/2013/12/service-discovery-with-docker-docker-links-and-beyond/ 

http://www.serfdom.io/intro/vs-zookeeper.html 

http://devo.ps/blog/zookeeper-vs-doozer-vs-etcd/ 

https://www.digitalocean.com/community/articles/how-to-set-up-a-serf-cluster-on-several-ubuntu-vps 

http://www.slideshare.net/JyrkiPulliainen/taming-pythons-with-zoo-keeper-ep2013?qid=e1267f58-090d-4147-9909-ec673525e76b&v=qf1&b=&from_search=8 

http://muratbuffalo.blogspot.com/2014/09/paper-summary-tango-distributed-data.html 

https://developer.yahoo.com/blogs/hadoop/apache-zookeeper-making-417.html 

http://www.knewton.com/tech/blog/2014/12/eureka-shouldnt-use-zookeeper-service-discovery 

http://codahale.com/you-cant-sacrifice-partition-tolerance/
原文地址:http://my.oschina.net/taogang/blog/410864 查看全部
随着大数据时代的到来,分布式是解决大数据问题的一个主要手段,随着越来越多的分布式的服务,如何在分布式的系统中对这些服务做协调变成了一个很棘手的问题。今天我们就来看看如何使用Python,利用开源对分布式服务做协调。
 
在对分布式的应用做协调的时候,主要会碰到以下的应用场景:
    []业务发现(service discovery)[/]

           找到分布式系统中存在那些可用的服务和节点
    []名字服务 (name service)[/]

           通过给定的名字知道到对应的资源
    []配置管理 (configuration management)[/]

           如何在分布式的节点中共享配置文件,保证一致性。
    []故障发现和故障转移 (failure detection and failover)[/]

           当某一个节点出故障的时候,如何检测到并通知其它节点, 或者把想用的服务转移到其它的可用节点
    []领导选举(leader election)[/]

           如何在众多的节点中选举一个领导者,来协调所有的节点
    []分布式的锁 (distributed exclusive lock)[/]

           如何通过锁在分布式的服务中进行同步
    []消息和通知服务 (message queue and notification)[/]

           如何在分布式的服务中传递消息,以通知的形式对事件作出主动的响应
有许多的开源软件试图解决以上的全部或者部分问题,例如ZooKeeper,consul,doozerd等等,我们现在就看看它们是如何做的。


ZooKeeper


 
Zookeeper 是使用最广泛,也是最有名的解决分布式服务的协调问题的开源软件了,它最早和Hadoop一起开发,后来成为了Apache的顶级项目,很多开源的项目都在使用ZooKeeper,例如大名鼎鼎的Kafka。
 
Zookeeper本身是一个分布式的应用,通过对共享的数据的管理来实现对分布式应用的协调。

ZooKeeper使用一个树形目录作为数据模型,这个目录和文件目录类似,目录上的每一个节点被称作ZNodes。
zk.png

ZooKeeper提供基本的API来操纵和控制Znodes,包括对节点的创建,删除,设置和获取数据,获得子节点等。

除了这些基本的操作,ZooKeeper还提供了一些配方(Recipe),其实就是一些常见的用例,例如锁,两阶段提交,领导选举等等。

ZooKeeper本身是用Java开发的,所以对Java的支持是最自然的。它同时还提供了C语言的绑定。
 
Kazoo 是一个非常成熟的Zookeeper Python客户端,我们这就看看如果使用Python来调用ZooKeeper。(注意,运行以下的例子,需要在本地启动ZooKeeper的服务)
 
基本操作
 
以下的例子现实了对Znode的基本操作,首先要创建一个客户端的连接,并启动客户端。然后我们可以利用该客户端对Znode做增删改,取内容的操作。最后退出客户端。
from kazoo.client import KazooClient

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/zk1")

# Create a node with data
zk.create("/test/zk1/node", b"a test value")

# Determine if a node exists
if zk.exists("/test/zk1"):
print "the node exist"

# Print the version of a node and its data
data, stat = zk.get("/test/zk1")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))

# List the children
children = zk.get_children("/test/zk1")
print("There are %s children with names %s" % (len(children), children))

zk.stop()
通过对ZNode的操作,我们可以完成一些分布式服务协调的基本需求,包括名字服务,配置服务,分组等等。
 
故障检测(Failure Detection)
在分布式系统中,一个最基本的需求就是当某一个服务出问题的时候,能够通知其它的节点或者某个管理节点。

ZooKeeper提供ephemeral Node的概念,当创建该Node的服务退出或者异常中止的时候,该Node会被删除,所以我们就可以利用这种行为来监控服务运行状态。

以下是worker的代码

from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/failure_detection")

# Create a node with data
zk.create("/test/failure_detection/worker",
value=b"a test value", ephemeral=True)

while True:
print "I am alive!"
time.sleep(3)

zk.stop()
以下的monitor 代码,监控worker服务是否运行。
from kazoo.client import KazooClient

import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Determine if a node exists
while True:
if zk.exists("/test/failure_detection/worker"):
print "the worker is alive!"
else:
print "the worker is dead!"
break
time.sleep(3)

zk.stop()

领导选举
Kazoo直接提供了领导选举的API,使用起来非常方便。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def leader_func():
print "I am the leader {}".format(str(my_id))
while True:
print "{} is working! ".format(str(my_id))
time.sleep(3)

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

election = zk.Election("/electionpath")

# blocks until the election is won, then calls
# leader_func()
election.run(leader_func)

zk.stop()
你可以同时运行多个worker,其中一个会获得Leader,当你杀死当前的leader后,会有一个新的leader被选出。
 
分布式锁
锁的概念大家都熟悉,当我们希望某一件事在同一时间只有一个服务在做,或者某一个资源在同一时间只有一个服务能访问,这个时候,我们就需要用到锁。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def work():
print "{} is working! ".format(str(my_id))

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

lock = zk.Lock("/lockpath", str(my_id))

print "I am {}".format(str(my_id))

while True:
with lock:
work()
time.sleep(3)

zk.stop()
当你运行多个worker的时候,不同的worker会试图获取同一个锁,然而只有一个worker会工作,其它的worker必须等待获得锁后才能执行。
 
监视
ZooKeeper提供了监视(Watch)的功能,当节点的数据被修改的时候,监控的function会被调用。我们可以利用这一点进行配置文件的同步,发消息,或其他需要通知的功能。
from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

@zk.DataWatch('/path/to/watch')
def my_func(data, stat):
if data:
print "Data is %s" % data
print "Version is %s" % stat.version
else :
print "data is not available"

while True:
time.sleep(10)

zk.stop()
除了我们上面列举的内容外,Kazoo还提供了许多其他的功能,例如:计数,租约,队列等等,大家有兴趣可以参考它的文档
 


Consul


Consul 是用Go开发的分布式服务协调管理的工具,它提供了服务发现,健康检查,Key/Value存储等功能,并且支持跨数据中心的功能。

Consul提供ZooKeeper类似的功能,它的基于HTTP的API可以方便的和各种语言进行绑定。自然Python也在列。

与Zookeeper有所差异的是Consul通过基于Client/Server架构的Agent部署来支持跨Data Center的功能。
ConsulArch.png

Consul在Cluster伤的每一个节点都运行一个Agent,这个Agent可以使Server或者Client模式。Client负责到Server的高效通信,相对为无状态的。 Server负责包括选举领导节点,维护cluster的状态,对所有的查询做响应,跨数据中心的通信等等。
 
KV基本操作
 
类似于Zookeeper,Consul支持对KV的增删查改的操作。
import consul

c = consul.Consul()

# set data for key foo
c.kv.put('foo', 'bar')

# poll a key for updates
index = None
while True:
index, data = c.kv.get('foo', index=index)
print data['Value']

c.kv.delete('foo')
这里和ZooKeeper对Znode的操作几乎是一样的。
 
服务发现(Service Discovery)和健康检查(Health Check)
 
Consul的另一个主要的功能是用于对分布式的服务做管理,用户可以注册一个服务,同时还提供对服务做健康检测的功能。

首先,用户需要定义一个服务。
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "127.0.0.1",
"port": 8000,
"checks": [
{
"script": "/usr/local/bin/check_redis.py",
"interval": "10s"
}
]
}}
其中,服务的名字是必须的,其它的字段可以自选,包括了服务的地址,端口,相应的健康检查的脚本。当用户注册了一个服务后,就可以通过Consul来查询该服务,获得该服务的状态。
 
Consul支持三种Check的模式:
    []调用一个外部脚本(Script),在该模式下,consul定时会调用一个外部脚本,通过脚本的返回内容获得对应服务的健康状态。[/][]调用HTTP,在该模式下,consul定时会调用一个HTTP请求,返回2XX,则为健康;429 (Too many request)是警告。其它均为不健康[/][]主动上报,在该模式下,服务需要主动调用一个consul提供的HTTP PUT请求,上报健康状态。[/]

 
Python API提供对应的接口,大家可以参考 http://python-consul.readthedocs.org/en/latest/
    []Consul.Agent.Service[/][]Consul.Agent.Check[/]

 
Consul的Health Check和Zookeeper的Failure Detection略有不同,ZooKeeper可以利用ephemeral Node来检测服务的状态,Consul的Health Check,通过调用脚本,HTTP或者主动上报的方式检查服务的状态,更为灵活,可以获得等多的信息,但是也需要做更多的工作。
 
故障检测(Failure Detection)
 
Consul提供Session的概念,利用Session可以检查服务是否存活。

对每一个服务我们都可以创建一个session对象,注意这里我们设置了ttl,consul会以ttl的数值为间隔时间,持续的对session的存活做检查。对应的在服务中,我们需要持续的renew session,保证session是合法的。
import consul
import time

c = consul.Consul()

s = c.session.create(name="worker",behavior='delete',ttl=10)

print "session id is {}".format(s)

while True:
c.session.renew(s)
print "I am alive ..."
time.sleep(3)
Moniter代码用于监控worker相关联的session的状态,但发现worker session已经不存在了,就做出响应的处理。
import consul
import time

def is_session_exist(name, sessions):
for s in sessions:
if s['Name'] == name:
return True

return False

c = consul.Consul()

while True:
index, sessions = c.session.list()
if is_session_exist('worker', sessions):
print "worker is alive ..."
else:
print 'worker is dead!'
break
time.sleep(3)
这里注意,因为是基于ttl(最小10秒)的检测,从业务中断到被检测到,至少有10秒的时延,对应需要实时响应的情景,并不适用。Zookeeper使用ephemeral Node的方式时延相对短一点,但也非实时。
 
领导选举和分布式的锁
 
无论是Consul本身还是Python客户端,都不直接提供Leader Election的功能,但是这篇文档介绍了如何利用Consul的KV存储来实现Leader Election,利用Consul的KV功能,可以很方便的实现领导选举和锁的功能。

当对某一个Key做put操作的时候,可以创建一个session对象,设置一个acquire标志为该 session,这样就获得了一个锁,获得所得客户则是被选举的leader。

代码如下:
import consul
import time

c = consul.Consul()

def request_lead(namespace, session_id):
lock = c.kv.put(leader_namespace,"leader check", acquire=session_id)
return lock

def release_lead(session_id):
c.session.destroy(session_id)

def whois_lead(namespace):
index,value = c.kv.get(namespace)
session = value.get('Session')
if session is None:
print 'No one is leading, maybe in electing'
else:
index, value = c.session.info(session)
print '{} is leading'.format(value['ID'])

def work_non_block():
print "working"

def work_block():
while True:
print "working"
time.sleep(3)

leader_namespace = 'leader/test'

[size=16] initialize leader key/value node[/size]
leader_index, leader_node = c.kv.get(leader_namespace)

if leader_node is None:
c.kv.put(leader_namespace,"a leader test")

while True:
whois_lead(leader_namespace)
session_id = c.session.create(ttl=10)
if request_lead(leader_namespace,session_id):
print "I am now the leader"
work_block()
release_lead(session_id)
else:
print "wait leader elected!"
time.sleep(3)
利用同样的机制,可以方便的实现锁,信号量等分布式的同步操作。
 
监视
Consul的Agent提供了Watch的功能,然而Python客户端并没有相应的接口。
 
 


Etcd


Etcd 是另一个用GO开发的分布式协调应用,它提供一个分布式的Key/Value存储来进行共享的配置管理和服务发现。
 
同样的etcd使用基于HTTP的API,可以灵活的进行不同语言的绑定,我们用的是这个客户端https://github.com/jplana/python-etcd
 
基本操作
import etcd

client = etcd.Client()
client.write('/nodes/n1', 1)
print client.read('/nodes/n1').value
etcd对节点的操作和ZooKeeper类似,不过etcd不支持ZooKeeper的ephemeral Node的概念,要监控服务的状态似乎比较麻烦。
 
分布式锁
etcd支持分布式锁,以下是一个例子。
import sys

sys.path.append("../../")

import etcd

import uuid
import time

my_id = uuid.uuid4()

def work():
print "I get the lock {}".format(str(my_id))

client = etcd.Client()

lock = etcd.Lock(client, '/customerlock', ttl=60)

with lock as my_lock:
work()
lock.is_locked() # True
lock.renew(60)
lock.is_locked() # False
老版本的etcd支持leader election,但是在最新版该功能被deprecated了,参见https://coreos.com/etcd/docs/0.4.7/etcd-modules/
 
其它
 
我们针对分布式协调的功能讨论了三个不同的开源应用,其实还有许多其它的选择,我这里就不一一介绍,大家有兴趣可以访问以下的链接:

           Netflix开发的定位服务,应用于fail over和load balance的功能

           基于ZooKeeper的更高层次的封装

           基于GO的高可靠,分布式的数据存储,过去两年已经不活跃

           基于Python开发的,面向对象的接口的分布式应用协调的工具

           serf提供轻量级的cluster成员管理,故障检测(failure detection)和协调。开发基于GO语言。Consul使用了serf提供的功能

           基于ruby的ZooKeeper实现,过去三年不活跃

            基于日志的分布式协调的框架,使用Java开发
 


总结


ZooKeeper无疑是分布式协调应用的最佳选择,功能全,社区活跃,用户群体很大,对所有典型的用例都有很好的封装,支持不同语言的绑定。缺点是,整个应用比较重,依赖于Java,不支持跨数据中心。

Consul作为使用Go语言开发的分布式协调,对业务发现的管理提供很好的支持,他的HTTP API也能很好的和不同的语言绑定,并支持跨数据中心的应用。缺点是相对较新,适合喜欢尝试新事物的用户。

etcd是一个更轻量级的分布式协调的应用,提供了基本的功能,更适合一些轻量级的应用来使用。
 
参考
如果大家对于分布式系统的协调想要进行更多的了解,可以阅读一下的链接:
http://stackoverflow.com/questions/6047917/zookeeper-alternatives-cluster-coordination-service 

http://txt.fliglio.com/2014/05/encapsulated-services-with-consul-and-confd/ 

http://txt.fliglio.com/2013/12/service-discovery-with-docker-docker-links-and-beyond/ 

http://www.serfdom.io/intro/vs-zookeeper.html 

http://devo.ps/blog/zookeeper-vs-doozer-vs-etcd/ 

https://www.digitalocean.com/community/articles/how-to-set-up-a-serf-cluster-on-several-ubuntu-vps 

http://www.slideshare.net/JyrkiPulliainen/taming-pythons-with-zoo-keeper-ep2013?qid=e1267f58-090d-4147-9909-ec673525e76b&v=qf1&b=&from_search=8 

http://muratbuffalo.blogspot.com/2014/09/paper-summary-tango-distributed-data.html 

https://developer.yahoo.com/blogs/hadoop/apache-zookeeper-making-417.html 

http://www.knewton.com/tech/blog/2014/12/eureka-shouldnt-use-zookeeper-service-discovery 

http://codahale.com/you-cant-sacrifice-partition-tolerance/
原文地址:http://my.oschina.net/taogang/blog/410864

怎么自定义zookeeper的启动内存

大数据/云计算OpenSkill 回复了问题 • 3 人关注 • 2 个回复 • 1020 次浏览 • 2016-04-03 02:54 • 来自相关话题

基于Zookeeper的服务注册与发现案例分析

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 717 次浏览 • 2016-03-16 17:22 • 来自相关话题

背景

大多数系统都是从一个单一系统开始起步的,随着公司业务的快速发展,这个单一系统变得越来越庞大,带来几个问题:
[]随着访问量的不断攀升,纯粹通过提升机器的性能来已经不能解决问题,系统无法进行有效的水平扩展[/][]维护这个单一系统,变得越来越复杂[/][]同时,随着业务场景的不同以及大研发的招兵买马带来了不同技术背景的工程师,在原有达达Python技术栈的基础上,引入了Java技术栈。[/]
如何来解决这些问题?业务服务化是个有效的手段来解决大规模系统的性能瓶颈和复杂性。通过系统拆分将原有的单一庞大系统拆分成小系统,它带来了如下好处:
[]原来系统的压力得到很好的分流,有效地解决了原先系统的瓶颈,同时带来了更好的扩展性[/][]独立的代码库,更少的业务逻辑,系统的维护性得到极大的增强[/]
同时,也带来了一系列问题:
[]随着系统服务的越来越多,如何来管理这些服务?[/][]如何分发请求到提供同一服务的多台主机上(负载均衡如何来做)[/][]如果提供服务的Endpoint发生变化,如何将这些信息通知服务的调用方?[/]

最初的解决方案

Linkedin的创始人里德霍夫曼曾经说过:成立一家初创公司就像把自己从悬崖上扔下来,在降落过程中去组装一架飞机。这对于初创公司达达也是一样的,业务在以火箭般的速度发展着。技术在业务发展中作用就是保障业务的稳定运行,快速地“组装一架飞机”。所以,在业务服务化的早期,我们采用了Nginx+本地hosts文件的方式进行内部服务的注册与发现,架构图如下:




各系统组件角色如下:
[]服务消费者通过本地hosts中的服务提供者域名与Nginx的IP绑定信息来调用服务[/][]Nginx用来对服务提供者提供的服务进行存活检查和负载均衡[/][]服务提供者提供服务给服务消费者访问,并通过Nginx来进行请求分发[/]
这在内部系统比较少,访问量比较小的情况下,解决了服务的注册,发现与负载均衡等问题。但是,随着内部服务越来愈多,访问量越来越大的情况下,该架构的隐患逐渐暴露出来:
[]最明显的问题是Nginx存在单点故障(SPOF),同时随着访问量的提升,会成为一个性能瓶颈[/][]随着内部服务的越来越多,不同的服务消费方需要配置不同的hosts,很容易在增加新的主机时忘记配置hosts导致服务不能调用问题,增加了运维负担[/][]服务的配置信息分散在各个主机hosts中,难以保持一致性,不便于服务的管理[/][]服务主机的发布和下线需要手工的修改Nginx upstream配置,修改的配置需要上线,不利于服务的快速部署[/]

如何解决

在谈如何来解决之前,现梳理一下服务注册与发现的目标:
[]服务的注册信息应该统一保存,方便于服务的管理[/][]自动通过服务的名称去发现服务,而不必了解这个服务提供的end-point到底是哪台主机[/][]支持服务的负载均衡及fail-over[/][]增加或移除某个服务的end-point时,对于服务的消费者来说是透明的[/][]支持Python和Java[/]
备选方案一: DNSDNS作为服务注册发现的一种方案,它比较简单。只要在DNS服务上,配置一个DNS名称与IP对应关系即可。定位一个服务只需要连接到DNS服务器上,随机返回一个IP地址即可。由于存在DNS缓存,所以DNS服务器本身不会成为一个瓶颈。这种基于Pull的方式不能及时获取服务的状态的更新(例如:服务的IP更新等)。如果服务的提供者出现故障,由于DNS缓存的存在,服务的调用方会仍然将请求转发给出现故障的服务提供方;反之亦然。备选方案二:Dubbo
Dubbo是阿里巴巴推出的分布式服务框架,致力于解决服务的注册与发现,编排,治理。
它的优点如下:
[]功能全面,易于扩展[/][]支持各种序列化协议(JSON,Hession,java序列化等)[/][]支持各种RPC协议(HTTP,Java RMI,Dubbo自身的RPC协议等)[/][]支持多种负载均衡算法[/][]其他高级特性:服务编排,服务治理,服务监控等[/]
缺点如下:
[]只支持Java,对于Python没有相应的支持[/][]虽然已经开源,但是没有成熟的社区来运营和维护,未来升级可能是个麻烦[/][]重量级的解决方案带来新的复杂性[/]
备选方案三:Zookeeper
Zookeeper是什么?按照Apache官网的描述是:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.参照官网的定义,它能够做:
[]作为配置信息的存储的中心服务器[/][]命名服务[/][]分布式的协调[/][]Mater选举等[/]
在定义中特别提到了命名服务。在调研之后,Zookeeper作为服务注册与发现的解决方案;
它有如下优点:
[]它提供的简单API[/][]已有互联网公司(例如:Pinterest,Airbnb)使用它来进行服务注册与发现[/][]支持多语言的客户端[/][]通过Watcher机制实现Push模型,服务注册信息的变更能够及时通知服务消费方[/]
缺点是:
[]引入新的Zookeeper组件,带来新的复杂性和运维问题[/][]需自己通过它提供的API来实现服务注册与发现逻辑(包含Python与Java版本)[/]
针对对上述几个方案的优缺点权衡之后,决定采用了基于Zookeeper实现自己的服务注册与发现。

基于Zookeeper的服务注册与发现架构





在此架构中有三类角色:服务提供者,服务注册中心,服务消费者。
服务提供者
服务提供者作为服务的提供方将自身的服务信息注册到服务注册中心中。服务信息包含:
[]隶属于哪个系统[/][]服务的IP,端口[/][]服务的请求URL[/][]服务的权重等等[/]
服务注册中心
服务注册中心主要提供所有服务注册信息的中心存储,同时负责将服务注册信息的更新通知实时的Push给服务消费者(主要是通过Zookeeper的Watcher机制来实现的)。
服务消费者
服务消费者主要职责如下:
[]服务消费者在启动时从服务注册中心获取需要的服务注册信息[/][]将服务注册信息缓存在本地[/][]监听服务注册信息的变更,如接收到服务注册中心的服务变更通知,则在本地缓存中更新服务的注册信息[/][]根据本地缓存中的服务注册信息构建服务调用请求,并根据负载均衡策略(随机负载均衡,Round-Robin负载均衡等)来转发请求[/][]对服务提供方的存活进行检测,如果出现服务不可用的服务提供方,将从本地缓存中剔除[/]
服务消费者只在自己初始化以及服务变更时会依赖服务注册中心,在此阶段的单点故障通过Zookeeper集群来进行保障。在整个服务调用过程中,服务消费者不依赖于任何第三方服务。

实现机制介绍​

Zookeeper数据模型介绍
在整个服务注册与发现的设计中,最重要是如何来存储服务的注册信息。
在设计基于Zookeeper的服务注册结构之前,我们先来看一下Zookeeper的数据模型。Zookeeper的数据模型如下图所示:



Zookeeper数据模型结构与Unix文件系统很类似,是一个树状层次结构。每个节点叫做Znode,节点可以拥有子节点,同时允许将少量数据存储在该节点下。客户端可以通过监听节点的数据变更和子节点变更来实时获取Znode的变更(Wather机制)。服务注册结构




服务注册结构如上图所示。
[]/dada来标示公司名称dada,同时能方便与其它应用的目录区分开(例如:Kafka的brokers注册信息放置在/brokers下)[/][]/dada/services将所有服务提供者都放置该目录下[/][]/dada/services/category1目录定义具体的服务提供者的id:category1,同时该Znode节点中允许存放该服务提供者的一些元数据信息,例如:名称,服务提供者的Owner,上下文路径(Java Web项目),健康检查路径等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld节点定义了服务提供者category1下的一个服务:helloworld。其中helloworld为该服务的ID,同时允许将该服务的元数据信息存储在该Znode下,例如图中标示的:服务名称,服务描述,服务路径,服务的调用的schema,服务的调用的HTTP METHOD等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld/providers节点定义了服务提供者的父节点。在这里其实可以将服务提供者的IP和端口直接放置在helloworld节点下,在这里单独放一个节点,是为了将来可以将服务消费者的消息挂载在helloworld节点下,进行一些扩展,例如命名为:/dada/services/category1/helloworld/consumers。[/][]/dada/services/category__1/helloworld/providers/192.168.1.1:8080该节点定义了服务提供者的IP和端口,同时在节点中定义了该服务提供者的权重。[/]

实现机制

由于目前服务注册通过我们的服务注册中心UI来进行注册,这部分逻辑比较简单,即通过UI界面来构造上述定义的服务注册结构。

下面着重介绍一下我们的服务发现是如何工作的:




在上述类图中,类ServiceDiscovery主要通过Zookeeper的API(Python/Java版本)来获取服务信息,同时对服务注册结构中的每个服务的providers节点增加Watcher,来监控节点变化。获取的服务注册信息保存在变量service_repos中。通过在初始化时设置LoadBalanceStrategy的实现(Round-Robin算法,Radmon算法)来实现服务提供者的负载均衡。主要方法:
[]init获取Zookeeper的服务注册信息,并缓存在service_repos[/][]get_service_repos方法获取实例变量service_repos[/][]get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址[/][]update_service_repos通过Zookeeper的Watcher机制来实时更新本地缓存service_repos[/][]heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,如果出现问题,将该服务提供者从该服务的提供者列表中移除;反之,则加入到服务的提供者列表中[/]
LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
RoundRobinStrategy和RandomStrategy分别实现了Round-Robin和随机的负载均衡算法

未来展望

目前达达基于Zookeeper的服务注册与发现的架构还处于初期,很多功能还未完善,例如:服务的路由功能,与部署平台的集成,服务的监控等等。当然基于Zookeeper还能做其它许多事情,例如:实时动态配置系统。目前,我们已经基于Zookeeper实现了实时动态配置系统。

分享原文地址:https://tech.imdada.cn/2015/12/03/service-registry-and-discovery-with-zk/
作者:杨 骏  达达CTO 查看全部


背景


大多数系统都是从一个单一系统开始起步的,随着公司业务的快速发展,这个单一系统变得越来越庞大,带来几个问题:
    []随着访问量的不断攀升,纯粹通过提升机器的性能来已经不能解决问题,系统无法进行有效的水平扩展[/][]维护这个单一系统,变得越来越复杂[/][]同时,随着业务场景的不同以及大研发的招兵买马带来了不同技术背景的工程师,在原有达达Python技术栈的基础上,引入了Java技术栈。[/]

如何来解决这些问题?业务服务化是个有效的手段来解决大规模系统的性能瓶颈和复杂性。通过系统拆分将原有的单一庞大系统拆分成小系统,它带来了如下好处:
    []原来系统的压力得到很好的分流,有效地解决了原先系统的瓶颈,同时带来了更好的扩展性[/][]独立的代码库,更少的业务逻辑,系统的维护性得到极大的增强[/]

同时,也带来了一系列问题:
    []随着系统服务的越来越多,如何来管理这些服务?[/][]如何分发请求到提供同一服务的多台主机上(负载均衡如何来做)[/][]如果提供服务的Endpoint发生变化,如何将这些信息通知服务的调用方?[/]


最初的解决方案


Linkedin的创始人里德霍夫曼曾经说过:
成立一家初创公司就像把自己从悬崖上扔下来,在降落过程中去组装一架飞机。
这对于初创公司达达也是一样的,业务在以火箭般的速度发展着。技术在业务发展中作用就是保障业务的稳定运行,快速地“组装一架飞机”。所以,在业务服务化的早期,我们采用了Nginx+本地hosts文件的方式进行内部服务的注册与发现,架构图如下:
zknginx.png

各系统组件角色如下:
    []服务消费者通过本地hosts中的服务提供者域名与Nginx的IP绑定信息来调用服务[/][]Nginx用来对服务提供者提供的服务进行存活检查和负载均衡[/][]服务提供者提供服务给服务消费者访问,并通过Nginx来进行请求分发[/]

这在内部系统比较少,访问量比较小的情况下,解决了服务的注册,发现与负载均衡等问题。但是,随着内部服务越来愈多,访问量越来越大的情况下,该架构的隐患逐渐暴露出来:
    []最明显的问题是Nginx存在单点故障(SPOF),同时随着访问量的提升,会成为一个性能瓶颈[/][]随着内部服务的越来越多,不同的服务消费方需要配置不同的hosts,很容易在增加新的主机时忘记配置hosts导致服务不能调用问题,增加了运维负担[/][]服务的配置信息分散在各个主机hosts中,难以保持一致性,不便于服务的管理[/][]服务主机的发布和下线需要手工的修改Nginx upstream配置,修改的配置需要上线,不利于服务的快速部署[/]


如何解决


在谈如何来解决之前,现梳理一下服务注册与发现的目标:
    []服务的注册信息应该统一保存,方便于服务的管理[/][]自动通过服务的名称去发现服务,而不必了解这个服务提供的end-point到底是哪台主机[/][]支持服务的负载均衡及fail-over[/][]增加或移除某个服务的end-point时,对于服务的消费者来说是透明的[/][]支持Python和Java[/]

备选方案一: DNS
DNS作为服务注册发现的一种方案,它比较简单。只要在DNS服务上,配置一个DNS名称与IP对应关系即可。定位一个服务只需要连接到DNS服务器上,随机返回一个IP地址即可。由于存在DNS缓存,所以DNS服务器本身不会成为一个瓶颈。
这种基于Pull的方式不能及时获取服务的状态的更新(例如:服务的IP更新等)。如果服务的提供者出现故障,由于DNS缓存的存在,服务的调用方会仍然将请求转发给出现故障的服务提供方;反之亦然。
备选方案二:Dubbo
Dubbo是阿里巴巴推出的分布式服务框架,致力于解决服务的注册与发现,编排,治理。
它的优点如下:
    []功能全面,易于扩展[/][]支持各种序列化协议(JSON,Hession,java序列化等)[/][]支持各种RPC协议(HTTP,Java RMI,Dubbo自身的RPC协议等)[/][]支持多种负载均衡算法[/][]其他高级特性:服务编排,服务治理,服务监控等[/]

缺点如下:
    []只支持Java,对于Python没有相应的支持[/][]虽然已经开源,但是没有成熟的社区来运营和维护,未来升级可能是个麻烦[/][]重量级的解决方案带来新的复杂性[/]

备选方案三:Zookeeper
Zookeeper是什么?按照Apache官网的描述是:
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
参照官网的定义,它能够做:
    []作为配置信息的存储的中心服务器[/][]命名服务[/][]分布式的协调[/][]Mater选举等[/]

在定义中特别提到了命名服务。在调研之后,Zookeeper作为服务注册与发现的解决方案;
它有如下优点:
    []它提供的简单API[/][]已有互联网公司(例如:Pinterest,Airbnb)使用它来进行服务注册与发现[/][]支持多语言的客户端[/][]通过Watcher机制实现Push模型,服务注册信息的变更能够及时通知服务消费方[/]

缺点是:
    []引入新的Zookeeper组件,带来新的复杂性和运维问题[/][]需自己通过它提供的API来实现服务注册与发现逻辑(包含Python与Java版本)[/]

针对对上述几个方案的优缺点权衡之后,决定采用了基于Zookeeper实现自己的服务注册与发现。


基于Zookeeper的服务注册与发现架构


zkregsine.png

在此架构中有三类角色:服务提供者,服务注册中心,服务消费者。
服务提供者
服务提供者作为服务的提供方将自身的服务信息注册到服务注册中心中。服务信息包含:
    []隶属于哪个系统[/][]服务的IP,端口[/][]服务的请求URL[/][]服务的权重等等[/]

服务注册中心
服务注册中心主要提供所有服务注册信息的中心存储,同时负责将服务注册信息的更新通知实时的Push给服务消费者(主要是通过Zookeeper的Watcher机制来实现的)。
服务消费者
服务消费者主要职责如下:
    []服务消费者在启动时从服务注册中心获取需要的服务注册信息[/][]将服务注册信息缓存在本地[/][]监听服务注册信息的变更,如接收到服务注册中心的服务变更通知,则在本地缓存中更新服务的注册信息[/][]根据本地缓存中的服务注册信息构建服务调用请求,并根据负载均衡策略(随机负载均衡,Round-Robin负载均衡等)来转发请求[/][]对服务提供方的存活进行检测,如果出现服务不可用的服务提供方,将从本地缓存中剔除[/]

服务消费者只在自己初始化以及服务变更时会依赖服务注册中心,在此阶段的单点故障通过Zookeeper集群来进行保障。在整个服务调用过程中,服务消费者不依赖于任何第三方服务。


实现机制介绍​


Zookeeper数据模型介绍
在整个服务注册与发现的设计中,最重要是如何来存储服务的注册信息。
在设计基于Zookeeper的服务注册结构之前,我们先来看一下Zookeeper的数据模型。Zookeeper的数据模型如下图所示:
zluo.png
Zookeeper数据模型结构与Unix文件系统很类似,是一个树状层次结构。每个节点叫做Znode,节点可以拥有子节点,同时允许将少量数据存储在该节点下。客户端可以通过监听节点的数据变更和子节点变更来实时获取Znode的变更(Wather机制)。
服务注册结构
dd.png

服务注册结构如上图所示。
    []/dada来标示公司名称dada,同时能方便与其它应用的目录区分开(例如:Kafka的brokers注册信息放置在/brokers下)[/][]/dada/services将所有服务提供者都放置该目录下[/][]/dada/services/category1目录定义具体的服务提供者的id:category1,同时该Znode节点中允许存放该服务提供者的一些元数据信息,例如:名称,服务提供者的Owner,上下文路径(Java Web项目),健康检查路径等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld节点定义了服务提供者category1下的一个服务:helloworld。其中helloworld为该服务的ID,同时允许将该服务的元数据信息存储在该Znode下,例如图中标示的:服务名称,服务描述,服务路径,服务的调用的schema,服务的调用的HTTP METHOD等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld/providers节点定义了服务提供者的父节点。在这里其实可以将服务提供者的IP和端口直接放置在helloworld节点下,在这里单独放一个节点,是为了将来可以将服务消费者的消息挂载在helloworld节点下,进行一些扩展,例如命名为:/dada/services/category1/helloworld/consumers。[/][]/dada/services/category__1/helloworld/providers/192.168.1.1:8080该节点定义了服务提供者的IP和端口,同时在节点中定义了该服务提供者的权重。[/]


实现机制


由于目前服务注册通过我们的服务注册中心UI来进行注册,这部分逻辑比较简单,即通过UI界面来构造上述定义的服务注册结构。

下面着重介绍一下我们的服务发现是如何工作的:
lj.png

在上述类图中,类ServiceDiscovery主要通过Zookeeper的API(Python/Java版本)来获取服务信息,同时对服务注册结构中的每个服务的providers节点增加Watcher,来监控节点变化。获取的服务注册信息保存在变量service_repos中。通过在初始化时设置LoadBalanceStrategy的实现(Round-Robin算法,Radmon算法)来实现服务提供者的负载均衡。主要方法:
    []init获取Zookeeper的服务注册信息,并缓存在service_repos[/][]get_service_repos方法获取实例变量service_repos[/][]get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址[/][]update_service_repos通过Zookeeper的Watcher机制来实时更新本地缓存service_repos[/][]heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,如果出现问题,将该服务提供者从该服务的提供者列表中移除;反之,则加入到服务的提供者列表中[/]

LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
RoundRobinStrategy和RandomStrategy分别实现了Round-Robin和随机的负载均衡算法


未来展望


目前达达基于Zookeeper的服务注册与发现的架构还处于初期,很多功能还未完善,例如:服务的路由功能,与部署平台的集成,服务的监控等等。
当然基于Zookeeper还能做其它许多事情,例如:实时动态配置系统。目前,我们已经基于Zookeeper实现了实时动态配置系统。


分享原文地址:https://tech.imdada.cn/2015/12/03/service-registry-and-discovery-with-zk/
作者:杨 骏  达达CTO


ZooKeeper架构设计及其应用

开源技术chris 发表了文章 • 0 个评论 • 4065 次浏览 • 2016-03-11 23:45 • 来自相关话题

ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。

总体架构​

ZooKeeper分布式协调服务框架的总体架构,如图所示:



ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点。Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变更同步(广播)到其他的Follower节点。

设计要点

ZooKeeper是基于如下4个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:
[]简单[/]
分布式应用中的各个进程可以通过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:



在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。
[]在上图示例的ZooKeeper的数据模型中,有如下要点:[/]
[]每个节点(ZNode)中存储的是同步相关的数据(这是ZooKeeper设计的初衷,数据量很小,大概B到KB量级),例如状态信息、配置内容、位置信息等。[/][]一个ZNode维护了一个状态结构,该结构包括:版本号、ACL变更、时间戳。每次ZNode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。[/][]每个ZNode都有一个ACL,用来限制是否可以访问该ZNode。[/][]在一个命名空间中,对ZNode上存储的数据执行读和写请求操作都是原子的。[/][]客户端可以在一个ZNode上设置一个监视器(Watch),如果该ZNode数据发生变更,ZooKeeper会通知客户端,从而触发监视器中实现的逻辑的执行。[/][]每个客户端与ZooKeeper连接,便建立了一次会话(Session),会话过程中,可能发生CONNECTING、CONNECTED和CLOSED三种状态。[/][]ZooKeeper支持临时节点(Ephemeral Nodes)的概念,它是与ZooKeeper中的会话(Session)相关的,如果连接断开,则该节点被删除。[/]
 
[]冗余[/]
ZooKeeper被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提高性能,为了避免发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。
[]有序[/]
ZooKeeper使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper可以实现更加高级的抽象操作,如同步等。
[]快速[/]
ZooKeeper包括读写两种操作,基于ZooKeeper的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1),那么读性能更能够体现出高效。

数据模型

ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念,前面我们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。
[]ZNode结构[/]
ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变更,一个Zxid与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode的组成结构,引用文档如下所示:
[]czxid – The zxid of the change that caused this znode to be created.[/][]mzxid – The zxid of the change that last modified this znode.[/][]ctime – The time in milliseconds from epoch when this znode was created.[/][]mtime – The time in milliseconds from epoch when this znode was last modified.[/][]version – The number of changes to the data of this znode.[/][]cversion – The number of changes to the children of this znode.[/][]aversion – The number of changes to the ACL of this znode.[/][]ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.[/][]dataLength – The length of the data field of this znode.[/][]numChildren – The number of children of this znode.[/]
[]Watches(监视)[/]
ZooKeeper中的Watch是只能触发一次。也就是说,如果客户端在指定的ZNode设置了Watch,如果该ZNode数据发生变更,ZooKeeper会发送一个变更通知给客户端,同时触发设置的Watch事件。如果ZNode数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode的Watch,则ZooKeeper就不会发送一个变更通知给客户端。ZooKeeper异步通知设置Watch的客户端。但是ZooKeeper能够保证在ZNode的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode数据的变更,但是看到变更的顺序是能够保证有序一致的。ZNode可以设置两类Watch,一个是Data Watches(该ZNode的数据变更导致触发Watch事件),另一个是Child Watches(该ZNode的孩子节点发生变更导致触发Watch事件)。调用getData()和exists() 方法可以设置Data Watches,调用getChildren()方法可以设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法创建一个ZNode,将触发该ZNode的Data Watches;调用create()方法创建ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,如果该被删除的ZNode还有父节点,则父节点触发一个Child Watches。另外,如果客户端与ZooKeeper Server断开连接,客户端就无法触发Watches,除非再次与ZooKeeper Server建立连接。
[]Sequence Nodes(序列节点)[/]
在创建ZNode的时候,可以请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列:qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007
[]ACLs(访问控制列表)[/]
ACL可以控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的所有孩子节点上。它主要有如下五种权限:
[]CREATE 允许创建Child Nodes[/][]READ 允许获取ZNode的数据,以及该节点的孩子列表[/][]WRITE 可以修改ZNode的数据[/][]DELETE 可以删除一个孩子节点[/][]ADMIN 可以设置权限[/]
ZooKeeper内置了4种方式实现ACL:
[]world 一个单独的ID,表示任何人都可以访问[/][]auth 不使用ID,只有认证的用户可以访问[/][]digest 使用username:password生成MD5哈希值作为认证ID[/][]ip 使用客户端主机IP地址来进行认证[/]
[]ZooKeeper Session[/]
当客户端连接到ZooKeeper集群时,建立了会话。会话过程中的状态变迁,如图所示:



建立连接过程中,会话状态为CONNECTING;当连接建立成功后,会话状态变为CONNECTED。会话过程中,如果正常的话,会话的状态只能是CONNECTING和CONNECTED二者之一。如果在会话过程中连接断开,则变为CLOSED状态。

应用陷阱

并非任何分布式应用都适合使用ZooKeeper来构建协调服务,我们根据ZooKeeper提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。总结如下:
1、丢失ZNode上的变更通知客户端连接到ZooKeeper Server以后,会维护一个TCP连接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。所以,如果使用ZooKeeper的Watch,必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。2、无效ZooKeeper集群节点列表​
与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况:一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active状态,能够提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。另一种情况,客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。所以,我们在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。3、配置导致的性能问题如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性能极大地下降,从而可能会影响应用程序。为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。4、事务日志存储设备性能​ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。5、ZNode存储大量数据导致性能问题​ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

 
参考链接:
http://zookeeper.apache.org/ 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperOver.html 
http://wiki.apache.org/hadoop/ZooKeeper/PoweredBy 
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 
http://zookeeper.apache.org/doc/r3.3.4/recipes.html 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperProgrammers.html 查看全部
ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。


总体架构​


ZooKeeper分布式协调服务框架的总体架构,如图所示:
zk1.png
ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点。
Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。
ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。
当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变更同步(广播)到其他的Follower节点。


设计要点


ZooKeeper是基于如下4个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:
    []简单[/]

分布式应用中的各个进程可以通过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:
zk2.png
在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。

    []在上图示例的ZooKeeper的数据模型中,有如下要点:[/]

    []每个节点(ZNode)中存储的是同步相关的数据(这是ZooKeeper设计的初衷,数据量很小,大概B到KB量级),例如状态信息、配置内容、位置信息等。[/][]一个ZNode维护了一个状态结构,该结构包括:版本号、ACL变更、时间戳。每次ZNode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。[/][]每个ZNode都有一个ACL,用来限制是否可以访问该ZNode。[/][]在一个命名空间中,对ZNode上存储的数据执行读和写请求操作都是原子的。[/][]客户端可以在一个ZNode上设置一个监视器(Watch),如果该ZNode数据发生变更,ZooKeeper会通知客户端,从而触发监视器中实现的逻辑的执行。[/][]每个客户端与ZooKeeper连接,便建立了一次会话(Session),会话过程中,可能发生CONNECTING、CONNECTED和CLOSED三种状态。[/][]ZooKeeper支持临时节点(Ephemeral Nodes)的概念,它是与ZooKeeper中的会话(Session)相关的,如果连接断开,则该节点被删除。[/]

 
    []冗余[/]

ZooKeeper被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提高性能,为了避免发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。

    []有序[/]

ZooKeeper使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper可以实现更加高级的抽象操作,如同步等。

    []快速[/]

ZooKeeper包括读写两种操作,基于ZooKeeper的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1),那么读性能更能够体现出高效。


数据模型


ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念,前面我们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。

    []ZNode结构[/]

ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变更,一个Zxid与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode的组成结构,引用文档如下所示:
    []czxid – The zxid of the change that caused this znode to be created.[/][]mzxid – The zxid of the change that last modified this znode.[/][]ctime – The time in milliseconds from epoch when this znode was created.[/][]mtime – The time in milliseconds from epoch when this znode was last modified.[/][]version – The number of changes to the data of this znode.[/][]cversion – The number of changes to the children of this znode.[/][]aversion – The number of changes to the ACL of this znode.[/][]ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.[/][]dataLength – The length of the data field of this znode.[/][]numChildren – The number of children of this znode.[/]

    []Watches(监视)[/]

ZooKeeper中的Watch是只能触发一次。也就是说,如果客户端在指定的ZNode设置了Watch,如果该ZNode数据发生变更,ZooKeeper会发送一个变更通知给客户端,同时触发设置的Watch事件。如果ZNode数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode的Watch,则ZooKeeper就不会发送一个变更通知给客户端。
ZooKeeper异步通知设置Watch的客户端。但是ZooKeeper能够保证在ZNode的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode数据的变更,但是看到变更的顺序是能够保证有序一致的。
ZNode可以设置两类Watch,一个是Data Watches(该ZNode的数据变更导致触发Watch事件),另一个是Child Watches(该ZNode的孩子节点发生变更导致触发Watch事件)。调用getData()和exists() 方法可以设置Data Watches,调用getChildren()方法可以设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法创建一个ZNode,将触发该ZNode的Data Watches;调用create()方法创建ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,如果该被删除的ZNode还有父节点,则父节点触发一个Child Watches。
另外,如果客户端与ZooKeeper Server断开连接,客户端就无法触发Watches,除非再次与ZooKeeper Server建立连接。

    []Sequence Nodes(序列节点)[/]

在创建ZNode的时候,可以请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列:
qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007

    []ACLs(访问控制列表)[/]

ACL可以控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的所有孩子节点上。它主要有如下五种权限:
    []CREATE 允许创建Child Nodes[/][]READ 允许获取ZNode的数据,以及该节点的孩子列表[/][]WRITE 可以修改ZNode的数据[/][]DELETE 可以删除一个孩子节点[/][]ADMIN 可以设置权限[/]

ZooKeeper内置了4种方式实现ACL:
    []world 一个单独的ID,表示任何人都可以访问[/][]auth 不使用ID,只有认证的用户可以访问[/][]digest 使用username:password生成MD5哈希值作为认证ID[/][]ip 使用客户端主机IP地址来进行认证[/]

    []ZooKeeper Session[/]

当客户端连接到ZooKeeper集群时,建立了会话。会话过程中的状态变迁,如图所示:
zk3.png
建立连接过程中,会话状态为CONNECTING;当连接建立成功后,会话状态变为CONNECTED。会话过程中,如果正常的话,会话的状态只能是CONNECTING和CONNECTED二者之一。如果在会话过程中连接断开,则变为CLOSED状态。


应用陷阱


并非任何分布式应用都适合使用ZooKeeper来构建协调服务,我们根据ZooKeeper提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。总结如下:
1、丢失ZNode上的变更通知
客户端连接到ZooKeeper Server以后,会维护一个TCP连接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。
所以,如果使用ZooKeeper的Watch,必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。
2、无效ZooKeeper集群节点列表​
与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况:
一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active状态,能够提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。
另一种情况,客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。
所以,我们在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。
3、配置导致的性能问题
如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性能极大地下降,从而可能会影响应用程序。
为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。
4、事务日志存储设备性能​
ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。
5、ZNode存储大量数据导致性能问题​
ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。
所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

 
参考链接:
http://zookeeper.apache.org/ 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperOver.html 
http://wiki.apache.org/hadoop/ZooKeeper/PoweredBy 
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 
http://zookeeper.apache.org/doc/r3.3.4/recipes.html 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperProgrammers.html

Zookeeper基本概念详解

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 1526 次浏览 • 2016-03-02 01:31 • 来自相关话题

根据如上思维导图,我们来展开对Zookeeper的基本的一些概念解释。

一、集群角色

LeaderLeader服务器是整个Zookeeper集群工作机制中的核心 FollowerFollower服务器是Zookeeper集群状态的跟随者ObserverObserver服务器充当一个观察者的角色Leader,Follower 设计模式;Observer 观察者设计模式

二、会话

会话是指客户端和ZooKeeper服务器的连接,ZooKeeper中的会话叫Session,客户端靠与服务器建立一个TCP的长连接;来维持一个Session,客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向ZK服务器发送请求并获得响应。

三、数据节点

Zookeeper中的节点有两类:
[]集群中的一台机器称为一个节点[/][]数据模型中的数据单元Znode,分为持久节点和临时节点[/]
 Zookeeper的数据模型是一棵树,树的节点就是Znode,Znode中可以保存信息。如下图所示:



ZK大致数据结构跟上图是一致的,如上图所示这个图就像一棵树,这个树有个根节点,然后其下有些子节点,然后每个子节点其下又可以有子节点,大多数的开发就是跟zk的这些数据节点打交道,来读写这些数据节点,来完成任务。

四、版本

ZK中的版本,是用来记录节点数据或者是节点的子节点列表或者是权限信息的修改次数,注意是这里是修改次数。如果一个节点的version是1,那就代表说这个节点从创建以来被修改了一次,那么这个版本怎么用呢,典型的我们可以利用版本来实现分布式的锁服务。我们知道在数据库中,一般有两种锁,一种是悲观锁一种是乐观锁。


悲观锁悲观锁又叫悲观并发锁,是数据库中一种非常严格的锁策略,具有强烈的排他性,能够避免不同事务对同一数据并发更新造成的数据不一致性,在上一个事务没有完成之前,下一个事务不能访问相同的资源,适合数据更新竞争非常激烈的场景;乐观锁相比悲观锁,乐观锁使用的场景会更多,悲观锁认为事务访问相同数据的时候一定会出现相互的干扰,所以简单粗暴的使用排他访问的方式,而乐观锁认为不同事务访问相同资源是很少出现相互干扰的情况,因此在事务处理期间不需要进行并发控制,当然乐观锁也是锁,它还是会有并发的控制!对于数据库我们通常的做法是在每个表中增加一个version版本字段,事务修改数据之前先读出数据,当然版号也顺势读取出来,然后把这个读取出来的版本号加入到更新语句的条件中,比如,读取出来的版本号是1,我们修改数据的语句可以这样写,update 某某表 set 字段一=某某值 where id=1 and version=1,那如果更新失败了说明以后其他事务已经修改过数据了,那系统需要抛出异常给客户端,让客户端自行处理,客户端可以选择重试。锁,ZK中版本有类式的作用。ZK的版本类型有三种:version cversion aversion





五、Watcher

Watcher我们可以理解为他是一个事件监听器ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化的时候,ZooKeeper服务器会把这个变化的通知发送给感兴趣的客户端。


两个客户端都在zookeeper集群中注册了watcher(事件监听器),那么当zk中的节点数据发生变化的时候,zk会把这一变化的通知发送给客户端,当客户端收到这个变化通知的时候,它可以再回到zk中,去取得这个数据的详细信息。

六、ACL权限控制

 
ACL是Access Control Lists 的简写, ZooKeeper采用ACL策略来进行权限控制,有以下权限:
[]CREATE: 创建子节点的权限[/][]READ: 获取节点数据和子节点列表的权限[/][]WRITE: 更新节点数据的权限[/][]DELETE: 删除子节点的权限[/][]ADMIN: 设置节点ACL的权限[/]
上面的权限有点类似我们信息系统的权限管理,我们在开发系统的时候一般也会对数据做这些权限管理,一个zk集群可能会服务很多的业务,尤其是一些大公司,zk集群的节点中会保存重要的信息,那么这些信息通常只能对一部分的访问者开放,通过acl我们可以对某些节点的访问进行授权,从而来保证数据的安全。 查看全部
zk_logic.png

根据如上思维导图,我们来展开对Zookeeper的基本的一些概念解释。


一、集群角色


Leader
Leader服务器是整个Zookeeper集群工作机制中的核心 
Follower
Follower服务器是Zookeeper集群状态的跟随者
Observer
Observer服务器充当一个观察者的角色
Leader,Follower 设计模式;Observer 观察者设计模式


二、会话


会话是指客户端和ZooKeeper服务器的连接,ZooKeeper中的会话叫Session,客户端靠与服务器建立一个TCP的长连接;
来维持一个Session,客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向ZK服务器发送请求并获得响应。


三、数据节点


Zookeeper中的节点有两类:
    []集群中的一台机器称为一个节点[/][]数据模型中的数据单元Znode,分为持久节点和临时节点[/]

 
Zookeeper的数据模型是一棵树,树的节点就是Znode,Znode中可以保存信息。
如下图所示:
tree.png
ZK大致数据结构跟上图是一致的,如上图所示这个图就像一棵树,这个树有个根节点,然后其下有些子节点,然后每个子节点其下又可以有子节点,大多数的开发就是跟zk的这些数据节点打交道,来读写这些数据节点,来完成任务。


四、版本


ZK中的版本,是用来记录节点数据或者是节点的子节点列表或者是权限信息的修改次数,注意是这里是修改次数。如果一个节点的version是1,那就代表说这个节点从创建以来被修改了一次,那么这个版本怎么用呢,典型的我们可以利用版本来实现分布式的锁服务。我们知道在数据库中,一般有两种锁,一种是悲观锁一种是乐观锁。


悲观锁
悲观锁又叫悲观并发锁,是数据库中一种非常严格的锁策略,具有强烈的排他性,能够避免不同事务对同一数据并发更新造成的数据不一致性,在上一个事务没有完成之前,下一个事务不能访问相同的资源,适合数据更新竞争非常激烈的场景;
乐观锁
相比悲观锁,乐观锁使用的场景会更多,悲观锁认为事务访问相同数据的时候一定会出现相互的干扰,所以简单粗暴的使用排他访问的方式,而乐观锁认为不同事务访问相同资源是很少出现相互干扰的情况,因此在事务处理期间不需要进行并发控制,当然乐观锁也是锁,它还是会有并发的控制!对于数据库我们通常的做法是在每个表中增加一个version版本字段,事务修改数据之前先读出数据,当然版号也顺势读取出来,然后把这个读取出来的版本号加入到更新语句的条件中,比如,读取出来的版本号是1,我们修改数据的语句可以这样写,update 某某表 set 字段一=某某值 where id=1 and version=1,那如果更新失败了说明以后其他事务已经修改过数据了,那系统需要抛出异常给客户端,让客户端自行处理,客户端可以选择重试。
锁,ZK中版本有类式的作用。
ZK的版本类型有三种:version cversion aversion
table.png


五、Watcher


Watcher我们可以理解为他是一个事件监听器
ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化的时候,ZooKeeper服务器会把这个变化的通知发送给感兴趣的客户端。
watcher.png
两个客户端都在zookeeper集群中注册了watcher(事件监听器),那么当zk中的节点数据发生变化的时候,zk会把这一变化的通知发送给客户端,当客户端收到这个变化通知的时候,它可以再回到zk中,去取得这个数据的详细信息。


六、ACL权限控制


 
ACL是Access Control Lists 的简写, ZooKeeper采用ACL策略来进行权限控制,有以下权限:
    []CREATE: 创建子节点的权限[/][]READ: 获取节点数据和子节点列表的权限[/][]WRITE: 更新节点数据的权限[/][]DELETE: 删除子节点的权限[/][]ADMIN: 设置节点ACL的权限[/]

上面的权限有点类似我们信息系统的权限管理,我们在开发系统的时候一般也会对数据做这些权限管理,一个zk集群可能会服务很多的业务,尤其是一些大公司,zk集群的节点中会保存重要的信息,那么这些信息通常只能对一部分的访问者开放,通过acl我们可以对某些节点的访问进行授权,从而来保证数据的安全。

Zookeeper介绍

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 1147 次浏览 • 2016-02-28 18:32 • 来自相关话题

根据如上思维导图,我来展开对Zookeeper的介绍

一、Zookeeper背景

随着互联网技术的高速发展,企业对计算机系统的计算、存储能力要求越来越高,最简单的证明就是出现了一些诸如:高并发,海量存储这样的词汇。在这样的背景下,单纯依靠少量高性能主机来完成计算任务也就不能满足现有大部分企业的需求了,企业的IT架构逐步从集中式向分布式过度,所谓的分布式是指:把一个计算任务分解成若干个计算单元,并且分配到若干不同的计算机中去执行,然后汇总计算结果的过程。这好比公司里面的某个团队,接到公司派发的任务,首先团队的主管,要把任务进行拆分,然后安排下去,划分给团队中不同的人去完成,并随时跟进任务的进展。如果团队主管离职了,那我们可能就会在团队中挑选一个对业务比较熟悉的人来接管主管位置。最后各个组员把任务完成,主管进行汇总,并上报给公司。在团队内部需要制定多个工作流程,来保证工作的有序开展。在分布式系统中同样需要设置这么一个协作规范。zookeeper可以很好的帮助我们来实现这个目的。

二、Zookeeper是什么?

ZooKeeper是一个开放源码的分布式协调服务,由知名互联网公司雅虎创建,是基于Google Chubby开源实现。(Google chubby是google公司开源的一个锁服务。)
ZooKeeper是一个高性能的分布式数据一致性解决方案,它将那些复杂的、容易出错的分布式一致性服务封装起来了,构成了一个高效可靠的源语集,并提供一系列简单易用的接口给用户。ZooKeeper致力于提供一个高性能、高可用、且具有严格的顺序访问控制能力的分布式协调服务。分布式应用可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知,集群管理、Master 选举、分布式锁和分布式队列等功能。Zookeeper知识点:
1、源代码开放开源意味着我们可以免费的获取和使用zk,并且可以深入研究zk的源代码,甚至可以根据自己业务特性和要求进行二次开发修改。
2、是分布式协调服务,它解决分布式数据一致性问题
A、顺序一致性所谓的顺序一致性是指从一个客户端发起一个请求,最终会严格按照发起的顺序应用到zk中。B、原子性原子性是指所有事物请求的处理结果在整个集群的所有机器上的应用情况是一致的。C、单一视图单一视图是指无任客户端连接到哪个zk的服务器,它看到的服务端数据都是一致的。D、可靠性可靠性是指一旦服务端成功的应用了一个事物并完成了对客户端的响应,那么这个事物所引起的服务端状态的变更会一直保留下来,除非有另外一个事物又对它进行了修改。E、实时性实时性是指zk保证在一段时间内客户端一定能从服务端读取最新的数据状态。3、高性能zk具有很高的吞吐量,一个三台服务器的集群可以达到12w-13w的QPS。4、我们可以通过调用zk提供的接口解决一些分布式易用中的实际问题。

三、Zookeeper的典型应用场景

Zookeeper包括但不限于如下应用场景
 
3.1、数据发布/订阅顾名思义就是一方把数据发布出来,另一方通过某种方式可以得到这些数据;通常数据订阅有两种方式:推送模式和拉取模式
推送模式一般是服务器主动向客户端推送信息, 拉取模式是客户端主动去服务器获取数据(通常是采用定时轮询的方式),ZK采用两种方式相结合;发布者将数据发布到ZK集群节点上,订阅者通过一定的方法告诉服务器,我对哪个节点的数据感兴趣,那服务器在这些节点的数据发生变化时,就通知客户端,客户端得到通知后可以去服务器获取数据信息。3.2、负载均衡




实现过程:1、首先DB在启动的时候先把自己在ZK上注册成一个临时节点,ZK的节点后面我们会讲到有两种,一种是永久节点,一类是临时节点临时节点在服务器出现问题的时候,节点会自动的从ZK上删除,那么这样ZK上的服务器列表就是最新的可用的列表。2、客户端在需要读写数据库的时候首先它去ZooKeeper得到所有可用的DB的连接信息(一张列表),得到可用的数据列表。3、客户端随机的算法,随机选择一个与之建立连接,每次会跟不同的数据库连接,就达到简单的复杂均衡。4、当客户端发现连接不可用的时候可再次从ZK上获取可用的DB连接信息,当然也可以在刚获取的那个列表里移除掉不可用的连接后再随机选择一个DB与之连接。3.3、命名服务顾名思义,就是提供名称的服务,例如数据库表格ID,一般用得比较多的有两种ID,一种是自动增长的ID,一种是UUID(9291d71a-0354-4d8e-acd8-64f7393c64ae),两种ID各自都有缺陷,自动增长的ID局限在单库单表中使用,不能在分布式中使用,UUID可以在分布式中使用但是由于ID没有规律难于理解,我们可以借用ZK来生成一个顺序增长的,可以在集群环境下使用的,命名易于理解的ID。3.4、分布式协调/通知心跳检测,在分布式系统中,我们常常需要知道某个机器是否可用,传统的开发中,可以通过Ping某个主机来实现,Ping得通说明对方是可用的,相反是不可用的;
ZK中我们让所有的机其都注册一个临时节点,我们判断一个机器是否可用,我们只需要判断这个节点在ZK中是否存在就可以了,不需要直接去连接需要检查的机器 ,降低系统的复杂度。

四、Zookeeper的优势

[]源代码开放[/][]已经被证实是高性能,易用稳定的工业级产品。[/][]有着广泛的应用:Hadoop,HBase,Storm,Solr。[/]
转载请注明来自开源技术社区 : http://openskill.cn/article/281 查看全部
zookeeper.png

根据如上思维导图,我来展开对Zookeeper的介绍


一、Zookeeper背景


随着互联网技术的高速发展,企业对计算机系统的计算、存储能力要求越来越高,最简单的证明就是出现了一些诸如:高并发,海量存储这样的词汇。在这样的背景下,单纯依靠少量高性能主机来完成计算任务也就不能满足现有大部分企业的需求了,企业的IT架构逐步从集中式向分布式过度,所谓的分布式是指:把一个计算任务分解成若干个计算单元,并且分配到若干不同的计算机中去执行,然后汇总计算结果的过程。
这好比公司里面的某个团队,接到公司派发的任务,首先团队的主管,要把任务进行拆分,然后安排下去,划分给团队中不同的人去完成,并随时跟进任务的进展。如果团队主管离职了,那我们可能就会在团队中挑选一个对业务比较熟悉的人来接管主管位置。最后各个组员把任务完成,主管进行汇总,并上报给公司。在团队内部需要制定多个工作流程,来保证工作的有序开展。在分布式系统中同样需要设置这么一个协作规范。zookeeper可以很好的帮助我们来实现这个目的。


二、Zookeeper是什么?


ZooKeeper是一个开放源码的分布式协调服务,由知名互联网公司雅虎创建,是基于Google Chubby开源实现。(Google chubby是google公司开源的一个锁服务。)
ZooKeeper是一个高性能的分布式数据一致性解决方案,它将那些复杂的、容易出错的分布式一致性服务封装起来了,构成了一个高效可靠的源语集,并提供一系列简单易用的接口给用户。
ZooKeeper致力于提供一个高性能、高可用、且具有严格的顺序访问控制能力的分布式协调服务。分布式应用可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知,集群管理、Master 选举、分布式锁和分布式队列等功能。
Zookeeper知识点:
1、源代码开放
开源意味着我们可以免费的获取和使用zk,并且可以深入研究zk的源代码,甚至可以根据自己业务特性和要求进行二次开发修改。
2、是分布式协调服务,它解决分布式数据一致性问题
A、顺序一致性
所谓的顺序一致性是指从一个客户端发起一个请求,最终会严格按照发起的顺序应用到zk中。
B、原子性
原子性是指所有事物请求的处理结果在整个集群的所有机器上的应用情况是一致的。
C、单一视图
单一视图是指无任客户端连接到哪个zk的服务器,它看到的服务端数据都是一致的。
D、可靠性
可靠性是指一旦服务端成功的应用了一个事物并完成了对客户端的响应,那么这个事物所引起的服务端状态的变更会一直保留下来,除非有另外一个事物又对它进行了修改。
E、实时性
实时性是指zk保证在一段时间内客户端一定能从服务端读取最新的数据状态。
3、高性能
zk具有很高的吞吐量,一个三台服务器的集群可以达到12w-13w的QPS。
4、我们可以通过调用zk提供的接口解决一些分布式易用中的实际问题。


三、Zookeeper的典型应用场景


Zookeeper包括但不限于如下应用场景
 
3.1、数据发布/订阅
顾名思义就是一方把数据发布出来,另一方通过某种方式可以得到这些数据;
通常数据订阅有两种方式:推送模式和拉取模式
推送模式一般是服务器主动向客户端推送信息, 拉取模式是客户端主动去服务器获取数据(通常是采用定时轮询的方式),ZK采用两种方式相结合;
发布者将数据发布到ZK集群节点上,订阅者通过一定的方法告诉服务器,我对哪个节点的数据感兴趣,那服务器在这些节点的数据发生变化时,就通知客户端,客户端得到通知后可以去服务器获取数据信息。
3.2、负载均衡
zk_arch.png

实现过程:
1、首先DB在启动的时候先把自己在ZK上注册成一个临时节点,ZK的节点后面我们会讲到有两种,一种是永久节点,一类是临时节点临时节点在服务器出现问题的时候,节点会自动的从ZK上删除,那么这样ZK上的服务器列表就是最新的可用的列表。
2、客户端在需要读写数据库的时候首先它去ZooKeeper得到所有可用的DB的连接信息(一张列表),得到可用的数据列表。
3、客户端随机的算法,随机选择一个与之建立连接,每次会跟不同的数据库连接,就达到简单的复杂均衡。
4、当客户端发现连接不可用的时候可再次从ZK上获取可用的DB连接信息,当然也可以在刚获取的那个列表里移除掉不可用的连接后再随机选择一个DB与之连接。
3.3、命名服务
顾名思义,就是提供名称的服务,例如数据库表格ID,一般用得比较多的有两种ID,一种是自动增长的ID,一种是UUID(9291d71a-0354-4d8e-acd8-64f7393c64ae),两种ID各自都有缺陷,自动增长的ID局限在单库单表中使用,不能在分布式中使用,UUID可以在分布式中使用但是由于ID没有规律难于理解,我们可以借用ZK来生成一个顺序增长的,可以在集群环境下使用的,命名易于理解的ID。
3.4、分布式协调/通知
心跳检测,在分布式系统中,我们常常需要知道某个机器是否可用,传统的开发中,可以通过Ping某个主机来实现,Ping得通说明对方是可用的,相反是不可用的;
ZK中我们让所有的机其都注册一个临时节点,我们判断一个机器是否可用,我们只需要判断这个节点在ZK中是否存在就可以了,不需要直接去连接需要检查的机器 ,降低系统的复杂度。


四、Zookeeper的优势


    []源代码开放[/][]已经被证实是高性能,易用稳定的工业级产品。[/][]有着广泛的应用:Hadoop,HBase,Storm,Solr。[/]

转载请注明来自开源技术社区http://openskill.cn/article/281

Zookeeper Failed to process transaction type: 1 error: KeeperErrorCode = NoNode

大数据/云计算chris 回复了问题 • 2 人关注 • 1 个回复 • 1294 次浏览 • 2016-02-25 11:19 • 来自相关话题

zookeeper启动失败案例分析

大数据/云计算Ansible 发表了文章 • 0 个评论 • 965 次浏览 • 2016-02-24 17:54 • 来自相关话题

启动zookeeper运行结果如下:
[root@zk1 bin]# ./zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED查看状态如下:
[root@zk1 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Error contacting service. It is probably not running.
我netstat -anltup |grep 2018,并没有监听,端口没有占用啊?
 
那只好去看日志了,查看zookeeper的zookeeper.out日志内容如下:
2016-02-24 17:35:22,425 [myid:] - ERROR [main:ZooKeeperServerMain@63] - Unexpected exception, exiting abnormally
java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:344)
at sun.nio.ch.Net.bind(Net.java:336)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:199)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
at org.apache.zookeeper.server.NIOServerCnxnFactory.configure(NIOServerCnxnFactory.java:95)
at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:110)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:86)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:52)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:116)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:78)从日志中"Address already in use"可以看出端口被占用了!
但我用netstat -lntup是没被使用的,再用 lsof -i:2181,发现了2181端口果然被占用了。其实最好的办法是nc -v -z locahost 2181 探测一些本地2181端口是否监听!
[root@zk1 ~]# nc -v -z localhost 2181
Connection to localhost 2181 port [tcp/eforward] succeeded!
[root@zk1 ~]# 好了,端口被占用了,只能修改zk的默认端口,从新启动ok! 查看全部
启动zookeeper运行结果如下:
[root@zk1 bin]# ./zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
查看状态如下:
[root@zk1 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Error contacting service. It is probably not running.

我netstat -anltup |grep 2018,并没有监听,端口没有占用啊?
 
那只好去看日志了,查看zookeeper的zookeeper.out日志内容如下:
2016-02-24 17:35:22,425 [myid:] - ERROR [main:ZooKeeperServerMain@63] - Unexpected exception, exiting abnormally
java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:344)
at sun.nio.ch.Net.bind(Net.java:336)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:199)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
at org.apache.zookeeper.server.NIOServerCnxnFactory.configure(NIOServerCnxnFactory.java:95)
at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:110)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:86)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:52)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:116)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:78)
从日志中"Address already in use"可以看出端口被占用了!
但我用netstat -lntup是没被使用的,再用 lsof -i:2181,发现了2181端口果然被占用了。其实最好的办法是nc -v -z locahost 2181 探测一些本地2181端口是否监听!
[root@zk1 ~]# nc -v -z localhost 2181
Connection to localhost 2181 port [tcp/eforward] succeeded!
[root@zk1 ~]#
好了,端口被占用了,只能修改zk的默认端口,从新启动ok!
条新动态, 点击查看
push

push 回答了问题 • 2015-12-22 01:06 • 2 个回复 不感兴趣

怎么自定义zookeeper的启动内存

赞同来自:

找到你zookeeper的启动脚本文件zkServer.sh,大约在109-110行,加入如下设置:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.r... 显示全部 »
找到你zookeeper的启动脚本文件zkServer.sh,大约在109-110行,加入如下设置:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
改为
nohup $JAVA "-Xmx1G -Xms1G -Dzookeeper.log.dir=${ZOO_LOG_DIR}"...或者你到脚本中定义一个变量:
JVMPARAM="-ms128M -mx128M -XX:MaxPermSize=16m"然后加入到nohup启动参数:
582
然后用jmap在确认一下。

Zookeeper Too many connections情况

回复

大数据/云计算采菊篱下 回复了问题 • 2 人关注 • 1 个回复 • 249 次浏览 • 2016-11-03 12:05 • 来自相关话题

zookeeper客户端连接zookeeper服务器策略是什么?

回复

大数据/云计算采菊篱下 回复了问题 • 2 人关注 • 1 个回复 • 273 次浏览 • 2016-10-11 12:29 • 来自相关话题

怎么自定义zookeeper的启动内存

回复

大数据/云计算OpenSkill 回复了问题 • 3 人关注 • 2 个回复 • 1020 次浏览 • 2016-04-03 02:54 • 来自相关话题

Zookeeper Failed to process transaction type: 1 error: KeeperErrorCode = NoNode

回复

大数据/云计算chris 回复了问题 • 2 人关注 • 1 个回复 • 1294 次浏览 • 2016-02-25 11:19 • 来自相关话题

zookeeper启动异常Invalid config, exiting abnormally

回复

大数据/云计算采菊篱下 回复了问题 • 2 人关注 • 1 个回复 • 1250 次浏览 • 2016-02-24 15:45 • 来自相关话题

为什么“hbase.zookeeper.quorum”必须配奇数个数的DataNode

回复

大数据/云计算采菊篱下 回复了问题 • 3 人关注 • 1 个回复 • 1225 次浏览 • 2015-06-29 00:48 • 来自相关话题

Python可用的分布式协调系统(ZooKeeper,Consul, etcd)介绍

大数据/云计算push 发表了文章 • 0 个评论 • 1033 次浏览 • 2016-06-19 22:52 • 来自相关话题

随着大数据时代的到来,分布式是解决大数据问题的一个主要手段,随着越来越多的分布式的服务,如何在分布式的系统中对这些服务做协调变成了一个很棘手的问题。今天我们就来看看如何使用Python,利用开源对分布式服务做协调。
 
在对分布式的应用做协调的时候,主要会碰到以下的应用场景:
[]业务发现(service discovery)[/]
           找到分布式系统中存在那些可用的服务和节点
[]名字服务 (name service)[/]
           通过给定的名字知道到对应的资源
[]配置管理 (configuration management)[/]
           如何在分布式的节点中共享配置文件,保证一致性。
[]故障发现和故障转移 (failure detection and failover)[/]
           当某一个节点出故障的时候,如何检测到并通知其它节点, 或者把想用的服务转移到其它的可用节点
[]领导选举(leader election)[/]
           如何在众多的节点中选举一个领导者,来协调所有的节点
[]分布式的锁 (distributed exclusive lock)[/]
           如何通过锁在分布式的服务中进行同步
[]消息和通知服务 (message queue and notification)[/]
           如何在分布式的服务中传递消息,以通知的形式对事件作出主动的响应
有许多的开源软件试图解决以上的全部或者部分问题,例如ZooKeeper,consul,doozerd等等,我们现在就看看它们是如何做的。

ZooKeeper

 
Zookeeper 是使用最广泛,也是最有名的解决分布式服务的协调问题的开源软件了,它最早和Hadoop一起开发,后来成为了Apache的顶级项目,很多开源的项目都在使用ZooKeeper,例如大名鼎鼎的Kafka。
 
Zookeeper本身是一个分布式的应用,通过对共享的数据的管理来实现对分布式应用的协调。

ZooKeeper使用一个树形目录作为数据模型,这个目录和文件目录类似,目录上的每一个节点被称作ZNodes。




ZooKeeper提供基本的API来操纵和控制Znodes,包括对节点的创建,删除,设置和获取数据,获得子节点等。

除了这些基本的操作,ZooKeeper还提供了一些配方(Recipe),其实就是一些常见的用例,例如锁,两阶段提交,领导选举等等。

ZooKeeper本身是用Java开发的,所以对Java的支持是最自然的。它同时还提供了C语言的绑定。
 
Kazoo 是一个非常成熟的Zookeeper Python客户端,我们这就看看如果使用Python来调用ZooKeeper。(注意,运行以下的例子,需要在本地启动ZooKeeper的服务)
 
基本操作
 
以下的例子现实了对Znode的基本操作,首先要创建一个客户端的连接,并启动客户端。然后我们可以利用该客户端对Znode做增删改,取内容的操作。最后退出客户端。from kazoo.client import KazooClient

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/zk1")

# Create a node with data
zk.create("/test/zk1/node", b"a test value")

# Determine if a node exists
if zk.exists("/test/zk1"):
print "the node exist"

# Print the version of a node and its data
data, stat = zk.get("/test/zk1")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))

# List the children
children = zk.get_children("/test/zk1")
print("There are %s children with names %s" % (len(children), children))

zk.stop()通过对ZNode的操作,我们可以完成一些分布式服务协调的基本需求,包括名字服务,配置服务,分组等等。
 
故障检测(Failure Detection)
在分布式系统中,一个最基本的需求就是当某一个服务出问题的时候,能够通知其它的节点或者某个管理节点。

ZooKeeper提供ephemeral Node的概念,当创建该Node的服务退出或者异常中止的时候,该Node会被删除,所以我们就可以利用这种行为来监控服务运行状态。

以下是worker的代码

from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/failure_detection")

# Create a node with data
zk.create("/test/failure_detection/worker",
value=b"a test value", ephemeral=True)

while True:
print "I am alive!"
time.sleep(3)

zk.stop()以下的monitor 代码,监控worker服务是否运行。
from kazoo.client import KazooClient

import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Determine if a node exists
while True:
if zk.exists("/test/failure_detection/worker"):
print "the worker is alive!"
else:
print "the worker is dead!"
break
time.sleep(3)

zk.stop()
领导选举
Kazoo直接提供了领导选举的API,使用起来非常方便。from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def leader_func():
print "I am the leader {}".format(str(my_id))
while True:
print "{} is working! ".format(str(my_id))
time.sleep(3)

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

election = zk.Election("/electionpath")

# blocks until the election is won, then calls
# leader_func()
election.run(leader_func)

zk.stop()你可以同时运行多个worker,其中一个会获得Leader,当你杀死当前的leader后,会有一个新的leader被选出。
 
分布式锁
锁的概念大家都熟悉,当我们希望某一件事在同一时间只有一个服务在做,或者某一个资源在同一时间只有一个服务能访问,这个时候,我们就需要用到锁。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def work():
print "{} is working! ".format(str(my_id))

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

lock = zk.Lock("/lockpath", str(my_id))

print "I am {}".format(str(my_id))

while True:
with lock:
work()
time.sleep(3)

zk.stop()当你运行多个worker的时候,不同的worker会试图获取同一个锁,然而只有一个worker会工作,其它的worker必须等待获得锁后才能执行。
 
监视
ZooKeeper提供了监视(Watch)的功能,当节点的数据被修改的时候,监控的function会被调用。我们可以利用这一点进行配置文件的同步,发消息,或其他需要通知的功能。
from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

@zk.DataWatch('/path/to/watch')
def my_func(data, stat):
if data:
print "Data is %s" % data
print "Version is %s" % stat.version
else :
print "data is not available"

while True:
time.sleep(10)

zk.stop()除了我们上面列举的内容外,Kazoo还提供了许多其他的功能,例如:计数,租约,队列等等,大家有兴趣可以参考它的文档。
 

Consul

Consul 是用Go开发的分布式服务协调管理的工具,它提供了服务发现,健康检查,Key/Value存储等功能,并且支持跨数据中心的功能。

Consul提供ZooKeeper类似的功能,它的基于HTTP的API可以方便的和各种语言进行绑定。自然Python也在列。

与Zookeeper有所差异的是Consul通过基于Client/Server架构的Agent部署来支持跨Data Center的功能。




Consul在Cluster伤的每一个节点都运行一个Agent,这个Agent可以使Server或者Client模式。Client负责到Server的高效通信,相对为无状态的。 Server负责包括选举领导节点,维护cluster的状态,对所有的查询做响应,跨数据中心的通信等等。
 
KV基本操作
 
类似于Zookeeper,Consul支持对KV的增删查改的操作。import consul

c = consul.Consul()

# set data for key foo
c.kv.put('foo', 'bar')

# poll a key for updates
index = None
while True:
index, data = c.kv.get('foo', index=index)
print data['Value']

c.kv.delete('foo')这里和ZooKeeper对Znode的操作几乎是一样的。
 
服务发现(Service Discovery)和健康检查(Health Check)
 
Consul的另一个主要的功能是用于对分布式的服务做管理,用户可以注册一个服务,同时还提供对服务做健康检测的功能。

首先,用户需要定义一个服务。
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "127.0.0.1",
"port": 8000,
"checks": [
{
"script": "/usr/local/bin/check_redis.py",
"interval": "10s"
}
]
}}其中,服务的名字是必须的,其它的字段可以自选,包括了服务的地址,端口,相应的健康检查的脚本。当用户注册了一个服务后,就可以通过Consul来查询该服务,获得该服务的状态。
 
Consul支持三种Check的模式:
[]调用一个外部脚本(Script),在该模式下,consul定时会调用一个外部脚本,通过脚本的返回内容获得对应服务的健康状态。[/][]调用HTTP,在该模式下,consul定时会调用一个HTTP请求,返回2XX,则为健康;429 (Too many request)是警告。其它均为不健康[/][]主动上报,在该模式下,服务需要主动调用一个consul提供的HTTP PUT请求,上报健康状态。[/]
 
Python API提供对应的接口,大家可以参考 http://python-consul.readthedocs.org/en/latest/
[]Consul.Agent.Service[/][]Consul.Agent.Check[/]
 
Consul的Health Check和Zookeeper的Failure Detection略有不同,ZooKeeper可以利用ephemeral Node来检测服务的状态,Consul的Health Check,通过调用脚本,HTTP或者主动上报的方式检查服务的状态,更为灵活,可以获得等多的信息,但是也需要做更多的工作。
 
故障检测(Failure Detection)
 
Consul提供Session的概念,利用Session可以检查服务是否存活。

对每一个服务我们都可以创建一个session对象,注意这里我们设置了ttl,consul会以ttl的数值为间隔时间,持续的对session的存活做检查。对应的在服务中,我们需要持续的renew session,保证session是合法的。
import consul
import time

c = consul.Consul()

s = c.session.create(name="worker",behavior='delete',ttl=10)

print "session id is {}".format(s)

while True:
c.session.renew(s)
print "I am alive ..."
time.sleep(3)Moniter代码用于监控worker相关联的session的状态,但发现worker session已经不存在了,就做出响应的处理。
import consul
import time

def is_session_exist(name, sessions):
for s in sessions:
if s['Name'] == name:
return True

return False

c = consul.Consul()

while True:
index, sessions = c.session.list()
if is_session_exist('worker', sessions):
print "worker is alive ..."
else:
print 'worker is dead!'
break
time.sleep(3)这里注意,因为是基于ttl(最小10秒)的检测,从业务中断到被检测到,至少有10秒的时延,对应需要实时响应的情景,并不适用。Zookeeper使用ephemeral Node的方式时延相对短一点,但也非实时。
 
领导选举和分布式的锁
 
无论是Consul本身还是Python客户端,都不直接提供Leader Election的功能,但是这篇文档介绍了如何利用Consul的KV存储来实现Leader Election,利用Consul的KV功能,可以很方便的实现领导选举和锁的功能。

当对某一个Key做put操作的时候,可以创建一个session对象,设置一个acquire标志为该 session,这样就获得了一个锁,获得所得客户则是被选举的leader。

代码如下:
import consul
import time

c = consul.Consul()

def request_lead(namespace, session_id):
lock = c.kv.put(leader_namespace,"leader check", acquire=session_id)
return lock

def release_lead(session_id):
c.session.destroy(session_id)

def whois_lead(namespace):
index,value = c.kv.get(namespace)
session = value.get('Session')
if session is None:
print 'No one is leading, maybe in electing'
else:
index, value = c.session.info(session)
print '{} is leading'.format(value['ID'])

def work_non_block():
print "working"

def work_block():
while True:
print "working"
time.sleep(3)

leader_namespace = 'leader/test'

[size=16] initialize leader key/value node[/size]
leader_index, leader_node = c.kv.get(leader_namespace)

if leader_node is None:
c.kv.put(leader_namespace,"a leader test")

while True:
whois_lead(leader_namespace)
session_id = c.session.create(ttl=10)
if request_lead(leader_namespace,session_id):
print "I am now the leader"
work_block()
release_lead(session_id)
else:
print "wait leader elected!"
time.sleep(3)利用同样的机制,可以方便的实现锁,信号量等分布式的同步操作。
 
监视
Consul的Agent提供了Watch的功能,然而Python客户端并没有相应的接口。
 
 

Etcd

Etcd 是另一个用GO开发的分布式协调应用,它提供一个分布式的Key/Value存储来进行共享的配置管理和服务发现。
 
同样的etcd使用基于HTTP的API,可以灵活的进行不同语言的绑定,我们用的是这个客户端https://github.com/jplana/python-etcd
 
基本操作
import etcd

client = etcd.Client()
client.write('/nodes/n1', 1)
print client.read('/nodes/n1').valueetcd对节点的操作和ZooKeeper类似,不过etcd不支持ZooKeeper的ephemeral Node的概念,要监控服务的状态似乎比较麻烦。
 
分布式锁
etcd支持分布式锁,以下是一个例子。
import sys

sys.path.append("../../")

import etcd

import uuid
import time

my_id = uuid.uuid4()

def work():
print "I get the lock {}".format(str(my_id))

client = etcd.Client()

lock = etcd.Lock(client, '/customerlock', ttl=60)

with lock as my_lock:
work()
lock.is_locked() # True
lock.renew(60)
lock.is_locked() # False老版本的etcd支持leader election,但是在最新版该功能被deprecated了,参见https://coreos.com/etcd/docs/0.4.7/etcd-modules/
 
其它
 
我们针对分布式协调的功能讨论了三个不同的开源应用,其实还有许多其它的选择,我这里就不一一介绍,大家有兴趣可以访问以下的链接:
[]eureka https://github.com/Netflix/eureka [/]
           Netflix开发的定位服务,应用于fail over和load balance的功能
[]curator http://curator.apache.org/[/]
           基于ZooKeeper的更高层次的封装
[]doozerd https://github.com/ha/doozerd[/]
           基于GO的高可靠,分布式的数据存储,过去两年已经不活跃
[]openreplica http://openreplica.org/[/]
           基于Python开发的,面向对象的接口的分布式应用协调的工具
[]serf http://www.serfdom.io/[/]
           serf提供轻量级的cluster成员管理,故障检测(failure detection)和协调。开发基于GO语言。Consul使用了serf提供的功能
[]noah https://github.com/lusis/Noah[/]
           基于ruby的ZooKeeper实现,过去三年不活跃
[]copy cat https://github.com/kuujo/copycat[/]
            基于日志的分布式协调的框架,使用Java开发
 

总结

ZooKeeper无疑是分布式协调应用的最佳选择,功能全,社区活跃,用户群体很大,对所有典型的用例都有很好的封装,支持不同语言的绑定。缺点是,整个应用比较重,依赖于Java,不支持跨数据中心。

Consul作为使用Go语言开发的分布式协调,对业务发现的管理提供很好的支持,他的HTTP API也能很好的和不同的语言绑定,并支持跨数据中心的应用。缺点是相对较新,适合喜欢尝试新事物的用户。

etcd是一个更轻量级的分布式协调的应用,提供了基本的功能,更适合一些轻量级的应用来使用。
 
参考
如果大家对于分布式系统的协调想要进行更多的了解,可以阅读一下的链接:
http://stackoverflow.com/questions/6047917/zookeeper-alternatives-cluster-coordination-service 

http://txt.fliglio.com/2014/05/encapsulated-services-with-consul-and-confd/ 

http://txt.fliglio.com/2013/12/service-discovery-with-docker-docker-links-and-beyond/ 

http://www.serfdom.io/intro/vs-zookeeper.html 

http://devo.ps/blog/zookeeper-vs-doozer-vs-etcd/ 

https://www.digitalocean.com/community/articles/how-to-set-up-a-serf-cluster-on-several-ubuntu-vps 

http://www.slideshare.net/JyrkiPulliainen/taming-pythons-with-zoo-keeper-ep2013?qid=e1267f58-090d-4147-9909-ec673525e76b&v=qf1&b=&from_search=8 

http://muratbuffalo.blogspot.com/2014/09/paper-summary-tango-distributed-data.html 

https://developer.yahoo.com/blogs/hadoop/apache-zookeeper-making-417.html 

http://www.knewton.com/tech/blog/2014/12/eureka-shouldnt-use-zookeeper-service-discovery 

http://codahale.com/you-cant-sacrifice-partition-tolerance/
原文地址:http://my.oschina.net/taogang/blog/410864 查看全部
随着大数据时代的到来,分布式是解决大数据问题的一个主要手段,随着越来越多的分布式的服务,如何在分布式的系统中对这些服务做协调变成了一个很棘手的问题。今天我们就来看看如何使用Python,利用开源对分布式服务做协调。
 
在对分布式的应用做协调的时候,主要会碰到以下的应用场景:
    []业务发现(service discovery)[/]

           找到分布式系统中存在那些可用的服务和节点
    []名字服务 (name service)[/]

           通过给定的名字知道到对应的资源
    []配置管理 (configuration management)[/]

           如何在分布式的节点中共享配置文件,保证一致性。
    []故障发现和故障转移 (failure detection and failover)[/]

           当某一个节点出故障的时候,如何检测到并通知其它节点, 或者把想用的服务转移到其它的可用节点
    []领导选举(leader election)[/]

           如何在众多的节点中选举一个领导者,来协调所有的节点
    []分布式的锁 (distributed exclusive lock)[/]

           如何通过锁在分布式的服务中进行同步
    []消息和通知服务 (message queue and notification)[/]

           如何在分布式的服务中传递消息,以通知的形式对事件作出主动的响应
有许多的开源软件试图解决以上的全部或者部分问题,例如ZooKeeper,consul,doozerd等等,我们现在就看看它们是如何做的。


ZooKeeper


 
Zookeeper 是使用最广泛,也是最有名的解决分布式服务的协调问题的开源软件了,它最早和Hadoop一起开发,后来成为了Apache的顶级项目,很多开源的项目都在使用ZooKeeper,例如大名鼎鼎的Kafka。
 
Zookeeper本身是一个分布式的应用,通过对共享的数据的管理来实现对分布式应用的协调。

ZooKeeper使用一个树形目录作为数据模型,这个目录和文件目录类似,目录上的每一个节点被称作ZNodes。
zk.png

ZooKeeper提供基本的API来操纵和控制Znodes,包括对节点的创建,删除,设置和获取数据,获得子节点等。

除了这些基本的操作,ZooKeeper还提供了一些配方(Recipe),其实就是一些常见的用例,例如锁,两阶段提交,领导选举等等。

ZooKeeper本身是用Java开发的,所以对Java的支持是最自然的。它同时还提供了C语言的绑定。
 
Kazoo 是一个非常成熟的Zookeeper Python客户端,我们这就看看如果使用Python来调用ZooKeeper。(注意,运行以下的例子,需要在本地启动ZooKeeper的服务)
 
基本操作
 
以下的例子现实了对Znode的基本操作,首先要创建一个客户端的连接,并启动客户端。然后我们可以利用该客户端对Znode做增删改,取内容的操作。最后退出客户端。
from kazoo.client import KazooClient

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/zk1")

# Create a node with data
zk.create("/test/zk1/node", b"a test value")

# Determine if a node exists
if zk.exists("/test/zk1"):
print "the node exist"

# Print the version of a node and its data
data, stat = zk.get("/test/zk1")
print("Version: %s, data: %s" % (stat.version, data.decode("utf-8")))

# List the children
children = zk.get_children("/test/zk1")
print("There are %s children with names %s" % (len(children), children))

zk.stop()
通过对ZNode的操作,我们可以完成一些分布式服务协调的基本需求,包括名字服务,配置服务,分组等等。
 
故障检测(Failure Detection)
在分布式系统中,一个最基本的需求就是当某一个服务出问题的时候,能够通知其它的节点或者某个管理节点。

ZooKeeper提供ephemeral Node的概念,当创建该Node的服务退出或者异常中止的时候,该Node会被删除,所以我们就可以利用这种行为来监控服务运行状态。

以下是worker的代码

from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Ensure a path, create if necessary
zk.ensure_path("/test/failure_detection")

# Create a node with data
zk.create("/test/failure_detection/worker",
value=b"a test value", ephemeral=True)

while True:
print "I am alive!"
time.sleep(3)

zk.stop()
以下的monitor 代码,监控worker服务是否运行。
from kazoo.client import KazooClient

import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

# Determine if a node exists
while True:
if zk.exists("/test/failure_detection/worker"):
print "the worker is alive!"
else:
print "the worker is dead!"
break
time.sleep(3)

zk.stop()

领导选举
Kazoo直接提供了领导选举的API,使用起来非常方便。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def leader_func():
print "I am the leader {}".format(str(my_id))
while True:
print "{} is working! ".format(str(my_id))
time.sleep(3)

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

election = zk.Election("/electionpath")

# blocks until the election is won, then calls
# leader_func()
election.run(leader_func)

zk.stop()
你可以同时运行多个worker,其中一个会获得Leader,当你杀死当前的leader后,会有一个新的leader被选出。
 
分布式锁
锁的概念大家都熟悉,当我们希望某一件事在同一时间只有一个服务在做,或者某一个资源在同一时间只有一个服务能访问,这个时候,我们就需要用到锁。
from kazoo.client import KazooClient
import time
import uuid

import logging
logging.basicConfig()

my_id = uuid.uuid4()

def work():
print "{} is working! ".format(str(my_id))

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

lock = zk.Lock("/lockpath", str(my_id))

print "I am {}".format(str(my_id))

while True:
with lock:
work()
time.sleep(3)

zk.stop()
当你运行多个worker的时候,不同的worker会试图获取同一个锁,然而只有一个worker会工作,其它的worker必须等待获得锁后才能执行。
 
监视
ZooKeeper提供了监视(Watch)的功能,当节点的数据被修改的时候,监控的function会被调用。我们可以利用这一点进行配置文件的同步,发消息,或其他需要通知的功能。
from kazoo.client import KazooClient
import time

import logging
logging.basicConfig()

zk = KazooClient(hosts='127.0.0.1:2181')
zk.start()

@zk.DataWatch('/path/to/watch')
def my_func(data, stat):
if data:
print "Data is %s" % data
print "Version is %s" % stat.version
else :
print "data is not available"

while True:
time.sleep(10)

zk.stop()
除了我们上面列举的内容外,Kazoo还提供了许多其他的功能,例如:计数,租约,队列等等,大家有兴趣可以参考它的文档
 


Consul


Consul 是用Go开发的分布式服务协调管理的工具,它提供了服务发现,健康检查,Key/Value存储等功能,并且支持跨数据中心的功能。

Consul提供ZooKeeper类似的功能,它的基于HTTP的API可以方便的和各种语言进行绑定。自然Python也在列。

与Zookeeper有所差异的是Consul通过基于Client/Server架构的Agent部署来支持跨Data Center的功能。
ConsulArch.png

Consul在Cluster伤的每一个节点都运行一个Agent,这个Agent可以使Server或者Client模式。Client负责到Server的高效通信,相对为无状态的。 Server负责包括选举领导节点,维护cluster的状态,对所有的查询做响应,跨数据中心的通信等等。
 
KV基本操作
 
类似于Zookeeper,Consul支持对KV的增删查改的操作。
import consul

c = consul.Consul()

# set data for key foo
c.kv.put('foo', 'bar')

# poll a key for updates
index = None
while True:
index, data = c.kv.get('foo', index=index)
print data['Value']

c.kv.delete('foo')
这里和ZooKeeper对Znode的操作几乎是一样的。
 
服务发现(Service Discovery)和健康检查(Health Check)
 
Consul的另一个主要的功能是用于对分布式的服务做管理,用户可以注册一个服务,同时还提供对服务做健康检测的功能。

首先,用户需要定义一个服务。
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "127.0.0.1",
"port": 8000,
"checks": [
{
"script": "/usr/local/bin/check_redis.py",
"interval": "10s"
}
]
}}
其中,服务的名字是必须的,其它的字段可以自选,包括了服务的地址,端口,相应的健康检查的脚本。当用户注册了一个服务后,就可以通过Consul来查询该服务,获得该服务的状态。
 
Consul支持三种Check的模式:
    []调用一个外部脚本(Script),在该模式下,consul定时会调用一个外部脚本,通过脚本的返回内容获得对应服务的健康状态。[/][]调用HTTP,在该模式下,consul定时会调用一个HTTP请求,返回2XX,则为健康;429 (Too many request)是警告。其它均为不健康[/][]主动上报,在该模式下,服务需要主动调用一个consul提供的HTTP PUT请求,上报健康状态。[/]

 
Python API提供对应的接口,大家可以参考 http://python-consul.readthedocs.org/en/latest/
    []Consul.Agent.Service[/][]Consul.Agent.Check[/]

 
Consul的Health Check和Zookeeper的Failure Detection略有不同,ZooKeeper可以利用ephemeral Node来检测服务的状态,Consul的Health Check,通过调用脚本,HTTP或者主动上报的方式检查服务的状态,更为灵活,可以获得等多的信息,但是也需要做更多的工作。
 
故障检测(Failure Detection)
 
Consul提供Session的概念,利用Session可以检查服务是否存活。

对每一个服务我们都可以创建一个session对象,注意这里我们设置了ttl,consul会以ttl的数值为间隔时间,持续的对session的存活做检查。对应的在服务中,我们需要持续的renew session,保证session是合法的。
import consul
import time

c = consul.Consul()

s = c.session.create(name="worker",behavior='delete',ttl=10)

print "session id is {}".format(s)

while True:
c.session.renew(s)
print "I am alive ..."
time.sleep(3)
Moniter代码用于监控worker相关联的session的状态,但发现worker session已经不存在了,就做出响应的处理。
import consul
import time

def is_session_exist(name, sessions):
for s in sessions:
if s['Name'] == name:
return True

return False

c = consul.Consul()

while True:
index, sessions = c.session.list()
if is_session_exist('worker', sessions):
print "worker is alive ..."
else:
print 'worker is dead!'
break
time.sleep(3)
这里注意,因为是基于ttl(最小10秒)的检测,从业务中断到被检测到,至少有10秒的时延,对应需要实时响应的情景,并不适用。Zookeeper使用ephemeral Node的方式时延相对短一点,但也非实时。
 
领导选举和分布式的锁
 
无论是Consul本身还是Python客户端,都不直接提供Leader Election的功能,但是这篇文档介绍了如何利用Consul的KV存储来实现Leader Election,利用Consul的KV功能,可以很方便的实现领导选举和锁的功能。

当对某一个Key做put操作的时候,可以创建一个session对象,设置一个acquire标志为该 session,这样就获得了一个锁,获得所得客户则是被选举的leader。

代码如下:
import consul
import time

c = consul.Consul()

def request_lead(namespace, session_id):
lock = c.kv.put(leader_namespace,"leader check", acquire=session_id)
return lock

def release_lead(session_id):
c.session.destroy(session_id)

def whois_lead(namespace):
index,value = c.kv.get(namespace)
session = value.get('Session')
if session is None:
print 'No one is leading, maybe in electing'
else:
index, value = c.session.info(session)
print '{} is leading'.format(value['ID'])

def work_non_block():
print "working"

def work_block():
while True:
print "working"
time.sleep(3)

leader_namespace = 'leader/test'

[size=16] initialize leader key/value node[/size]
leader_index, leader_node = c.kv.get(leader_namespace)

if leader_node is None:
c.kv.put(leader_namespace,"a leader test")

while True:
whois_lead(leader_namespace)
session_id = c.session.create(ttl=10)
if request_lead(leader_namespace,session_id):
print "I am now the leader"
work_block()
release_lead(session_id)
else:
print "wait leader elected!"
time.sleep(3)
利用同样的机制,可以方便的实现锁,信号量等分布式的同步操作。
 
监视
Consul的Agent提供了Watch的功能,然而Python客户端并没有相应的接口。
 
 


Etcd


Etcd 是另一个用GO开发的分布式协调应用,它提供一个分布式的Key/Value存储来进行共享的配置管理和服务发现。
 
同样的etcd使用基于HTTP的API,可以灵活的进行不同语言的绑定,我们用的是这个客户端https://github.com/jplana/python-etcd
 
基本操作
import etcd

client = etcd.Client()
client.write('/nodes/n1', 1)
print client.read('/nodes/n1').value
etcd对节点的操作和ZooKeeper类似,不过etcd不支持ZooKeeper的ephemeral Node的概念,要监控服务的状态似乎比较麻烦。
 
分布式锁
etcd支持分布式锁,以下是一个例子。
import sys

sys.path.append("../../")

import etcd

import uuid
import time

my_id = uuid.uuid4()

def work():
print "I get the lock {}".format(str(my_id))

client = etcd.Client()

lock = etcd.Lock(client, '/customerlock', ttl=60)

with lock as my_lock:
work()
lock.is_locked() # True
lock.renew(60)
lock.is_locked() # False
老版本的etcd支持leader election,但是在最新版该功能被deprecated了,参见https://coreos.com/etcd/docs/0.4.7/etcd-modules/
 
其它
 
我们针对分布式协调的功能讨论了三个不同的开源应用,其实还有许多其它的选择,我这里就不一一介绍,大家有兴趣可以访问以下的链接:

           Netflix开发的定位服务,应用于fail over和load balance的功能

           基于ZooKeeper的更高层次的封装

           基于GO的高可靠,分布式的数据存储,过去两年已经不活跃

           基于Python开发的,面向对象的接口的分布式应用协调的工具

           serf提供轻量级的cluster成员管理,故障检测(failure detection)和协调。开发基于GO语言。Consul使用了serf提供的功能

           基于ruby的ZooKeeper实现,过去三年不活跃

            基于日志的分布式协调的框架,使用Java开发
 


总结


ZooKeeper无疑是分布式协调应用的最佳选择,功能全,社区活跃,用户群体很大,对所有典型的用例都有很好的封装,支持不同语言的绑定。缺点是,整个应用比较重,依赖于Java,不支持跨数据中心。

Consul作为使用Go语言开发的分布式协调,对业务发现的管理提供很好的支持,他的HTTP API也能很好的和不同的语言绑定,并支持跨数据中心的应用。缺点是相对较新,适合喜欢尝试新事物的用户。

etcd是一个更轻量级的分布式协调的应用,提供了基本的功能,更适合一些轻量级的应用来使用。
 
参考
如果大家对于分布式系统的协调想要进行更多的了解,可以阅读一下的链接:
http://stackoverflow.com/questions/6047917/zookeeper-alternatives-cluster-coordination-service 

http://txt.fliglio.com/2014/05/encapsulated-services-with-consul-and-confd/ 

http://txt.fliglio.com/2013/12/service-discovery-with-docker-docker-links-and-beyond/ 

http://www.serfdom.io/intro/vs-zookeeper.html 

http://devo.ps/blog/zookeeper-vs-doozer-vs-etcd/ 

https://www.digitalocean.com/community/articles/how-to-set-up-a-serf-cluster-on-several-ubuntu-vps 

http://www.slideshare.net/JyrkiPulliainen/taming-pythons-with-zoo-keeper-ep2013?qid=e1267f58-090d-4147-9909-ec673525e76b&v=qf1&b=&from_search=8 

http://muratbuffalo.blogspot.com/2014/09/paper-summary-tango-distributed-data.html 

https://developer.yahoo.com/blogs/hadoop/apache-zookeeper-making-417.html 

http://www.knewton.com/tech/blog/2014/12/eureka-shouldnt-use-zookeeper-service-discovery 

http://codahale.com/you-cant-sacrifice-partition-tolerance/
原文地址:http://my.oschina.net/taogang/blog/410864

基于Zookeeper的服务注册与发现案例分析

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 717 次浏览 • 2016-03-16 17:22 • 来自相关话题

背景

大多数系统都是从一个单一系统开始起步的,随着公司业务的快速发展,这个单一系统变得越来越庞大,带来几个问题:
[]随着访问量的不断攀升,纯粹通过提升机器的性能来已经不能解决问题,系统无法进行有效的水平扩展[/][]维护这个单一系统,变得越来越复杂[/][]同时,随着业务场景的不同以及大研发的招兵买马带来了不同技术背景的工程师,在原有达达Python技术栈的基础上,引入了Java技术栈。[/]
如何来解决这些问题?业务服务化是个有效的手段来解决大规模系统的性能瓶颈和复杂性。通过系统拆分将原有的单一庞大系统拆分成小系统,它带来了如下好处:
[]原来系统的压力得到很好的分流,有效地解决了原先系统的瓶颈,同时带来了更好的扩展性[/][]独立的代码库,更少的业务逻辑,系统的维护性得到极大的增强[/]
同时,也带来了一系列问题:
[]随着系统服务的越来越多,如何来管理这些服务?[/][]如何分发请求到提供同一服务的多台主机上(负载均衡如何来做)[/][]如果提供服务的Endpoint发生变化,如何将这些信息通知服务的调用方?[/]

最初的解决方案

Linkedin的创始人里德霍夫曼曾经说过:成立一家初创公司就像把自己从悬崖上扔下来,在降落过程中去组装一架飞机。这对于初创公司达达也是一样的,业务在以火箭般的速度发展着。技术在业务发展中作用就是保障业务的稳定运行,快速地“组装一架飞机”。所以,在业务服务化的早期,我们采用了Nginx+本地hosts文件的方式进行内部服务的注册与发现,架构图如下:




各系统组件角色如下:
[]服务消费者通过本地hosts中的服务提供者域名与Nginx的IP绑定信息来调用服务[/][]Nginx用来对服务提供者提供的服务进行存活检查和负载均衡[/][]服务提供者提供服务给服务消费者访问,并通过Nginx来进行请求分发[/]
这在内部系统比较少,访问量比较小的情况下,解决了服务的注册,发现与负载均衡等问题。但是,随着内部服务越来愈多,访问量越来越大的情况下,该架构的隐患逐渐暴露出来:
[]最明显的问题是Nginx存在单点故障(SPOF),同时随着访问量的提升,会成为一个性能瓶颈[/][]随着内部服务的越来越多,不同的服务消费方需要配置不同的hosts,很容易在增加新的主机时忘记配置hosts导致服务不能调用问题,增加了运维负担[/][]服务的配置信息分散在各个主机hosts中,难以保持一致性,不便于服务的管理[/][]服务主机的发布和下线需要手工的修改Nginx upstream配置,修改的配置需要上线,不利于服务的快速部署[/]

如何解决

在谈如何来解决之前,现梳理一下服务注册与发现的目标:
[]服务的注册信息应该统一保存,方便于服务的管理[/][]自动通过服务的名称去发现服务,而不必了解这个服务提供的end-point到底是哪台主机[/][]支持服务的负载均衡及fail-over[/][]增加或移除某个服务的end-point时,对于服务的消费者来说是透明的[/][]支持Python和Java[/]
备选方案一: DNSDNS作为服务注册发现的一种方案,它比较简单。只要在DNS服务上,配置一个DNS名称与IP对应关系即可。定位一个服务只需要连接到DNS服务器上,随机返回一个IP地址即可。由于存在DNS缓存,所以DNS服务器本身不会成为一个瓶颈。这种基于Pull的方式不能及时获取服务的状态的更新(例如:服务的IP更新等)。如果服务的提供者出现故障,由于DNS缓存的存在,服务的调用方会仍然将请求转发给出现故障的服务提供方;反之亦然。备选方案二:Dubbo
Dubbo是阿里巴巴推出的分布式服务框架,致力于解决服务的注册与发现,编排,治理。
它的优点如下:
[]功能全面,易于扩展[/][]支持各种序列化协议(JSON,Hession,java序列化等)[/][]支持各种RPC协议(HTTP,Java RMI,Dubbo自身的RPC协议等)[/][]支持多种负载均衡算法[/][]其他高级特性:服务编排,服务治理,服务监控等[/]
缺点如下:
[]只支持Java,对于Python没有相应的支持[/][]虽然已经开源,但是没有成熟的社区来运营和维护,未来升级可能是个麻烦[/][]重量级的解决方案带来新的复杂性[/]
备选方案三:Zookeeper
Zookeeper是什么?按照Apache官网的描述是:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.参照官网的定义,它能够做:
[]作为配置信息的存储的中心服务器[/][]命名服务[/][]分布式的协调[/][]Mater选举等[/]
在定义中特别提到了命名服务。在调研之后,Zookeeper作为服务注册与发现的解决方案;
它有如下优点:
[]它提供的简单API[/][]已有互联网公司(例如:Pinterest,Airbnb)使用它来进行服务注册与发现[/][]支持多语言的客户端[/][]通过Watcher机制实现Push模型,服务注册信息的变更能够及时通知服务消费方[/]
缺点是:
[]引入新的Zookeeper组件,带来新的复杂性和运维问题[/][]需自己通过它提供的API来实现服务注册与发现逻辑(包含Python与Java版本)[/]
针对对上述几个方案的优缺点权衡之后,决定采用了基于Zookeeper实现自己的服务注册与发现。

基于Zookeeper的服务注册与发现架构





在此架构中有三类角色:服务提供者,服务注册中心,服务消费者。
服务提供者
服务提供者作为服务的提供方将自身的服务信息注册到服务注册中心中。服务信息包含:
[]隶属于哪个系统[/][]服务的IP,端口[/][]服务的请求URL[/][]服务的权重等等[/]
服务注册中心
服务注册中心主要提供所有服务注册信息的中心存储,同时负责将服务注册信息的更新通知实时的Push给服务消费者(主要是通过Zookeeper的Watcher机制来实现的)。
服务消费者
服务消费者主要职责如下:
[]服务消费者在启动时从服务注册中心获取需要的服务注册信息[/][]将服务注册信息缓存在本地[/][]监听服务注册信息的变更,如接收到服务注册中心的服务变更通知,则在本地缓存中更新服务的注册信息[/][]根据本地缓存中的服务注册信息构建服务调用请求,并根据负载均衡策略(随机负载均衡,Round-Robin负载均衡等)来转发请求[/][]对服务提供方的存活进行检测,如果出现服务不可用的服务提供方,将从本地缓存中剔除[/]
服务消费者只在自己初始化以及服务变更时会依赖服务注册中心,在此阶段的单点故障通过Zookeeper集群来进行保障。在整个服务调用过程中,服务消费者不依赖于任何第三方服务。

实现机制介绍​

Zookeeper数据模型介绍
在整个服务注册与发现的设计中,最重要是如何来存储服务的注册信息。
在设计基于Zookeeper的服务注册结构之前,我们先来看一下Zookeeper的数据模型。Zookeeper的数据模型如下图所示:



Zookeeper数据模型结构与Unix文件系统很类似,是一个树状层次结构。每个节点叫做Znode,节点可以拥有子节点,同时允许将少量数据存储在该节点下。客户端可以通过监听节点的数据变更和子节点变更来实时获取Znode的变更(Wather机制)。服务注册结构




服务注册结构如上图所示。
[]/dada来标示公司名称dada,同时能方便与其它应用的目录区分开(例如:Kafka的brokers注册信息放置在/brokers下)[/][]/dada/services将所有服务提供者都放置该目录下[/][]/dada/services/category1目录定义具体的服务提供者的id:category1,同时该Znode节点中允许存放该服务提供者的一些元数据信息,例如:名称,服务提供者的Owner,上下文路径(Java Web项目),健康检查路径等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld节点定义了服务提供者category1下的一个服务:helloworld。其中helloworld为该服务的ID,同时允许将该服务的元数据信息存储在该Znode下,例如图中标示的:服务名称,服务描述,服务路径,服务的调用的schema,服务的调用的HTTP METHOD等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld/providers节点定义了服务提供者的父节点。在这里其实可以将服务提供者的IP和端口直接放置在helloworld节点下,在这里单独放一个节点,是为了将来可以将服务消费者的消息挂载在helloworld节点下,进行一些扩展,例如命名为:/dada/services/category1/helloworld/consumers。[/][]/dada/services/category__1/helloworld/providers/192.168.1.1:8080该节点定义了服务提供者的IP和端口,同时在节点中定义了该服务提供者的权重。[/]

实现机制

由于目前服务注册通过我们的服务注册中心UI来进行注册,这部分逻辑比较简单,即通过UI界面来构造上述定义的服务注册结构。

下面着重介绍一下我们的服务发现是如何工作的:




在上述类图中,类ServiceDiscovery主要通过Zookeeper的API(Python/Java版本)来获取服务信息,同时对服务注册结构中的每个服务的providers节点增加Watcher,来监控节点变化。获取的服务注册信息保存在变量service_repos中。通过在初始化时设置LoadBalanceStrategy的实现(Round-Robin算法,Radmon算法)来实现服务提供者的负载均衡。主要方法:
[]init获取Zookeeper的服务注册信息,并缓存在service_repos[/][]get_service_repos方法获取实例变量service_repos[/][]get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址[/][]update_service_repos通过Zookeeper的Watcher机制来实时更新本地缓存service_repos[/][]heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,如果出现问题,将该服务提供者从该服务的提供者列表中移除;反之,则加入到服务的提供者列表中[/]
LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
RoundRobinStrategy和RandomStrategy分别实现了Round-Robin和随机的负载均衡算法

未来展望

目前达达基于Zookeeper的服务注册与发现的架构还处于初期,很多功能还未完善,例如:服务的路由功能,与部署平台的集成,服务的监控等等。当然基于Zookeeper还能做其它许多事情,例如:实时动态配置系统。目前,我们已经基于Zookeeper实现了实时动态配置系统。

分享原文地址:https://tech.imdada.cn/2015/12/03/service-registry-and-discovery-with-zk/
作者:杨 骏  达达CTO 查看全部


背景


大多数系统都是从一个单一系统开始起步的,随着公司业务的快速发展,这个单一系统变得越来越庞大,带来几个问题:
    []随着访问量的不断攀升,纯粹通过提升机器的性能来已经不能解决问题,系统无法进行有效的水平扩展[/][]维护这个单一系统,变得越来越复杂[/][]同时,随着业务场景的不同以及大研发的招兵买马带来了不同技术背景的工程师,在原有达达Python技术栈的基础上,引入了Java技术栈。[/]

如何来解决这些问题?业务服务化是个有效的手段来解决大规模系统的性能瓶颈和复杂性。通过系统拆分将原有的单一庞大系统拆分成小系统,它带来了如下好处:
    []原来系统的压力得到很好的分流,有效地解决了原先系统的瓶颈,同时带来了更好的扩展性[/][]独立的代码库,更少的业务逻辑,系统的维护性得到极大的增强[/]

同时,也带来了一系列问题:
    []随着系统服务的越来越多,如何来管理这些服务?[/][]如何分发请求到提供同一服务的多台主机上(负载均衡如何来做)[/][]如果提供服务的Endpoint发生变化,如何将这些信息通知服务的调用方?[/]


最初的解决方案


Linkedin的创始人里德霍夫曼曾经说过:
成立一家初创公司就像把自己从悬崖上扔下来,在降落过程中去组装一架飞机。
这对于初创公司达达也是一样的,业务在以火箭般的速度发展着。技术在业务发展中作用就是保障业务的稳定运行,快速地“组装一架飞机”。所以,在业务服务化的早期,我们采用了Nginx+本地hosts文件的方式进行内部服务的注册与发现,架构图如下:
zknginx.png

各系统组件角色如下:
    []服务消费者通过本地hosts中的服务提供者域名与Nginx的IP绑定信息来调用服务[/][]Nginx用来对服务提供者提供的服务进行存活检查和负载均衡[/][]服务提供者提供服务给服务消费者访问,并通过Nginx来进行请求分发[/]

这在内部系统比较少,访问量比较小的情况下,解决了服务的注册,发现与负载均衡等问题。但是,随着内部服务越来愈多,访问量越来越大的情况下,该架构的隐患逐渐暴露出来:
    []最明显的问题是Nginx存在单点故障(SPOF),同时随着访问量的提升,会成为一个性能瓶颈[/][]随着内部服务的越来越多,不同的服务消费方需要配置不同的hosts,很容易在增加新的主机时忘记配置hosts导致服务不能调用问题,增加了运维负担[/][]服务的配置信息分散在各个主机hosts中,难以保持一致性,不便于服务的管理[/][]服务主机的发布和下线需要手工的修改Nginx upstream配置,修改的配置需要上线,不利于服务的快速部署[/]


如何解决


在谈如何来解决之前,现梳理一下服务注册与发现的目标:
    []服务的注册信息应该统一保存,方便于服务的管理[/][]自动通过服务的名称去发现服务,而不必了解这个服务提供的end-point到底是哪台主机[/][]支持服务的负载均衡及fail-over[/][]增加或移除某个服务的end-point时,对于服务的消费者来说是透明的[/][]支持Python和Java[/]

备选方案一: DNS
DNS作为服务注册发现的一种方案,它比较简单。只要在DNS服务上,配置一个DNS名称与IP对应关系即可。定位一个服务只需要连接到DNS服务器上,随机返回一个IP地址即可。由于存在DNS缓存,所以DNS服务器本身不会成为一个瓶颈。
这种基于Pull的方式不能及时获取服务的状态的更新(例如:服务的IP更新等)。如果服务的提供者出现故障,由于DNS缓存的存在,服务的调用方会仍然将请求转发给出现故障的服务提供方;反之亦然。
备选方案二:Dubbo
Dubbo是阿里巴巴推出的分布式服务框架,致力于解决服务的注册与发现,编排,治理。
它的优点如下:
    []功能全面,易于扩展[/][]支持各种序列化协议(JSON,Hession,java序列化等)[/][]支持各种RPC协议(HTTP,Java RMI,Dubbo自身的RPC协议等)[/][]支持多种负载均衡算法[/][]其他高级特性:服务编排,服务治理,服务监控等[/]

缺点如下:
    []只支持Java,对于Python没有相应的支持[/][]虽然已经开源,但是没有成熟的社区来运营和维护,未来升级可能是个麻烦[/][]重量级的解决方案带来新的复杂性[/]

备选方案三:Zookeeper
Zookeeper是什么?按照Apache官网的描述是:
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
参照官网的定义,它能够做:
    []作为配置信息的存储的中心服务器[/][]命名服务[/][]分布式的协调[/][]Mater选举等[/]

在定义中特别提到了命名服务。在调研之后,Zookeeper作为服务注册与发现的解决方案;
它有如下优点:
    []它提供的简单API[/][]已有互联网公司(例如:Pinterest,Airbnb)使用它来进行服务注册与发现[/][]支持多语言的客户端[/][]通过Watcher机制实现Push模型,服务注册信息的变更能够及时通知服务消费方[/]

缺点是:
    []引入新的Zookeeper组件,带来新的复杂性和运维问题[/][]需自己通过它提供的API来实现服务注册与发现逻辑(包含Python与Java版本)[/]

针对对上述几个方案的优缺点权衡之后,决定采用了基于Zookeeper实现自己的服务注册与发现。


基于Zookeeper的服务注册与发现架构


zkregsine.png

在此架构中有三类角色:服务提供者,服务注册中心,服务消费者。
服务提供者
服务提供者作为服务的提供方将自身的服务信息注册到服务注册中心中。服务信息包含:
    []隶属于哪个系统[/][]服务的IP,端口[/][]服务的请求URL[/][]服务的权重等等[/]

服务注册中心
服务注册中心主要提供所有服务注册信息的中心存储,同时负责将服务注册信息的更新通知实时的Push给服务消费者(主要是通过Zookeeper的Watcher机制来实现的)。
服务消费者
服务消费者主要职责如下:
    []服务消费者在启动时从服务注册中心获取需要的服务注册信息[/][]将服务注册信息缓存在本地[/][]监听服务注册信息的变更,如接收到服务注册中心的服务变更通知,则在本地缓存中更新服务的注册信息[/][]根据本地缓存中的服务注册信息构建服务调用请求,并根据负载均衡策略(随机负载均衡,Round-Robin负载均衡等)来转发请求[/][]对服务提供方的存活进行检测,如果出现服务不可用的服务提供方,将从本地缓存中剔除[/]

服务消费者只在自己初始化以及服务变更时会依赖服务注册中心,在此阶段的单点故障通过Zookeeper集群来进行保障。在整个服务调用过程中,服务消费者不依赖于任何第三方服务。


实现机制介绍​


Zookeeper数据模型介绍
在整个服务注册与发现的设计中,最重要是如何来存储服务的注册信息。
在设计基于Zookeeper的服务注册结构之前,我们先来看一下Zookeeper的数据模型。Zookeeper的数据模型如下图所示:
zluo.png
Zookeeper数据模型结构与Unix文件系统很类似,是一个树状层次结构。每个节点叫做Znode,节点可以拥有子节点,同时允许将少量数据存储在该节点下。客户端可以通过监听节点的数据变更和子节点变更来实时获取Znode的变更(Wather机制)。
服务注册结构
dd.png

服务注册结构如上图所示。
    []/dada来标示公司名称dada,同时能方便与其它应用的目录区分开(例如:Kafka的brokers注册信息放置在/brokers下)[/][]/dada/services将所有服务提供者都放置该目录下[/][]/dada/services/category1目录定义具体的服务提供者的id:category1,同时该Znode节点中允许存放该服务提供者的一些元数据信息,例如:名称,服务提供者的Owner,上下文路径(Java Web项目),健康检查路径等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld节点定义了服务提供者category1下的一个服务:helloworld。其中helloworld为该服务的ID,同时允许将该服务的元数据信息存储在该Znode下,例如图中标示的:服务名称,服务描述,服务路径,服务的调用的schema,服务的调用的HTTP METHOD等。该信息可以根据实际需要进行自由扩展。[/][]/dada/services/category1/helloworld/providers节点定义了服务提供者的父节点。在这里其实可以将服务提供者的IP和端口直接放置在helloworld节点下,在这里单独放一个节点,是为了将来可以将服务消费者的消息挂载在helloworld节点下,进行一些扩展,例如命名为:/dada/services/category1/helloworld/consumers。[/][]/dada/services/category__1/helloworld/providers/192.168.1.1:8080该节点定义了服务提供者的IP和端口,同时在节点中定义了该服务提供者的权重。[/]


实现机制


由于目前服务注册通过我们的服务注册中心UI来进行注册,这部分逻辑比较简单,即通过UI界面来构造上述定义的服务注册结构。

下面着重介绍一下我们的服务发现是如何工作的:
lj.png

在上述类图中,类ServiceDiscovery主要通过Zookeeper的API(Python/Java版本)来获取服务信息,同时对服务注册结构中的每个服务的providers节点增加Watcher,来监控节点变化。获取的服务注册信息保存在变量service_repos中。通过在初始化时设置LoadBalanceStrategy的实现(Round-Robin算法,Radmon算法)来实现服务提供者的负载均衡。主要方法:
    []init获取Zookeeper的服务注册信息,并缓存在service_repos[/][]get_service_repos方法获取实例变量service_repos[/][]get_service_endpoint根据init构建好的service_repos,以及lb_strategy提供的负载均衡策略返回某个服务的URL地址[/][]update_service_repos通过Zookeeper的Watcher机制来实时更新本地缓存service_repos[/][]heartbeat_monitor是一个心跳检测线程,用来进行服务提供者的健康存活检测,如果出现问题,将该服务提供者从该服务的提供者列表中移除;反之,则加入到服务的提供者列表中[/]

LoadBalanceStrategy定义了根据服务提供者的信息返回对应的服务Host和IP,即决定由那台主机+端口来提供服务。
RoundRobinStrategy和RandomStrategy分别实现了Round-Robin和随机的负载均衡算法


未来展望


目前达达基于Zookeeper的服务注册与发现的架构还处于初期,很多功能还未完善,例如:服务的路由功能,与部署平台的集成,服务的监控等等。
当然基于Zookeeper还能做其它许多事情,例如:实时动态配置系统。目前,我们已经基于Zookeeper实现了实时动态配置系统。


分享原文地址:https://tech.imdada.cn/2015/12/03/service-registry-and-discovery-with-zk/
作者:杨 骏  达达CTO


ZooKeeper架构设计及其应用

开源技术chris 发表了文章 • 0 个评论 • 4065 次浏览 • 2016-03-11 23:45 • 来自相关话题

ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。

总体架构​

ZooKeeper分布式协调服务框架的总体架构,如图所示:



ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点。Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变更同步(广播)到其他的Follower节点。

设计要点

ZooKeeper是基于如下4个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:
[]简单[/]
分布式应用中的各个进程可以通过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:



在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。
[]在上图示例的ZooKeeper的数据模型中,有如下要点:[/]
[]每个节点(ZNode)中存储的是同步相关的数据(这是ZooKeeper设计的初衷,数据量很小,大概B到KB量级),例如状态信息、配置内容、位置信息等。[/][]一个ZNode维护了一个状态结构,该结构包括:版本号、ACL变更、时间戳。每次ZNode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。[/][]每个ZNode都有一个ACL,用来限制是否可以访问该ZNode。[/][]在一个命名空间中,对ZNode上存储的数据执行读和写请求操作都是原子的。[/][]客户端可以在一个ZNode上设置一个监视器(Watch),如果该ZNode数据发生变更,ZooKeeper会通知客户端,从而触发监视器中实现的逻辑的执行。[/][]每个客户端与ZooKeeper连接,便建立了一次会话(Session),会话过程中,可能发生CONNECTING、CONNECTED和CLOSED三种状态。[/][]ZooKeeper支持临时节点(Ephemeral Nodes)的概念,它是与ZooKeeper中的会话(Session)相关的,如果连接断开,则该节点被删除。[/]
 
[]冗余[/]
ZooKeeper被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提高性能,为了避免发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。
[]有序[/]
ZooKeeper使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper可以实现更加高级的抽象操作,如同步等。
[]快速[/]
ZooKeeper包括读写两种操作,基于ZooKeeper的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1),那么读性能更能够体现出高效。

数据模型

ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念,前面我们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。
[]ZNode结构[/]
ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变更,一个Zxid与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode的组成结构,引用文档如下所示:
[]czxid – The zxid of the change that caused this znode to be created.[/][]mzxid – The zxid of the change that last modified this znode.[/][]ctime – The time in milliseconds from epoch when this znode was created.[/][]mtime – The time in milliseconds from epoch when this znode was last modified.[/][]version – The number of changes to the data of this znode.[/][]cversion – The number of changes to the children of this znode.[/][]aversion – The number of changes to the ACL of this znode.[/][]ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.[/][]dataLength – The length of the data field of this znode.[/][]numChildren – The number of children of this znode.[/]
[]Watches(监视)[/]
ZooKeeper中的Watch是只能触发一次。也就是说,如果客户端在指定的ZNode设置了Watch,如果该ZNode数据发生变更,ZooKeeper会发送一个变更通知给客户端,同时触发设置的Watch事件。如果ZNode数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode的Watch,则ZooKeeper就不会发送一个变更通知给客户端。ZooKeeper异步通知设置Watch的客户端。但是ZooKeeper能够保证在ZNode的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode数据的变更,但是看到变更的顺序是能够保证有序一致的。ZNode可以设置两类Watch,一个是Data Watches(该ZNode的数据变更导致触发Watch事件),另一个是Child Watches(该ZNode的孩子节点发生变更导致触发Watch事件)。调用getData()和exists() 方法可以设置Data Watches,调用getChildren()方法可以设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法创建一个ZNode,将触发该ZNode的Data Watches;调用create()方法创建ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,如果该被删除的ZNode还有父节点,则父节点触发一个Child Watches。另外,如果客户端与ZooKeeper Server断开连接,客户端就无法触发Watches,除非再次与ZooKeeper Server建立连接。
[]Sequence Nodes(序列节点)[/]
在创建ZNode的时候,可以请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列:qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007
[]ACLs(访问控制列表)[/]
ACL可以控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的所有孩子节点上。它主要有如下五种权限:
[]CREATE 允许创建Child Nodes[/][]READ 允许获取ZNode的数据,以及该节点的孩子列表[/][]WRITE 可以修改ZNode的数据[/][]DELETE 可以删除一个孩子节点[/][]ADMIN 可以设置权限[/]
ZooKeeper内置了4种方式实现ACL:
[]world 一个单独的ID,表示任何人都可以访问[/][]auth 不使用ID,只有认证的用户可以访问[/][]digest 使用username:password生成MD5哈希值作为认证ID[/][]ip 使用客户端主机IP地址来进行认证[/]
[]ZooKeeper Session[/]
当客户端连接到ZooKeeper集群时,建立了会话。会话过程中的状态变迁,如图所示:



建立连接过程中,会话状态为CONNECTING;当连接建立成功后,会话状态变为CONNECTED。会话过程中,如果正常的话,会话的状态只能是CONNECTING和CONNECTED二者之一。如果在会话过程中连接断开,则变为CLOSED状态。

应用陷阱

并非任何分布式应用都适合使用ZooKeeper来构建协调服务,我们根据ZooKeeper提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。总结如下:
1、丢失ZNode上的变更通知客户端连接到ZooKeeper Server以后,会维护一个TCP连接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。所以,如果使用ZooKeeper的Watch,必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。2、无效ZooKeeper集群节点列表​
与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况:一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active状态,能够提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。另一种情况,客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。所以,我们在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。3、配置导致的性能问题如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性能极大地下降,从而可能会影响应用程序。为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。4、事务日志存储设备性能​ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。5、ZNode存储大量数据导致性能问题​ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

 
参考链接:
http://zookeeper.apache.org/ 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperOver.html 
http://wiki.apache.org/hadoop/ZooKeeper/PoweredBy 
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 
http://zookeeper.apache.org/doc/r3.3.4/recipes.html 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperProgrammers.html 查看全部
ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。


总体架构​


ZooKeeper分布式协调服务框架的总体架构,如图所示:
zk1.png
ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower。当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点。
Leader节点在接收到数据变更请求后,首先将变更写入本地磁盘,以作恢复之用。当所有的写请求持久化到磁盘以后,才会将变更应用到内存中。
ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。
当一个Leader节点发生故障失效时,失败故障是快速响应的,消息层负责重新选择一个Leader,继续作为协调服务集群的中心,处理客户端写请求,并将ZooKeeper协调系统的数据变更同步(广播)到其他的Follower节点。


设计要点


ZooKeeper是基于如下4个目标来进行权衡和设计的,我们从设计及其特性的角度来详细说明:
    []简单[/]

分布式应用中的各个进程可以通过ZooKeeper的命名空间(Namespace)来进行协调,这个命名空间是共享的、具有层次结构的,更重要的是它的结构足够简单,像我们平时接触到的文件系统的目录结构一样容易理解,如图所示:
zk2.png
在ZooKeeper中每个命名空间(Namespace)被称为ZNode,你可以这样理解,每个ZNode包含一个路径和与之相关的元数据,以及继承自该节点的孩子列表。与传统文件系统不同的是,ZooKeeper中的数据保存在内存中,实现了分布式同步服务的高吞吐和低延迟。

    []在上图示例的ZooKeeper的数据模型中,有如下要点:[/]

    []每个节点(ZNode)中存储的是同步相关的数据(这是ZooKeeper设计的初衷,数据量很小,大概B到KB量级),例如状态信息、配置内容、位置信息等。[/][]一个ZNode维护了一个状态结构,该结构包括:版本号、ACL变更、时间戳。每次ZNode数据发生变化,版本号都会递增,这样客户端的读请求可以基于版本号来检索状态相关数据。[/][]每个ZNode都有一个ACL,用来限制是否可以访问该ZNode。[/][]在一个命名空间中,对ZNode上存储的数据执行读和写请求操作都是原子的。[/][]客户端可以在一个ZNode上设置一个监视器(Watch),如果该ZNode数据发生变更,ZooKeeper会通知客户端,从而触发监视器中实现的逻辑的执行。[/][]每个客户端与ZooKeeper连接,便建立了一次会话(Session),会话过程中,可能发生CONNECTING、CONNECTED和CLOSED三种状态。[/][]ZooKeeper支持临时节点(Ephemeral Nodes)的概念,它是与ZooKeeper中的会话(Session)相关的,如果连接断开,则该节点被删除。[/]

 
    []冗余[/]

ZooKeeper被设计为复制集群架构,每个节点的数据都可以在集群中复制传播,使集群中的每个节点数据同步一致,从而达到服务的可靠性和可用性。前面说到,ZooKeeper将数据放在内存中来提高性能,为了避免发生单点故障(SPOF),支持数据的复制来达到冗余存储,这是必不可少的。

    []有序[/]

ZooKeeper使用时间戳来记录导致状态变更的事务性操作,也就是说,一组事务通过时间戳来保证有序性。基于这一特性。ZooKeeper可以实现更加高级的抽象操作,如同步等。

    []快速[/]

ZooKeeper包括读写两种操作,基于ZooKeeper的分布式应用,如果是读多写少的应用场景(读写比例大约是10:1),那么读性能更能够体现出高效。


数据模型


ZooKeeper有一个分层的命名空间,结构类似文件系统的目录结构,非常简单而直观。其中,ZNode是最重要的概念,前面我们已经描述过。另外,有ZNode有关的还包括Watches、ACL、临时节点、序列节点(Sequence Node)。

    []ZNode结构[/]

ZooKeeper中使用Zxid(ZooKeeper Transaction Id)来表示每次节点数据变更,一个Zxid与一个时间戳对应,所以多个不同的变更对应的事务是有序的。下面是ZNode的组成结构,引用文档如下所示:
    []czxid – The zxid of the change that caused this znode to be created.[/][]mzxid – The zxid of the change that last modified this znode.[/][]ctime – The time in milliseconds from epoch when this znode was created.[/][]mtime – The time in milliseconds from epoch when this znode was last modified.[/][]version – The number of changes to the data of this znode.[/][]cversion – The number of changes to the children of this znode.[/][]aversion – The number of changes to the ACL of this znode.[/][]ephemeralOwner – The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero.[/][]dataLength – The length of the data field of this znode.[/][]numChildren – The number of children of this znode.[/]

    []Watches(监视)[/]

ZooKeeper中的Watch是只能触发一次。也就是说,如果客户端在指定的ZNode设置了Watch,如果该ZNode数据发生变更,ZooKeeper会发送一个变更通知给客户端,同时触发设置的Watch事件。如果ZNode数据又发生了变更,客户端在收到第一次通知后没有重新设置该ZNode的Watch,则ZooKeeper就不会发送一个变更通知给客户端。
ZooKeeper异步通知设置Watch的客户端。但是ZooKeeper能够保证在ZNode的变更生效之后才会异步地通知客户端,然后客户端才能够看到ZNode的数据变更。由于网络延迟,多个客户端可能会在不同的时间看到ZNode数据的变更,但是看到变更的顺序是能够保证有序一致的。
ZNode可以设置两类Watch,一个是Data Watches(该ZNode的数据变更导致触发Watch事件),另一个是Child Watches(该ZNode的孩子节点发生变更导致触发Watch事件)。调用getData()和exists() 方法可以设置Data Watches,调用getChildren()方法可以设置Child Watches。调用setData()方法触发在该ZNode的注册的Data Watches。调用create()方法创建一个ZNode,将触发该ZNode的Data Watches;调用create()方法创建ZNode的孩子节点,则触发ZNode的Child Watches。调用delete()方法删除ZNode,则同时触发Data Watches和Child Watches,如果该被删除的ZNode还有父节点,则父节点触发一个Child Watches。
另外,如果客户端与ZooKeeper Server断开连接,客户端就无法触发Watches,除非再次与ZooKeeper Server建立连接。

    []Sequence Nodes(序列节点)[/]

在创建ZNode的时候,可以请求ZooKeeper生成序列,以路径名为前缀,计数器紧接在路径名后面,例如,会生成类似如下形式序列:
qn-0000000001, qn-0000000002, qn-0000000003, qn-0000000004, qn-0000000005, qn-0000000006, qn-0000000007

    []ACLs(访问控制列表)[/]

ACL可以控制访问ZooKeeper的节点,只能应用于特定的ZNode上,而不能应用于该ZNode的所有孩子节点上。它主要有如下五种权限:
    []CREATE 允许创建Child Nodes[/][]READ 允许获取ZNode的数据,以及该节点的孩子列表[/][]WRITE 可以修改ZNode的数据[/][]DELETE 可以删除一个孩子节点[/][]ADMIN 可以设置权限[/]

ZooKeeper内置了4种方式实现ACL:
    []world 一个单独的ID,表示任何人都可以访问[/][]auth 不使用ID,只有认证的用户可以访问[/][]digest 使用username:password生成MD5哈希值作为认证ID[/][]ip 使用客户端主机IP地址来进行认证[/]

    []ZooKeeper Session[/]

当客户端连接到ZooKeeper集群时,建立了会话。会话过程中的状态变迁,如图所示:
zk3.png
建立连接过程中,会话状态为CONNECTING;当连接建立成功后,会话状态变为CONNECTED。会话过程中,如果正常的话,会话的状态只能是CONNECTING和CONNECTED二者之一。如果在会话过程中连接断开,则变为CLOSED状态。


应用陷阱


并非任何分布式应用都适合使用ZooKeeper来构建协调服务,我们根据ZooKeeper提供的文档,给出哪些情况下使用会出现问题,又是如何应对这种问题的。总结如下:
1、丢失ZNode上的变更通知
客户端连接到ZooKeeper Server以后,会维护一个TCP连接。在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知(后续会触发一定的逻辑执行流程)。但是,如果由于网络异常,客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。
所以,如果使用ZooKeeper的Watch,必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。
2、无效ZooKeeper集群节点列表​
与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况:
一种情况是,如果客户端持有的列表或者列表子集,其中节点都处于Active状态,能够提供协调服务,那么客户端访问ZooKeeper集群没有任何问题。
另一种情况,客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群,如果客户端再次连接这一类失效的节点,就无法获取服务。
所以,我们在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查ZooKeeper集群,使整个集群恢复正常。
3、配置导致的性能问题
如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性能极大地下降,从而可能会影响应用程序。
为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。
4、事务日志存储设备性能​
ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,如果存储设备是专用的,那么整个服务以至外部应用都会获得极大地性能提升。
5、ZNode存储大量数据导致性能问题​
ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据,如果存储了大量数据,导致ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。
所以,如果需要与大量的数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

 
参考链接:
http://zookeeper.apache.org/ 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperOver.html 
http://wiki.apache.org/hadoop/ZooKeeper/PoweredBy 
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ 
http://zookeeper.apache.org/doc/r3.3.4/recipes.html 
http://zookeeper.apache.org/doc/r3.3.4/zookeeperProgrammers.html

Zookeeper基本概念详解

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 1526 次浏览 • 2016-03-02 01:31 • 来自相关话题

根据如上思维导图,我们来展开对Zookeeper的基本的一些概念解释。

一、集群角色

LeaderLeader服务器是整个Zookeeper集群工作机制中的核心 FollowerFollower服务器是Zookeeper集群状态的跟随者ObserverObserver服务器充当一个观察者的角色Leader,Follower 设计模式;Observer 观察者设计模式

二、会话

会话是指客户端和ZooKeeper服务器的连接,ZooKeeper中的会话叫Session,客户端靠与服务器建立一个TCP的长连接;来维持一个Session,客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向ZK服务器发送请求并获得响应。

三、数据节点

Zookeeper中的节点有两类:
[]集群中的一台机器称为一个节点[/][]数据模型中的数据单元Znode,分为持久节点和临时节点[/]
 Zookeeper的数据模型是一棵树,树的节点就是Znode,Znode中可以保存信息。如下图所示:



ZK大致数据结构跟上图是一致的,如上图所示这个图就像一棵树,这个树有个根节点,然后其下有些子节点,然后每个子节点其下又可以有子节点,大多数的开发就是跟zk的这些数据节点打交道,来读写这些数据节点,来完成任务。

四、版本

ZK中的版本,是用来记录节点数据或者是节点的子节点列表或者是权限信息的修改次数,注意是这里是修改次数。如果一个节点的version是1,那就代表说这个节点从创建以来被修改了一次,那么这个版本怎么用呢,典型的我们可以利用版本来实现分布式的锁服务。我们知道在数据库中,一般有两种锁,一种是悲观锁一种是乐观锁。


悲观锁悲观锁又叫悲观并发锁,是数据库中一种非常严格的锁策略,具有强烈的排他性,能够避免不同事务对同一数据并发更新造成的数据不一致性,在上一个事务没有完成之前,下一个事务不能访问相同的资源,适合数据更新竞争非常激烈的场景;乐观锁相比悲观锁,乐观锁使用的场景会更多,悲观锁认为事务访问相同数据的时候一定会出现相互的干扰,所以简单粗暴的使用排他访问的方式,而乐观锁认为不同事务访问相同资源是很少出现相互干扰的情况,因此在事务处理期间不需要进行并发控制,当然乐观锁也是锁,它还是会有并发的控制!对于数据库我们通常的做法是在每个表中增加一个version版本字段,事务修改数据之前先读出数据,当然版号也顺势读取出来,然后把这个读取出来的版本号加入到更新语句的条件中,比如,读取出来的版本号是1,我们修改数据的语句可以这样写,update 某某表 set 字段一=某某值 where id=1 and version=1,那如果更新失败了说明以后其他事务已经修改过数据了,那系统需要抛出异常给客户端,让客户端自行处理,客户端可以选择重试。锁,ZK中版本有类式的作用。ZK的版本类型有三种:version cversion aversion





五、Watcher

Watcher我们可以理解为他是一个事件监听器ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化的时候,ZooKeeper服务器会把这个变化的通知发送给感兴趣的客户端。


两个客户端都在zookeeper集群中注册了watcher(事件监听器),那么当zk中的节点数据发生变化的时候,zk会把这一变化的通知发送给客户端,当客户端收到这个变化通知的时候,它可以再回到zk中,去取得这个数据的详细信息。

六、ACL权限控制

 
ACL是Access Control Lists 的简写, ZooKeeper采用ACL策略来进行权限控制,有以下权限:
[]CREATE: 创建子节点的权限[/][]READ: 获取节点数据和子节点列表的权限[/][]WRITE: 更新节点数据的权限[/][]DELETE: 删除子节点的权限[/][]ADMIN: 设置节点ACL的权限[/]
上面的权限有点类似我们信息系统的权限管理,我们在开发系统的时候一般也会对数据做这些权限管理,一个zk集群可能会服务很多的业务,尤其是一些大公司,zk集群的节点中会保存重要的信息,那么这些信息通常只能对一部分的访问者开放,通过acl我们可以对某些节点的访问进行授权,从而来保证数据的安全。 查看全部
zk_logic.png

根据如上思维导图,我们来展开对Zookeeper的基本的一些概念解释。


一、集群角色


Leader
Leader服务器是整个Zookeeper集群工作机制中的核心 
Follower
Follower服务器是Zookeeper集群状态的跟随者
Observer
Observer服务器充当一个观察者的角色
Leader,Follower 设计模式;Observer 观察者设计模式


二、会话


会话是指客户端和ZooKeeper服务器的连接,ZooKeeper中的会话叫Session,客户端靠与服务器建立一个TCP的长连接;
来维持一个Session,客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向ZK服务器发送请求并获得响应。


三、数据节点


Zookeeper中的节点有两类:
    []集群中的一台机器称为一个节点[/][]数据模型中的数据单元Znode,分为持久节点和临时节点[/]

 
Zookeeper的数据模型是一棵树,树的节点就是Znode,Znode中可以保存信息。
如下图所示:
tree.png
ZK大致数据结构跟上图是一致的,如上图所示这个图就像一棵树,这个树有个根节点,然后其下有些子节点,然后每个子节点其下又可以有子节点,大多数的开发就是跟zk的这些数据节点打交道,来读写这些数据节点,来完成任务。


四、版本


ZK中的版本,是用来记录节点数据或者是节点的子节点列表或者是权限信息的修改次数,注意是这里是修改次数。如果一个节点的version是1,那就代表说这个节点从创建以来被修改了一次,那么这个版本怎么用呢,典型的我们可以利用版本来实现分布式的锁服务。我们知道在数据库中,一般有两种锁,一种是悲观锁一种是乐观锁。


悲观锁
悲观锁又叫悲观并发锁,是数据库中一种非常严格的锁策略,具有强烈的排他性,能够避免不同事务对同一数据并发更新造成的数据不一致性,在上一个事务没有完成之前,下一个事务不能访问相同的资源,适合数据更新竞争非常激烈的场景;
乐观锁
相比悲观锁,乐观锁使用的场景会更多,悲观锁认为事务访问相同数据的时候一定会出现相互的干扰,所以简单粗暴的使用排他访问的方式,而乐观锁认为不同事务访问相同资源是很少出现相互干扰的情况,因此在事务处理期间不需要进行并发控制,当然乐观锁也是锁,它还是会有并发的控制!对于数据库我们通常的做法是在每个表中增加一个version版本字段,事务修改数据之前先读出数据,当然版号也顺势读取出来,然后把这个读取出来的版本号加入到更新语句的条件中,比如,读取出来的版本号是1,我们修改数据的语句可以这样写,update 某某表 set 字段一=某某值 where id=1 and version=1,那如果更新失败了说明以后其他事务已经修改过数据了,那系统需要抛出异常给客户端,让客户端自行处理,客户端可以选择重试。
锁,ZK中版本有类式的作用。
ZK的版本类型有三种:version cversion aversion
table.png


五、Watcher


Watcher我们可以理解为他是一个事件监听器
ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化的时候,ZooKeeper服务器会把这个变化的通知发送给感兴趣的客户端。
watcher.png
两个客户端都在zookeeper集群中注册了watcher(事件监听器),那么当zk中的节点数据发生变化的时候,zk会把这一变化的通知发送给客户端,当客户端收到这个变化通知的时候,它可以再回到zk中,去取得这个数据的详细信息。


六、ACL权限控制


 
ACL是Access Control Lists 的简写, ZooKeeper采用ACL策略来进行权限控制,有以下权限:
    []CREATE: 创建子节点的权限[/][]READ: 获取节点数据和子节点列表的权限[/][]WRITE: 更新节点数据的权限[/][]DELETE: 删除子节点的权限[/][]ADMIN: 设置节点ACL的权限[/]

上面的权限有点类似我们信息系统的权限管理,我们在开发系统的时候一般也会对数据做这些权限管理,一个zk集群可能会服务很多的业务,尤其是一些大公司,zk集群的节点中会保存重要的信息,那么这些信息通常只能对一部分的访问者开放,通过acl我们可以对某些节点的访问进行授权,从而来保证数据的安全。

Zookeeper介绍

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 1147 次浏览 • 2016-02-28 18:32 • 来自相关话题

根据如上思维导图,我来展开对Zookeeper的介绍

一、Zookeeper背景

随着互联网技术的高速发展,企业对计算机系统的计算、存储能力要求越来越高,最简单的证明就是出现了一些诸如:高并发,海量存储这样的词汇。在这样的背景下,单纯依靠少量高性能主机来完成计算任务也就不能满足现有大部分企业的需求了,企业的IT架构逐步从集中式向分布式过度,所谓的分布式是指:把一个计算任务分解成若干个计算单元,并且分配到若干不同的计算机中去执行,然后汇总计算结果的过程。这好比公司里面的某个团队,接到公司派发的任务,首先团队的主管,要把任务进行拆分,然后安排下去,划分给团队中不同的人去完成,并随时跟进任务的进展。如果团队主管离职了,那我们可能就会在团队中挑选一个对业务比较熟悉的人来接管主管位置。最后各个组员把任务完成,主管进行汇总,并上报给公司。在团队内部需要制定多个工作流程,来保证工作的有序开展。在分布式系统中同样需要设置这么一个协作规范。zookeeper可以很好的帮助我们来实现这个目的。

二、Zookeeper是什么?

ZooKeeper是一个开放源码的分布式协调服务,由知名互联网公司雅虎创建,是基于Google Chubby开源实现。(Google chubby是google公司开源的一个锁服务。)
ZooKeeper是一个高性能的分布式数据一致性解决方案,它将那些复杂的、容易出错的分布式一致性服务封装起来了,构成了一个高效可靠的源语集,并提供一系列简单易用的接口给用户。ZooKeeper致力于提供一个高性能、高可用、且具有严格的顺序访问控制能力的分布式协调服务。分布式应用可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知,集群管理、Master 选举、分布式锁和分布式队列等功能。Zookeeper知识点:
1、源代码开放开源意味着我们可以免费的获取和使用zk,并且可以深入研究zk的源代码,甚至可以根据自己业务特性和要求进行二次开发修改。
2、是分布式协调服务,它解决分布式数据一致性问题
A、顺序一致性所谓的顺序一致性是指从一个客户端发起一个请求,最终会严格按照发起的顺序应用到zk中。B、原子性原子性是指所有事物请求的处理结果在整个集群的所有机器上的应用情况是一致的。C、单一视图单一视图是指无任客户端连接到哪个zk的服务器,它看到的服务端数据都是一致的。D、可靠性可靠性是指一旦服务端成功的应用了一个事物并完成了对客户端的响应,那么这个事物所引起的服务端状态的变更会一直保留下来,除非有另外一个事物又对它进行了修改。E、实时性实时性是指zk保证在一段时间内客户端一定能从服务端读取最新的数据状态。3、高性能zk具有很高的吞吐量,一个三台服务器的集群可以达到12w-13w的QPS。4、我们可以通过调用zk提供的接口解决一些分布式易用中的实际问题。

三、Zookeeper的典型应用场景

Zookeeper包括但不限于如下应用场景
 
3.1、数据发布/订阅顾名思义就是一方把数据发布出来,另一方通过某种方式可以得到这些数据;通常数据订阅有两种方式:推送模式和拉取模式
推送模式一般是服务器主动向客户端推送信息, 拉取模式是客户端主动去服务器获取数据(通常是采用定时轮询的方式),ZK采用两种方式相结合;发布者将数据发布到ZK集群节点上,订阅者通过一定的方法告诉服务器,我对哪个节点的数据感兴趣,那服务器在这些节点的数据发生变化时,就通知客户端,客户端得到通知后可以去服务器获取数据信息。3.2、负载均衡




实现过程:1、首先DB在启动的时候先把自己在ZK上注册成一个临时节点,ZK的节点后面我们会讲到有两种,一种是永久节点,一类是临时节点临时节点在服务器出现问题的时候,节点会自动的从ZK上删除,那么这样ZK上的服务器列表就是最新的可用的列表。2、客户端在需要读写数据库的时候首先它去ZooKeeper得到所有可用的DB的连接信息(一张列表),得到可用的数据列表。3、客户端随机的算法,随机选择一个与之建立连接,每次会跟不同的数据库连接,就达到简单的复杂均衡。4、当客户端发现连接不可用的时候可再次从ZK上获取可用的DB连接信息,当然也可以在刚获取的那个列表里移除掉不可用的连接后再随机选择一个DB与之连接。3.3、命名服务顾名思义,就是提供名称的服务,例如数据库表格ID,一般用得比较多的有两种ID,一种是自动增长的ID,一种是UUID(9291d71a-0354-4d8e-acd8-64f7393c64ae),两种ID各自都有缺陷,自动增长的ID局限在单库单表中使用,不能在分布式中使用,UUID可以在分布式中使用但是由于ID没有规律难于理解,我们可以借用ZK来生成一个顺序增长的,可以在集群环境下使用的,命名易于理解的ID。3.4、分布式协调/通知心跳检测,在分布式系统中,我们常常需要知道某个机器是否可用,传统的开发中,可以通过Ping某个主机来实现,Ping得通说明对方是可用的,相反是不可用的;
ZK中我们让所有的机其都注册一个临时节点,我们判断一个机器是否可用,我们只需要判断这个节点在ZK中是否存在就可以了,不需要直接去连接需要检查的机器 ,降低系统的复杂度。

四、Zookeeper的优势

[]源代码开放[/][]已经被证实是高性能,易用稳定的工业级产品。[/][]有着广泛的应用:Hadoop,HBase,Storm,Solr。[/]
转载请注明来自开源技术社区 : http://openskill.cn/article/281 查看全部
zookeeper.png

根据如上思维导图,我来展开对Zookeeper的介绍


一、Zookeeper背景


随着互联网技术的高速发展,企业对计算机系统的计算、存储能力要求越来越高,最简单的证明就是出现了一些诸如:高并发,海量存储这样的词汇。在这样的背景下,单纯依靠少量高性能主机来完成计算任务也就不能满足现有大部分企业的需求了,企业的IT架构逐步从集中式向分布式过度,所谓的分布式是指:把一个计算任务分解成若干个计算单元,并且分配到若干不同的计算机中去执行,然后汇总计算结果的过程。
这好比公司里面的某个团队,接到公司派发的任务,首先团队的主管,要把任务进行拆分,然后安排下去,划分给团队中不同的人去完成,并随时跟进任务的进展。如果团队主管离职了,那我们可能就会在团队中挑选一个对业务比较熟悉的人来接管主管位置。最后各个组员把任务完成,主管进行汇总,并上报给公司。在团队内部需要制定多个工作流程,来保证工作的有序开展。在分布式系统中同样需要设置这么一个协作规范。zookeeper可以很好的帮助我们来实现这个目的。


二、Zookeeper是什么?


ZooKeeper是一个开放源码的分布式协调服务,由知名互联网公司雅虎创建,是基于Google Chubby开源实现。(Google chubby是google公司开源的一个锁服务。)
ZooKeeper是一个高性能的分布式数据一致性解决方案,它将那些复杂的、容易出错的分布式一致性服务封装起来了,构成了一个高效可靠的源语集,并提供一系列简单易用的接口给用户。
ZooKeeper致力于提供一个高性能、高可用、且具有严格的顺序访问控制能力的分布式协调服务。分布式应用可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知,集群管理、Master 选举、分布式锁和分布式队列等功能。
Zookeeper知识点:
1、源代码开放
开源意味着我们可以免费的获取和使用zk,并且可以深入研究zk的源代码,甚至可以根据自己业务特性和要求进行二次开发修改。
2、是分布式协调服务,它解决分布式数据一致性问题
A、顺序一致性
所谓的顺序一致性是指从一个客户端发起一个请求,最终会严格按照发起的顺序应用到zk中。
B、原子性
原子性是指所有事物请求的处理结果在整个集群的所有机器上的应用情况是一致的。
C、单一视图
单一视图是指无任客户端连接到哪个zk的服务器,它看到的服务端数据都是一致的。
D、可靠性
可靠性是指一旦服务端成功的应用了一个事物并完成了对客户端的响应,那么这个事物所引起的服务端状态的变更会一直保留下来,除非有另外一个事物又对它进行了修改。
E、实时性
实时性是指zk保证在一段时间内客户端一定能从服务端读取最新的数据状态。
3、高性能
zk具有很高的吞吐量,一个三台服务器的集群可以达到12w-13w的QPS。
4、我们可以通过调用zk提供的接口解决一些分布式易用中的实际问题。


三、Zookeeper的典型应用场景


Zookeeper包括但不限于如下应用场景
 
3.1、数据发布/订阅
顾名思义就是一方把数据发布出来,另一方通过某种方式可以得到这些数据;
通常数据订阅有两种方式:推送模式和拉取模式
推送模式一般是服务器主动向客户端推送信息, 拉取模式是客户端主动去服务器获取数据(通常是采用定时轮询的方式),ZK采用两种方式相结合;
发布者将数据发布到ZK集群节点上,订阅者通过一定的方法告诉服务器,我对哪个节点的数据感兴趣,那服务器在这些节点的数据发生变化时,就通知客户端,客户端得到通知后可以去服务器获取数据信息。
3.2、负载均衡
zk_arch.png

实现过程:
1、首先DB在启动的时候先把自己在ZK上注册成一个临时节点,ZK的节点后面我们会讲到有两种,一种是永久节点,一类是临时节点临时节点在服务器出现问题的时候,节点会自动的从ZK上删除,那么这样ZK上的服务器列表就是最新的可用的列表。
2、客户端在需要读写数据库的时候首先它去ZooKeeper得到所有可用的DB的连接信息(一张列表),得到可用的数据列表。
3、客户端随机的算法,随机选择一个与之建立连接,每次会跟不同的数据库连接,就达到简单的复杂均衡。
4、当客户端发现连接不可用的时候可再次从ZK上获取可用的DB连接信息,当然也可以在刚获取的那个列表里移除掉不可用的连接后再随机选择一个DB与之连接。
3.3、命名服务
顾名思义,就是提供名称的服务,例如数据库表格ID,一般用得比较多的有两种ID,一种是自动增长的ID,一种是UUID(9291d71a-0354-4d8e-acd8-64f7393c64ae),两种ID各自都有缺陷,自动增长的ID局限在单库单表中使用,不能在分布式中使用,UUID可以在分布式中使用但是由于ID没有规律难于理解,我们可以借用ZK来生成一个顺序增长的,可以在集群环境下使用的,命名易于理解的ID。
3.4、分布式协调/通知
心跳检测,在分布式系统中,我们常常需要知道某个机器是否可用,传统的开发中,可以通过Ping某个主机来实现,Ping得通说明对方是可用的,相反是不可用的;
ZK中我们让所有的机其都注册一个临时节点,我们判断一个机器是否可用,我们只需要判断这个节点在ZK中是否存在就可以了,不需要直接去连接需要检查的机器 ,降低系统的复杂度。


四、Zookeeper的优势


    []源代码开放[/][]已经被证实是高性能,易用稳定的工业级产品。[/][]有着广泛的应用:Hadoop,HBase,Storm,Solr。[/]

转载请注明来自开源技术社区http://openskill.cn/article/281

zookeeper启动失败案例分析

大数据/云计算Ansible 发表了文章 • 0 个评论 • 965 次浏览 • 2016-02-24 17:54 • 来自相关话题

启动zookeeper运行结果如下:
[root@zk1 bin]# ./zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED查看状态如下:
[root@zk1 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Error contacting service. It is probably not running.
我netstat -anltup |grep 2018,并没有监听,端口没有占用啊?
 
那只好去看日志了,查看zookeeper的zookeeper.out日志内容如下:
2016-02-24 17:35:22,425 [myid:] - ERROR [main:ZooKeeperServerMain@63] - Unexpected exception, exiting abnormally
java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:344)
at sun.nio.ch.Net.bind(Net.java:336)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:199)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
at org.apache.zookeeper.server.NIOServerCnxnFactory.configure(NIOServerCnxnFactory.java:95)
at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:110)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:86)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:52)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:116)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:78)从日志中"Address already in use"可以看出端口被占用了!
但我用netstat -lntup是没被使用的,再用 lsof -i:2181,发现了2181端口果然被占用了。其实最好的办法是nc -v -z locahost 2181 探测一些本地2181端口是否监听!
[root@zk1 ~]# nc -v -z localhost 2181
Connection to localhost 2181 port [tcp/eforward] succeeded!
[root@zk1 ~]# 好了,端口被占用了,只能修改zk的默认端口,从新启动ok! 查看全部
启动zookeeper运行结果如下:
[root@zk1 bin]# ./zkServer.sh start
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
查看状态如下:
[root@zk1 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Error contacting service. It is probably not running.

我netstat -anltup |grep 2018,并没有监听,端口没有占用啊?
 
那只好去看日志了,查看zookeeper的zookeeper.out日志内容如下:
2016-02-24 17:35:22,425 [myid:] - ERROR [main:ZooKeeperServerMain@63] - Unexpected exception, exiting abnormally
java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:344)
at sun.nio.ch.Net.bind(Net.java:336)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:199)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:67)
at org.apache.zookeeper.server.NIOServerCnxnFactory.configure(NIOServerCnxnFactory.java:95)
at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:110)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:86)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:52)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:116)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:78)
从日志中"Address already in use"可以看出端口被占用了!
但我用netstat -lntup是没被使用的,再用 lsof -i:2181,发现了2181端口果然被占用了。其实最好的办法是nc -v -z locahost 2181 探测一些本地2181端口是否监听!
[root@zk1 ~]# nc -v -z localhost 2181
Connection to localhost 2181 port [tcp/eforward] succeeded!
[root@zk1 ~]#
好了,端口被占用了,只能修改zk的默认端口,从新启动ok!

zookeepr运维经验分享

大数据/云计算采菊篱下 发表了文章 • 0 个评论 • 574 次浏览 • 2015-10-19 23:09 • 来自相关话题

ZooKeeper 是分布式环境下非常重要的一个中间件,可以完成动态配置推送、分布式 Leader 选举、分布式锁等功能。在运维 ZooKeeper 服务的以来,积累如下经验:

集群数量
3台起,如果是虚拟机,必须分散在不同的宿主机上,以实现容灾的目的。如果长远来看(如2-3年)需求会持续增长,可以直接部署5台。ZooKeeper集群扩容是比较麻烦的事情,因此宁可前期稍微浪费一点。

客户端配置域名而不是 IP
如果有一天你的 ZooKeeper 集群需要做机房迁移,或者其中几个节点机器挂了,需要更换。让你的用户更新 ZooKeeper 服务器配置不是件轻松的事情,因此一开始就配置好域名,到时候更新 DNS 即可。

开启 autopurge.snapRetainCount
ZooKeeper 默认不会自动清理 tx log,所以总会有一天你会收到磁盘报警(如果你有磁盘监控的话)。开启自动清理机制后,就不用担心了,我的配置如下:
autopurge.snapRetainCount=500
autopurge.purgeInterval=24

扩容
如果你可以接受停止服务半个小时,那基本随意玩了,但在比较严肃的环境下,还是不能停服务的。我的做法是这样的:
0. 有节点 A, B, C 处于服务状态
server.3=192.168.12.1:2888:3888
server.4=192.168.12.2:2888:3888
server.5=192.168.12.3:2888:38881. 加入节点 D,配置如下:
server.3=192.168.12.1:2888:3888
server.4=192.168.12.2:2888:3888
server.5=192.168.12.3:2888:3888
server.6=192.168.12.4:2888:3888
server.7=192.168.12.5:2888:3888用 4 字命令检查,保证该节点同步完毕集群数据,处于 Follower 状态:
# echo srvr | nc 192.168.12.4 2181
Zookeeper version: 3.4.5-1392090, built on 09/30/2012 17:52 GMT
Latency min/avg/max: 0/0/13432
Received: ***
Sent: ***
Connections: ***
Outstanding: 0
Zxid: 0x***
Mode: follower
Node count: ***需要注意的是,这一步加入的节点的 id,必须大于集群中原有的节点的 id,例如 6 > 3,4,5,我也不知道为什么需要这样。
同上一步一样,加入节点 E更新 A B C 的配置如 D 和 E,并依此重启
机房迁移
例如要把服务从 X 机房的 A B C 迁移到 Y 机房的 A’ B’ C’。
做法是首先把集群扩容成包含6个节点的集群;然后修改域名指向让用户的连接都转到 A’ B’ C’;最后更新集群配置,把 A B C 从集群摘除。

跨机房容灾
由于 ZooKeeper 天生不喜欢偶数(怕脑裂),因此有条件的就三机房部署,但机房之间的网络条件得是类似局域网的条件,否则性能就堪忧了。

双机房做自动容灾基本不可能,加入手动步骤是可以的,和 DB 一样,短时间不可用,立刻启用另外一个机房,平时保证数据同步。

三机房部署,如果一个机房离的比较远,网络延迟较高怎么办?可以 3 + 3 + 1 部署,1 就放在那个网络延迟较高的地方,确保 leader 在 3 + 3 这两个机房中间,那么平时的性能就能保证了。怎么保证 leader 不到 1 呢?目前能想到的办法就是如果发现就重启它 查看全部
ZooKeeper 是分布式环境下非常重要的一个中间件,可以完成动态配置推送、分布式 Leader 选举、分布式锁等功能。在运维 ZooKeeper 服务的以来,积累如下经验:


  1. 集群数量


3台起,如果是虚拟机,必须分散在不同的宿主机上,以实现容灾的目的。如果长远来看(如2-3年)需求会持续增长,可以直接部署5台。ZooKeeper集群扩容是比较麻烦的事情,因此宁可前期稍微浪费一点。


  1. 客户端配置域名而不是 IP


如果有一天你的 ZooKeeper 集群需要做机房迁移,或者其中几个节点机器挂了,需要更换。让你的用户更新 ZooKeeper 服务器配置不是件轻松的事情,因此一开始就配置好域名,到时候更新 DNS 即可。


  1. 开启 autopurge.snapRetainCount


ZooKeeper 默认不会自动清理 tx log,所以总会有一天你会收到磁盘报警(如果你有磁盘监控的话)。开启自动清理机制后,就不用担心了,我的配置如下:
autopurge.snapRetainCount=500
autopurge.purgeInterval=24


  1. 扩容


如果你可以接受停止服务半个小时,那基本随意玩了,但在比较严肃的环境下,还是不能停服务的。我的做法是这样的:
0. 有节点 A, B, C 处于服务状态
server.3=192.168.12.1:2888:3888
server.4=192.168.12.2:2888:3888
server.5=192.168.12.3:2888:3888
1. 加入节点 D,配置如下:
server.3=192.168.12.1:2888:3888
server.4=192.168.12.2:2888:3888
server.5=192.168.12.3:2888:3888
server.6=192.168.12.4:2888:3888
server.7=192.168.12.5:2888:3888
用 4 字命令检查,保证该节点同步完毕集群数据,处于 Follower 状态:
# echo srvr | nc 192.168.12.4 2181
Zookeeper version: 3.4.5-1392090, built on 09/30/2012 17:52 GMT
Latency min/avg/max: 0/0/13432
Received: ***
Sent: ***
Connections: ***
Outstanding: 0
Zxid: 0x***
Mode: follower
Node count: ***
需要注意的是,这一步加入的节点的 id,必须大于集群中原有的节点的 id,例如 6 > 3,4,5,我也不知道为什么需要这样。
  1. 同上一步一样,加入节点 E
  2. 更新 A B C 的配置如 D 和 E,并依此重启


  1. 机房迁移


例如要把服务从 X 机房的 A B C 迁移到 Y 机房的 A’ B’ C’。
做法是首先把集群扩容成包含6个节点的集群;然后修改域名指向让用户的连接都转到 A’ B’ C’;最后更新集群配置,把 A B C 从集群摘除。


  1. 跨机房容灾


由于 ZooKeeper 天生不喜欢偶数(怕脑裂),因此有条件的就三机房部署,但机房之间的网络条件得是类似局域网的条件,否则性能就堪忧了。

双机房做自动容灾基本不可能,加入手动步骤是可以的,和 DB 一样,短时间不可用,立刻启用另外一个机房,平时保证数据同步。

三机房部署,如果一个机房离的比较远,网络延迟较高怎么办?可以 3 + 3 + 1 部署,1 就放在那个网络延迟较高的地方,确保 leader 在 3 + 3 这两个机房中间,那么平时的性能就能保证了。怎么保证 leader 不到 1 呢?目前能想到的办法就是如果发现就重启它