Curator应用场景分析之zk实现分布式全局计数器

Curator实现分布式全局计数器

单机环境下,多线程之间如何实现线程安全自增(计数)的实现方法及原理,那么如果是在分布式环境中呢?自然synchronized,lock,atomicInteger等基于Java的方法不能满足,因为这些都只能在当前JVM环境中生效,而分布式环境中多个JVM实例是很正常的事

下面介绍Curator基于Zookeeper实现的分布式计数器

Curator recipes包下实现了DistributedAtomicInteger,DistributedAtomicLong等分布式原子自增计数器

简单应用

public class CuratorAtomicInteger {
    /** zookeeper地址 */
    static final String CONNECT_ADDR = "172.16.158.11:2181,"
            + "172.16.158.12:2181,"
            + "172.16.158.13:2181";
    /** session超时时间 */
    static final int SESSION_OUTTIME = 5000;//ms

    public static void main(String[] args) throws Exception {

        //1 重试策略:初试时间为1s 重试10次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //2 通过工厂创建连接
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString(CONNECT_ADDR)
                .sessionTimeoutMs(SESSION_OUTTIME)
                .retryPolicy(retryPolicy)
                .build();
        //3 开启连接
        cf.start();
        //cf.delete().forPath("/super");


        //4 使用DistributedAtomicInteger
        DistributedAtomicInteger atomicIntger =
                new DistributedAtomicInteger(cf, "/super", new RetryNTimes(3, 1000));

        AtomicValue<Integer> value = atomicIntger.add(1);
        System.out.println(value.succeeded());
        System.out.println(value.postValue());	//最新值
        System.out.println(value.preValue());	//原始值

    }
}

结果正常返回,没有任何问题

高级应用(多线程环境下)

那么在多线程情况下,是否正确呢?有待验证

public class CuratorAtomicInteger2 {
    static CountDownLatch countDownLatch = new CountDownLatch(10);

    public static void main(String[] args) throws Exception {
        CuratorFramework zkClient = getZkClient();
		//指定计数器存放路径 及重试策略
        DistributedAtomicInteger distributedAtomicInteger = new DistributedAtomicInteger(zkClient, "/counter", new ExponentialBackoffRetry(1000, 3));
        //多线程自增10*100次
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    try {
                    	//调用add方法自增
                        AtomicValue<Integer> result = distributedAtomicInteger.add(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                countDownLatch.countDown();

            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //查看结果
        System.out.println("多线程自增结果" + distributedAtomicInteger.get().postValue());
    }

    private static CuratorFramework getZkClient() {
        String zkServerAddress = "172.16.158.11:2181,"
            + "172.16.158.12:2181,"
            + "172.16.158.13:2181";
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
        CuratorFramework zkClient = CuratorFrameworkFactory.builder()
                .connectString(zkServerAddress)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();
        zkClient.start();
        return zkClient;
    }

}

按道理说,自增完后,结果是不是应该是10*100?,道友们运行几次后会发现实际值不全是1000。为什么呢?这玩意是假的?
我们看下AtomicValue result = distributedAtomicInteger.add(1)这行代码中,add()方法源码

/**
     * Add delta to the current value and return the new value information. Remember to always
     * check {@link AtomicValue#succeeded()}.
     *
     * @param delta amount to add
     * @return value info
     * @throws Exception ZooKeeper errors
     */
    @Override
    public AtomicValue<Integer>    add(Integer delta) throws Exception
    {
        return worker(delta);
    }

其中写道 Remember to always check {@link AtomicValue#succeeded()}. 也就是说,这个方法的自增是不一定会成功的,在前面初始化分布式机器数对象时,传入了重试策略,如果分布式环境中出现了并发自增的情况,会不断重试,如果重试后还失败,则结果返回失败

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值