第二部分 使用zookeeper进行开发

开始使用api

  • 会话创建:不要自己尝试去管理zookeeper客户端连接。zookeeper客户端库会监控与服务器之间的连接,客户端不仅告诉我们连接发生什么问题,还会主动尝试重新建立通信。
  • telent命令中的stat和dump字符串可以查看服务器的信息。

  • 创建znode节点

    • 同步创建:create(String path,byte[] data,List<ACL> acl,CreateMode mode ) 注意超时时间一般为5-10秒
    • 异步创建:create(String path,byte[] data,List<ACL> acl,CreateMode mode,AsyncCallback cb,Object ctx) 提供回调方法,指定上下文信息

处理状态变化

  • 单次触发是否会丢失事件

    • 丢失事件通常并不是问题,因为任何在接受通知与注册新监视点之间的变化情况,均可以通过读取Zookeeper的状态信息来获得。
  • WatchedEvent数据结构:

    • Zookeeper会话状态(KeepState):Disconnected、SyncConnected、AuthFailed、ConnectedReadonly、SaslAuthenticated和Expired
    • 事件类型(EventType):NodeCreated、NodeDeleted、NodeDataChanged、NodeChildrenChanged和None
    • 如果事件类型不是None时,返回一个znode路径
  • watcher通用模型: 1.进行调用异步 2.实现回调对象,并传入异步调用函数中 3.如果操作需需要设置监视点,实现一个Wathcer对象,并传入异步调用函数中

主-从模式的例子

  • 管理权变化

    • 连接丢失的情况下,客户端会检查/master节点是否存在;
    • 返回ok,则形使领导权;
    • 如果其他节点已经创建了这个节点,则客户端需要监视该节点;
    • 如果发生了某些未知,记录错误日志;
    • 通过exists调用在/master节点上设置监视点;
    • 如果/master节点删除,那么再次竞选主节点。
  • 主节点等待从节点列表的变化

    • 观察/workers的子节点变化
    • 当检测到子节点变化,需要重新分配崩溃从节点的任务,并重新设置新的从节点列表
  • 主节点等待新任务进行分配

    • 在任务列表变化时,监视点获得任务列表
    • 分配列表中的任务,获得任务信息
    • 随机选择一个从节点,分配任务给这个从节点
    • 创建分配节点,路径形式为/assign/worker-id/task-num
    • 删除/tasks下对应的任务节点
  • 从节点等待分配新任务

    • 在assign创建/assign/worker-id节点,先注册该节点是为了防止主节点给从节点分配任务
    • 然后创建一个znode节点来注册从节点/workers/worker-id
    • 监控/assign/worker-id的子节点,获得变化通知后,获取子节点的列表,
    • 通过线程池,循环子节点,获得任务信息并执行任务,将正在执行的任务添加到执行列表中,防止多次执行
  • 客户端等待任务的执行结果

    • 创建提交任务节点/tasks/task-id
    • 针对任务id,监控其/status/task-id的状态
    • 若监控节点存在,获取执行结果
  • 执行过程
    • 启动Master程序,执行runForMaster()创建/master节点,若创建成功,则该客户端为主节点,则执行主节点任务,第一,监控从节点,通过getWorkers()监视/workers下的子节点。第二,分配任务,通过getTasks()监控/tasks下的子节点,若发现有任务提交,则随机将其分配可用的从节点,;若创建失败则作为master的备份客户端,同时通过masterExists()监控/master的状态,一旦/master节点消失,客户端收到通知就运行runForMaster();master创建成功后,初始化创建/workers、/assign、/tasks、/status节点;
    • 启动第一个Worker程序,首先在可分配任务/assign目录下创建节点/assign/worker-id,再在/workers目录下注册/workers/worker-id节点,成功后需要监控/assign/worker-id子节点,该子节点表示master分配给worker的任务,一旦监控到就需要执行该任务

multiop

multiop可以原子性地执行多个Zookeeper的操作,执行过程为原子性,即在multiop代码块中的所有操作要不全部成功,要不全部失败。 特性:

  • 1.创建一个op对象,该对象表示你想通过multiop方法执行的每个zookeeper操作,Zookeeper提供了每个改变状态操作的Op对象的实现:create、delete和setData。
  • 2.通过Op对象中提供的一个静态方法调用进行操作。
  • 3.将Op对象添加到Java的Iterable类型对象中,如列表(list)。
  • 4.使用列表对象调用multi方法。

实现原理,有待后续分析......

监视点的羊群效应

  • 如果有10000个客户端通过exists操作监视这个Znode节点,那么当Znode节点创建后就会发送10000个通知,因而被监视的Znode节点的一个变化会产生一个尖峰的通知。
  • 解决方法(并不普遍):为了获取锁,一个进程试着创建/lock节点,如果节点存在就获取锁,如果不存在就删除该节点,当删除节点时所有监视/lock节点的客户端收到通知。
  • 另一个方法让客户端创建一个有序的节点/lock/lock-,zk会自动为其增加序列号xxx,我们可以使用这个序列号来确定那个客户端获得锁,通过判断/lock下的所有创建的子节点的最小序列号。如果客户端创建的节点不是最小序列号,机会根据序列确定序列,并在前一个节点上设置监视点。

故障处理

  • 故障发生的主要三个点:
    • Zookeeper服务
    • 网络
    • 应用程序
  • 可恢复的故障:

    • Disconnected事件和ConnectionLossException异常来表示自己无法了解当前的系统状态
    • zk客户端会积极地尝试重新连接到另一个ZooKeeper服务,直到最终重新建立了会话。
    • leader-election中,当进程收到Disconnected事件时,在重新连接之前,进程需要挂起群首的操作
    • 很长的延时与过期,开发者自己控制会话的重启或关闭
  • 不可恢复的故障

    • 会话过期或者已认证的会话无法在与zk完成认证,这两种情况zk都会丢弃会话的状态。
    • 最简单的恢复的方法是中止进程并重启,这样可以使进程恢复原状,通过一个新的会话初始化自己的状态。
  • 群首选举和外部资源

    • 问题:由于客户端机器发生更过载等影响性能的情况,那么一方面zk会无法及时与zk服务器发送心跳信息,导致zk的会话超时,另一方面,客户端其他线程任然觉得持有主节点,并与外部资源进行通信
    • 解决:一个方法是不要让你的机器在超载或时钟偏移的环境下运行,另一个方法是通过zk扩展对外部资源设备的协作的数据,使用隔离(fencing)的技巧,来确保分布式环境中的独占访问。
    • 例子:stat结构中的czxid,表示创建节点时的zxid,zxid为唯一的单调递增的序列号,可以作为一个隔离符号。

Zookeeper注意事项

  • ACl控制

    • 权限
      • CREATE: 能创建子节点
      • READ:能获取节点数据和列出其子节点
      • WRITE: 能设置节点数据
      • DELETE: 能删除子节点
      • ADMIN: 能设置权限
    • scheme
      • world:它下面只有一个id, 叫anyone,zookeeper中对所有人有权限的结点就是属于world
      • auth:它不需要id, 只要是通过authentication的user都有权限
      • digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
      • ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段
      • super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情
      • sasl: sasl的对应的id,是一个通过sasl authentication用户的id
    • 实例参考
  • 恢复会话 有待进一步研究。。。。

  • sync方法:异步调用 sync方法实际上并不会影响Zookeeper,当服务端处理sync调用时,服务端会刷新群首与调用sync操作的客户端c所连接的服务端之间的通信,刷新的意思是说在调用getData的返回数据的时候,服务端确保返回所有客户端c调用sync方法时所有可能的变化情况。 没怎么看明白。。。。

  • 顺序性保障 正常情况下zk声明对一个会话中所有客户端操作提供顺序性保障,但是可能存在意外情况:

    • 连接丢失时的顺序性:在连接丢失的情况下,应用程序可以依赖客户端库来解决所有后续操作,而不能依赖zk来承担这些操作。
    • 同步API和多线程的顺序性:多线程情况下,客户端需要注意这些操作的提交顺序
    • 同步和异步混合调用的顺序性
  • 数据字段和子节点的限制

    • zk默认情况下对数据字段的传输限制为1MB
    • 同时也限制了任何父节点可以拥有的子节点数

results matching ""

    No results matching ""