背景

前段时间学习了zookeeper后,在新的项目中刚好派上了用场,我在项目中主要负责分布式任务调度模块的开发,对我自己来说是个不小的挑战。

分布式的任务调度,技术上我们选择了zookeeper,具体的整个分布式任务调度的架构选择会另起一篇文章进行介绍。

本文主要是介绍自己在项目中zookeeper的一些扩展使用,希望可以对大家有所帮助。

项目中使用的zookeeper版本3.3.3,对应的文档地址: http://zookeeper.apache.org/doc/trunk/

扩展一:优先集群

先来点背景知识:

1.zookeeper中的server机器之间会组成leader/follower集群,1:n的关系。采用了paxos一致性算法保证了数据的一致性,就是leader/follower会采用通讯的方式进行投票来实现paxns。

2.zookeeper还支持一种observer模式,提供只读服务不参与投票,提升系统,对应文档: http://zookeeper.apache.org/doc/trunk/zookeeperObservers.html

我们项目特性的决定了我们需要进行跨机房操作,比如杭州,美国,香港,青岛等多个机房之间进行数据交互。

跨机房之间对应的网络延迟都比较大,比如中美机房走海底光缆有ping操作200ms的延迟,杭州和青岛机房有70ms的延迟。

为了提升系统的网络性能,我们在部署zookeeper网络时会在每个机房部署节点,多个机房之间再组成一个大的网络保证数据一致性。(zookeeper千万别再搞多个集群)

最后的部署结构就会是:

  • 杭州机房  >=3台 (构建leader/follower的zk集群)
  • 青岛机房  >=1台 (构建observer的zk集群)
  • 美国机房  >=1台 (构建observer的zk集群)
  • 香港机房  >=1台 (构建observer的zk集群)


 
一句话概括就是: 在单个机房内组成一个投票集群,外围的机房都会是一个observer集群和投票集群进行数据交互。 这样部署的一些好处,大家可以细细体会一下
 
针对这样的部署结构,我们会引入一个优先集群问题: 比如在美国机房的机器需要优先去访问本机房的zk集群,访问不到后才去访问杭州机房。 
默认在zookeeper3.3.3的实现中,认为所有的节点都是对等的。并没有对应的优先集群的概念,单个机器也没有对应的优先级的概念。
 
扩展代码:(比较暴力,采用反射的方式改变了zk client的集群列表)
  • 先使用美国机房的集群ip初始化一次zk client
  • 通过反射方式,强制在初始化后的zk client中的server列表中又加入杭州机房的机器列表
  1. 1.ZooKeeper zk = null;
  2. 2. try {
  3. 3. zk = new ZooKeeper(cluster1, sessionTimeout, new AsyncWatcher() {
  4. 4.
  5. 5. public void asyncProcess(WatchedEvent event) {
  6. 6. //do nothing
  7. 7. }
  8. 8.
  9. 9. });
  10. 10. if (serveraddrs.size() > 1) {
  11. 11. // 强制的声明accessible
  12. 12. ReflectionUtils.makeAccessible(clientCnxnField);
  13. 13. ReflectionUtils.makeAccessible(serverAddrsField);
  14. 14. // 添加第二组集群列表
  15. 15. for (int i = 1; i < serveraddrs.size(); i++) {
  16. 16. String cluster = serveraddrs.get(i);
  17. 17. // 强制获取zk中的地址信息
  18. 18. ClientCnxn cnxn = (ClientCnxn) ReflectionUtils.getField(clientCnxnField, zk);
  19. 19. List<InetSocketAddress> serverAddrs = (List<InetSocketAddress>) ReflectionUtils
  20. 20. .getField(serverAddrsField, cnxn);
  21. 21. // 添加第二组集群列表
  22. 22. serverAddrs.addAll(buildServerAddrs(cluster));
  23. 23. }
  24. 24. }
  25. 25. }

扩展二:异步Watcher处理

最早在看zookeeper的代码时,一直对它的watcher处理比较满意,使用watcher推送数据可以很方便的实现分布式锁的功能。

zookeeper的watcher实现原理也挺简单的,就是在zookeeper client和zookeeper server上都保存一份对应的watcher对象。每个zookeeper机器都会有一份完整的node tree数据和watcher数据,每次leader通知follower/observer数据发生变更后,每个zookeeper server会根据自己节点中的watcher事件推送给响应的zookeeper client,每个zk client收到后再根据内存中的watcher引用,进行回调。

这里会有个问题,就是zk client在处理watcher时,回凋的过程是一个串行的执行过程,所以单个watcher的处理慢会影响整个列表的响应。

可以看一下ClientCnxn类中的EventThread处理,该线程会定时消费一个queue的数据,挨个调用processEvent(Object event) 进行回调处理。

扩展代码:

  1. 1.public abstract class AsyncWatcher implements Watcher {
  2. 2.
  3. 3. private static final int DEFAULT_POOL_SIZE = 30;
  4. 4. private static final int DEFAULT_ACCEPT_COUNT = 60;
  5. 5.
  6. 6. private static ExecutorService executor = new ThreadPoolExecutor(
  7. 7. 1,
  8. 8. DEFAULT_POOL_SIZE,
  9. 9. 0L,
  10. 10. TimeUnit.MILLISECONDS,
  11. 11. new ArrayBlockingQueue(
  12. 12. DEFAULT_ACCEPT_COUNT),
  13. 13. new NamedThreadFactory(
  14. 14. "Arbitrate-Async-Watcher"),
  15. 15. new ThreadPoolExecutor.CallerRunsPolicy());
  16. 16.
  17. 17. public void process(final WatchedEvent event) {
  18. 18. executor.execute(new Runnable() {//提交异步处理
  19. 19.
  20. 20. @Override
  21. 21. public void run() {
  22. 22. asyncProcess(event);
  23. 23. }
  24. 24. });
  25. 25.
  26. 26. }
  27. 27.
  28. 28. public abstract void asyncProcess(WatchedEvent event);
  29. 29.
  30. 30.}
说明:
  • zookeeper针对watcher的调用是以单线程串行的方式进行处理,容易造成堵塞影响,monitor的数据同步及时性
  • AsyncWatcher为采取的一种策略为当不超过acceptCount=60的任务时,会采用异步线程的方式处理。如果超过60任务,会变为原先的单线程串行的模式

扩展三:重试处理

这个也不多说啥,看一下相关文档就清楚了

需要特殊处理下ConnectionLoss的异常,一种可恢复的异常。
 
重试处理:

  1. 1.public interface ZooKeeperOperation<T> {
  2. 2.
  3. 3. public T execute() throws KeeperException, InterruptedException;
  4. 4.}
  5. 5.
  6. 6.
  7. 7./**
  8. 8. * 包装重试策略
  9. 9. */
  10. 10. public <T> T retryOperation(ZooKeeperOperation<T> operation) throws KeeperException,
  11. 11. InterruptedException {
  12. 12. KeeperException exception = null;
  13. 13. for (int i = 0; i < maxRetry; i++) {
  14. 14. try {
  15. 15. return (T) operation.execute();
  16. 16. } catch (KeeperException.SessionExpiredException e) {
  17. 17. logger.warn("Session expired for: " + this + " so reconnecting due to: " + e, e);
  18. 18. throw e;
  19. 19. } catch (KeeperException.ConnectionLossException e) { //特殊处理Connection Loss
  20. 20. if (exception == null) {
  21. 21. exception = e;
  22. 22. }
  23. 23. logger.warn("Attempt " + i + " failed with connection loss so "
  24. 24. + "attempting to reconnect: " + e, e);
  25. 25.
  26. 26. retryDelay(i);
  27. 27. }
  28. 28. }
  29. 29.
  30. 30. throw exception;
  31. 31. }

注意点:Watcher原子性

在使用zookeeper的过程中,需要特别注意一点就是注册对应watcher事件时,如果当前的节点已经满足了条件,比如exist的watcher,它不会触发你的watcher,而会等待下一次watcher条件的满足。

它的watcher是一个一次性的监听,而不是一个永久的订阅过程。所以在watcher响应和再次注册watcher过程并不是一个原子操作,编写多线程代码和锁时需要特别注意

总结

zookeepr是一个挺不错的产品,源代码写的也非常不错,大量使用了queue和异步Thread的处理模式,真是一个伟大的产品。

云栖社区站内文章,如需转载,请保留作者和出处(云栖社区),并邮件通知云栖社区(yqeditor@list.alibaba-inc.com)。

zookeeper项目使用几点小结的更多相关文章

  1. 使用javac编译zookeeper项目

    这里记录zookeeper编译源代码上的一些细节的问题. 网上不少关于如何使用ant eclipse来构建zookeeper对应的eclipse工程的记录.这里就不再过多赘述.只做简单阐述. 这里主要 ...

  2. springmvc 项目完整示例03 小结

    利用spring 创建一个web项目 大致原理 利用spring的ioc 原理,例子中也就是体现在了配置文件中 设置了自动扫描注解 配置了数据库信息等 一般一个项目,主要有domain,dao,ser ...

  3. 第一个dubbo+zookeeper项目例子

    公司项目要用这两个东西,于是打算学习它. 首先我的理解dubbo是什么?zookeeper是什么?为什要这么搞. 项目分层: 传统的,mvc -->垂直架构(将模块抽取成单独项目,项目互相调用) ...

  4. maven dubbo zookeeper 项目搭建(有效)jar包非war测试

    zookeeper安装以及dubbo-admin.war(管理端)配置启动,本章省略,参考其他内容 这里主要说服务提供者和消费者 项目结构: 1)服务端 DemoServer.java package ...

  5. .Net 项目代码风格要求小结

    代码风格没有正确与否,重要的是整齐划一,这是我拟的一份<.Net 项目代码风格要求>,供大家参考. 1. C# 代码风格要求1.1注释 类型.属性.事件.方法.方法参数,根据需要添加注释. ...

  6. git文件夹下项目更改ip地址小结

    在我们开发的过程中,经常切换项目IP地址是很正常的,之前弄过一次,没有记住,现在简单的总结下: 找到要切换IP地址的项目,点击鼠标右键,弹出下图: 打开该项目的路径后,双击打开该项目,具体参考自己项目 ...

  7. Zookeeper项目开发环境搭建(Eclipse\MyEclipse + Maven)

    写在前面的话 可详细参考,一定得去看 HBase 开发环境搭建(Eclipse\MyEclipse + Maven) 我这里,相信,能看此博客的朋友,想必是有一定基础的了.我前期写了大量的基础性博文. ...

  8. eclipse maven jdk1.8 还原站点项目红感叹号总是小结

    问题背景有三 maven 默认是jdk1.5jdk1.8 目录文件夹不全操作: 在项目上右击-> build path-->config build path-->libraries ...

  9. 【Zookeeper学习】Apache Zookeeper项目简介

    正在撰写,稍后来访……

随机推荐

  1. XML 配置里的 Bean 自动装配

    在XML文件中,先看一下下面的代码: <bean id="student" class="com.jeremy.spring.beans.student" ...

  2. 解决IOS7在TableView 被导航栏挡住的BUG!!

    self.edgesForExtendedLayout = UIRectEdgeNone; 就这么简单!

  3. C程序编译过程浅析(转)

    前几天看了<程序员的自我修养——链接.装载与库>中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧. 我现在一般都是用gcc,所以自然以GCC编译hellworld ...

  4. 数字货币量化分析报告_20170905_P

    [分析时间]2017-09-05 16:36:46 [数据来源]中国比特币 https://www.chbtc.com/ef4101d7dd4f1faf4af825035564dd81聚币网 http ...

  5. 剑指Offer——树的子结构

    题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 分析: 先匹配到A的某个结点和B的根相同,然后往下继续匹配.不匹配则递归匹配左右子树. 代码: ...

  6. perl 常用命令

    过滤重复字符 perl -e '$_=<STDIN>; @in = split; if (@in < 100){ @out = grep {! $hash{$_}++ } @in;  ...

  7. Python 最难的问题

    Python 最难的问题 超过十年以上,没有比解释器全局锁(GIL)让Python新手和专家更有挫折感或者更有好奇心. 未解决的问题 随处都是问题.难度大.耗时多肯定是其中一个问题.仅仅是尝试解决这个 ...

  8. PLSQLDeveloper安装与配置

    1.前提:首先要有oracle数据库或者有oracle服务器,才可以实现使用PLSQL Developer 工具连接到oracle数据库进行开发 2.下载PLSQLDeveloper并解压 3.配置环 ...

  9. debian flam3 源码

    https://packages.debian.org/source/jessie/flam3 Source Package: flam3 (3.0.1-3) Links for flam3 Debi ...

  10. zen-cart 一页支付实现方法

    1.下载插件CSS JS Loader 和 Fast and Easy Checkout for Zen Cart,插件请下载附件 2.先把CSS JS Loader覆盖,后台选项点击,点击后,程序会 ...