这一篇文章我们将学习使用Curator来实现计数器。顾名思义,计数器是用来计数的,利用ZooKeeper可以实现一个集群共享的计数器。只要使用相同的path就可以得到最新的计数器值,这是由ZooKeeper的一致性保证的。Curator有两个计数器,一个是用int来计数,一个用long来计数。

1.SharedCount

1.SharedCount计数器介绍
这个类使用int类型来计数。 主要涉及三个类。
  • SharedCount - 管理一个共享的整数。所有看同样的路径客户端将有共享的整数(考虑ZK的正常一致性保证)的最高最新的值。
  • SharedCountReader - 一个共享的整数接口,并允许监听改变它的值。
  • SharedCountListener - 用于监听共享整数发生变化的监听器。
SharedCount类代表计数器,可以为它增加一个SharedCountListener,当计数器改变时此Listener可以监听到改变的事件,而SharedCountReader可以读取到最新的值,包括字面值和带版本信息的值VersionedValue。
注意:使用SharedCount之前需要调用start(),使用完成之后需要调用stop()
2.编写示例程序
  1. public class SharedCounterExample implements SharedCountListener
  2. {
  3. private static final int QTY = 5;
  4. private static final String PATH = "/examples/counter";

  5. public static void main(String[] args) throws IOException, Exception
  6. {
  7. final Random rand = new Random();
  8. SharedCounterExample example = new SharedCounterExample();
  9. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  10. client.start();
  11. SharedCount baseCount = new SharedCount(client, PATH, 0);
  12. baseCount.addListener(example);
  13. baseCount.start();
  14. List<SharedCount> examples = Lists.newArrayList();
  15. ExecutorService service = Executors.newFixedThreadPool(QTY);
  16. for (int i = 0; i < QTY; ++i)
  17. {
  18. final SharedCount count = new SharedCount(client, PATH, 0);
  19. examples.add(count);
  20. Callable<Void> task = new Callable<Void>()
  21. {
  22. @Override
  23. public Void call() throws Exception
  24. {
  25. count.start();
  26. Thread.sleep(rand.nextInt(10000));
  27. count.setCount(rand.nextInt(10000));
  28. System.out.println("计数器当前值:" + count.getVersionedValue().getValue());
  29. System.out.println("计数器当前版本:" + count.getVersionedValue().getVersion());
  30. System.out.println("trySetCount:" + count.trySetCount(count.getVersionedValue(), 123));
  31. return null;
  32. }
  33. };
  34. service.submit(task);
  35. }
  36. service.shutdown();
  37. service.awaitTermination(10, TimeUnit.MINUTES);
  38. for (int i = 0; i < QTY; ++i)
  39. {
  40. examples.get(i).close();
  41. }
  42. baseCount.close();
  43. client.close();
  44. System.out.println("OK!");
  45. }
  46. @Override
  47. public void stateChanged(CuratorFramework client, ConnectionState newState)
  48. {
  49. System.out.println("连接状态: " + newState.toString());
  50. }
  51. @Override
  52. public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception
  53. {
  54. System.out.println("计数器值改变:" + newCount);
  55. }
  56. }
在这个例子中,我们使用baseCount来监听计数值(addListener方法)。任意的SharedCount,只要使用相同的PATH,都可以得到这个计数值。然后我们使用5个线程为计数值增加一个10以内的随机数。
这里我们使用trySetCount去设置计数器。第一个参数提供当前的VersionedValue,如果期间其它client更新了此计数值,你的更新可能不成功,但是这时你的client更新了最新的值,所以失败了你可以尝试再更新一次。而setCount是强制更新计数器的值。
注意:计数器必须start,使用完之后必须调用close关闭它。
在这里再重复一遍前面讲到的, 强烈推荐你监控ConnectionStateListener, 尽管我们的有些例子没有监控它。 在本例中SharedCountListener扩展了ConnectionStateListener。 这一条针对所有的Curator recipes都适用,后面的文章中就不专门提示了。
3.示例程序运行结果
    运行结果控制台:
  1. 连接状态: CONNECTED
  2. 计数器当前值:1684
  3. 计数器当前版本:11
  4. trySetCount:true
  5. 计数器值改变:123
  6. 计数器当前值:8425
  7. 计数器当前版本:13
  8. trySetCount:true
  9. 计数器值改变:123
  10. 计数器当前值:9369
  11. 计数器当前版本:15
  12. trySetCount:true
  13. 计数器值改变:123
  14. 计数器当前值:4075
  15. 计数器当前版本:17
  16. trySetCount:true
  17. 计数器值改变:123
  18. 计数器当前值:9221
  19. 计数器当前版本:19
  20. trySetCount:true
  21. OK!
    Zookeeper节点信息如下:

2.DistributedAtomicLong

    再看一个Long类型的计数器。除了计数的范围比SharedCount大了之外,它首先尝试使用乐观锁的方式设置计数器,如果不成功(比如期间计数器已经被其它client更新了),它使用InterProcessMutex方式来更新计数值。还记得InterProcessMutex是什么吗?它是我们前面讲的分布式可重入锁。这和上面的计数器的实现有显著的不同。
1.DistributedAtomicLong计数器介绍
DistributedAtomicLong计数器和上面的计数器的实现有显著的不同,可以从它的内部实现DistributedAtomicValue.trySet中看出端倪。
  1. public class DistributedAtomicLong implements DistributedAtomicNumber<Long>
  2. {
  3. private final DistributedAtomicValue value;
  4. ......
  5. }
  6. public class DistributedAtomicValue
  7. {
  8. ......
  9. AtomicValue<byte[]> trySet(MakeValue makeValue) throws Exception
  10. {
  11. MutableAtomicValue<byte[]> result = new MutableAtomicValue<byte[]>(null, null, false);
  12. tryOptimistic(result, makeValue);
  13. if ( !result.succeeded() && (mutex != null) )
  14. {
  15. tryWithMutex(result, makeValue);
  16. }
  17. return result;
  18. }
  19. ......
  20. }
此计数器有一系列的操作:
  • get(): 获取当前值
  • increment(): 加一
  • decrement(): 减一
  • add(): 增加特定的值
  • subtract(): 减去特定的值
  • trySet(): 尝试设置计数值
  • forceSet(): 强制设置计数值
你必须检查返回结果的succeeded(),它代表此操作是否成功。如果操作成功,preValue()代表操作前的值,postValue()代表操作后的值。
2.编写示例程序
我们下面的例子中使用5个线程对计数器进行加一操作,如果成功,将操作前后的值打印出来。
  1. public class DistributedAtomicLongExample
  2. {
  3. private static final int QTY = 5;
  4. private static final String PATH = "/examples/counter";
  5. public static void main(String[] args) throws IOException, Exception
  6. {
  7. final Random rand = new Random();
  8. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
  9. client.start();
  10. List<DistributedAtomicLong> examples = Lists.newArrayList();
  11. ExecutorService service = Executors.newFixedThreadPool(QTY);
  12. for (int i = 0; i < QTY; ++i)
  13. {
  14. final DistributedAtomicLong count = new DistributedAtomicLong(client, PATH, new RetryNTimes(10, 10));
  15. examples.add(count);
  16. Callable<Void> task = new Callable<Void>()
  17. {
  18. @Override
  19. public Void call() throws Exception
  20. {
  21. try
  22. {
  23. Thread.sleep(1000 + rand.nextInt(10000));
  24. AtomicValue<Long> value = count.increment();
  25. System.out.println("修改成功: " + value.succeeded());
  26. if (value.succeeded())
  27. {
  28. System.out.println("修改之前的值:" + value.preValue() + " | 修改之后的值:" + value.postValue());
  29. }
  30. }
  31. catch (Exception e)
  32. {
  33. e.printStackTrace();
  34. }
  35. return null;
  36. }
  37. };
  38. service.submit(task);
  39. }
  40. service.shutdown();
  41. service.awaitTermination(10, TimeUnit.MINUTES);
  42. client.close();
  43. System.out.println("OK!");
  44. }
  45. }
注意:你必须检查返回结果的succeeded(),它代表此操作是否成功。如果操作成功,preValue()代表操作前的值,postValue()代表操作后的值。
3.示例程序运行结果
    运行结果控制台:
  1. 修改成功: true
  2. 修改之前的值:0 | 修改之后的值:1
  3. 修改成功: true
  4. 修改之前的值:1 | 修改之后的值:2
  5. 修改成功: true
  6. 修改之前的值:2 | 修改之后的值:3
  7. 修改成功: true
  8. 修改之前的值:3 | 修改之后的值:4
  9. 修改成功: true
  10. 修改之前的值:4 | 修改之后的值:5
  11. OK!
    Zookeeper节点信息如下:

-------------------------------------------------------------------------------------------------------------------------------

07.Curator计数器的更多相关文章

  1. zookeeper之分布式锁以及分布式计数器(通过curator框架实现)

    有人可能会问zookeeper我知道,但是curator是什么呢? 其实curator是apachede针对zookeeper开发的一个api框架是apache的顶级项目 他与zookeeper原生a ...

  2. PHP学习之[第07讲]PHP5.4 文件操作函数 之 图片计数器的实例

    1.filetype():输出文件类型: 2.stat():获取文件的基本属性的数组: 3.clearstatcache().is_executable().isDir().idFile().scan ...

  3. Zookeeper开源客户端框架Curator简介

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  4. Zookeeper开源客户端框架Curator简介[转]

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  5. Python学习笔记——基础篇2【第三周】——计数器、有序字典、元组、单(双)向队列、深浅拷贝、函数、装饰器

    目录 1.Python计数器Counter 2.Python有序字典OrderredDict 3.Python默认字典default 4.python可命名元组namedtuple 5.Python双 ...

  6. zookeeper(2)-curator

    一.Curator介绍 zookeeper的提交人也说过,curator对于zookeeper而言就像是guava对于java差不多,更加优雅高效. 而且之前的zookeeper原生API,往往因为2 ...

  7. 15. 使用Apache Curator管理ZooKeeper

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  8. Zookeeper客户端Curator基本API

    在使用zookeper的时候一般不使用原生的API,Curator,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连.反复注册Watcher和NodeExistsExceptio ...

  9. MapReduce 计数器简介

    转自:http://my.oschina.net/leejun2005/blog/276891?utm_source=tuicool&utm_medium=referral 1.计数器 简介 ...

随机推荐

  1. 关于Cocos2d-x事件处理机制

    事件处理步骤: 1.创建一个触摸事件监听器(单点触摸或多点触摸) 2.实现触摸事件的响应方法 3.添加事件监听器(场景优先或固定值优先) 4.当用户触摸时,事件分发器就会将事件分发给监听器进行响应 首 ...

  2. am335x phy led problem

    问题描述 连接网线的情况下,每次进行软件"reboot",网口的LINK LED能够正常的熄灭,而ACTIVE LED却是亮的. reboot重启之后,LINK的灯正常变亮,而AC ...

  3. 安装 oracle [转]

    先下载3个东西:链接忘记了,大家自己找一下 1  ORA+11+G+R2+server+64bit+for+windows.iso  (Oracle 安装文件) 2  PLSql 3  oracle6 ...

  4. jQuery分页插件(jquery.page.js)的使用

    效果描述: 不用分页即可显示的jQuery插件 jQuery分页插件——jQuery.page.js用法很简单,效果很棒   1.前端   首先html的head中引入相关css与js <lin ...

  5. 解析oracle的rownum

    本人最近在使用oracle的rownum实现分页显示的时候,对rownum做了进一步的分析和研究.现归纳如下,希望能给大家带来收获. 对于rownum来说它是oracle系统顺序分配为从查询返回的行的 ...

  6. VC++ 打开文件或文件夹对话框的实现方法

    实际工作开发中,由于各种应用,我们需要调用系统的打开文件对话框或者打开文件夹对话框,或两者兼有.特总结了常用的实现方法,仅供开发参考. 1. 打开文件对话框 常用的方法是使用系统的CFileDialo ...

  7. php 用命令行导出和导入MySQL数据库

    命令行导出数据库:1,进入MySQL目录下的bin文件夹:cd MySQL中到bin文件夹的目录如我输入的命令行:cd C:\Program Files\MySQL\MySQL Server 4.1\ ...

  8. linux常用命令-tar,scp,du

    tar 打包排除指定目录 tar -zcvf afish.tar.gz * --exclude=file1 --exclude=dir1 排除目录注意: 1.--exclude=file1 而不是 - ...

  9. fiddler抓包,搞定接口

    上篇介绍的世纪佳缘登录是由已有cookie保持登录状态的.世纪佳缘登陆不需要填入验证码,可以很方便直接请求登录接口来达到登录状态的目的. 这篇介绍直接从登录接口进行登录,那么这就要求要找到登录接口ur ...

  10. 记XX2013届优秀毕业生评选(请重视在公司展现自己,重视业绩參评过程,非技术贴)

    本文不是什么技术贴.仅仅是作为一名码农,在公司发展中遇到"參评"中的一个分享,希望对大家有帮助.毕竟,升职加薪这样的事情,你须要自己去争取,须要获得领导和同事的认可.. . .考虑 ...