Curator之recipes之Counters

来源:互联网 发布:java项目管理系统源码 编辑:程序博客网 时间:2024/04/28 16:47

参考:http://curator.apache.org/curator-recipes/shared-counter.html
http://ifeve.com/zookeeper-sharedcount/

分两种,Shared Counter和Distributed Atomic Long

共享计数器Shared Counter

管理一个共享的整型数。所有客户端监听相同的path能够获取共享整型数的最新值。 由zookeeper的一致性保证。

相关的类

  • SharedCount
  • SharedCountReader
  • SharedCountListener
    SharedCount代表计数器, 可以为它增加一个SharedCountListener,当计数器改变时此Listener可以监听到改变的事件,而SharedCountReader可以读取到最新的值, 包括字面值和带版本信息的值VersionedValue。

使用

创建实例

public SharedCount(CuratorFramework client,                   String path,                   int seedValue)Parameters:client - the clientpath - the shared path - i.e. where the shared count is storedseedValue - the initial value for the count if/f the path has not yet been created

其中,path为共享值存储的路径 。seedValue为初始值。

启动

调用 start()方法启动

count.start();

关闭

调用 close()方法关闭

count.close();

调用setCount()强制修改值

public void setCount(int newCount)Change the shared count value irrespective of its previous state

调用trySetCount()也可以设置新值 ,只有当上一次读的值 没有修改时,修改才会成功。如果值已经改变了,修改不成功,但可以通过getCount()读取最新值。

public boolean trySetCount(int newCount)Changes the shared count only if its value has not changed since this client last read it. If the counthas changed, the value is not set and this client's view of the value is updated. i.e. if the count isnot successful you can get the updated value by calling getCount().Parameters:newCount - the new value to attemptReturns:true if the change attempt was successful, false if not. If the change was not successful, getCount()will return the updated value

错误处理

SharedCountListener 继承ConnectionStateListener。 当SharedCount启动时,Curator客户端上会添加该listener。 使用ShareCount时必须时刻注意连接变化。
如果出现 SUSPENDED连接,实例必须了解count值并不准备并且也不能被更新直接重新建立连接。如果LOST连接,count值将会保持不变。

代码示例

package counter;import java.io.IOException;import java.util.List;import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.framework.recipes.shared.SharedCount;import org.apache.curator.framework.recipes.shared.SharedCountListener;import org.apache.curator.framework.recipes.shared.SharedCountReader;import org.apache.curator.framework.state.ConnectionState;import org.apache.curator.retry.ExponentialBackoffRetry;import org.apache.curator.test.TestingServer;import com.google.common.collect.Lists;public class SharedCounterExample implements SharedCountListener {    private static final int QTY = 5;    private static final String PATH = "/examples/counter";    public static void main(String[] args) throws IOException, Exception {        final Random rand = new Random();        SharedCounterExample example = new SharedCounterExample();        try (TestingServer server = new TestingServer()) {            CuratorFramework client = CuratorFrameworkFactory.newClient(server                    .getConnectString(), new ExponentialBackoffRetry(1000, 3));            client.start();            SharedCount baseCount = new SharedCount(client, PATH, 0);            baseCount.addListener(example);            baseCount.start();            List<SharedCount> examples = Lists.newArrayList();            ExecutorService service = Executors.newFixedThreadPool(QTY);            for (int i = 0; i < QTY; ++i) {                final SharedCount count = new SharedCount(client, PATH, 0);                examples.add(count);                Callable<Void> task = new Callable<Void>() {                    @Override                    public Void call() throws Exception {                        count.start();                        Thread.sleep(rand.nextInt(10000));                        System.out.println("Increment:"                                + count.trySetCount(count.getVersionedValue(),                                        count.getCount() + rand.nextInt(10)));                        return null;                    }                };                service.submit(task);            }            service.shutdown();            service.awaitTermination(10, TimeUnit.MINUTES);            for (int i = 0; i < QTY; ++i) {                examples.get(i).close();            }            baseCount.close();        }    }    @Override    public void stateChanged(CuratorFramework arg0, ConnectionState arg1) {        System.out.println("State changed: " + arg1.toString());    }    @Override    public void countHasChanged(SharedCountReader sharedCount, int newCount)            throws Exception {        System.out.println("Counter's value is changed to " + newCount);    }}

在这个例子中,我们使用baseCount来监听计数值(addListener方法)。 任意的SharedCount, 只要使用相同的path,都可以得到这个计数值。 然后我们使用5个线程为计数值增加一个10以内的随机数。
通过

count.trySetCount(count.getVersionedValue(), count.getCount() + rand.nextInt(10))

这里我们使用trySetCount去设置计数器。 第一个参数提供当前的VersionedValue,如果期间其它client更新了此计数值, 你的更新可能不成功, 但是这时你的client更新了最新的值,所以失败了你可以尝试再更新一次。 而setCount是强制更新计数器的值。
注意计数器必须start,使用完之后必须调用close关闭它。

在这里再重复一遍前面讲到的, 强烈推荐监控ConnectionStateListener, 在本例中SharedCountListener扩展了ConnectionStateListener。 这一条针对所有的Curator recipes都适用

Distributed Atomic Long

Long类型计数型,每一次增长是原子性的。第一次修改值是使用乐观锁,如果失败,会使用InterProcessMutex共享可重入锁。有两种模式,乐观模式和互斥模式,在两种模式下,会使用重试策略试图使其值 增长 。

相关的类

  • DistributedAtomicLong
  • AtomicValue
  • PromotedToLock

使用方法

创建实例

只使用乐观模式,即不会使用InterProcessMutex。

public DistributedAtomicLong(CuratorFramework client,                                String counterPath,                                RetryPolicy retryPolicy)Creates the counter in optimistic mode only - i.e. the promotion to a mutex is not doneParameters:client - the clientcounterPath - path to hold the counter valueretryPolicy - the retry policy to use

互斥模式。即第一次增量时使用乐观锁,如果失败,会尝试使用InterProcessMutex共享可重入锁。

public DistributedAtomicLong(CuratorFramework client,                                String counterPath,                                RetryPolicy retryPolicy,                                PromotedToLock promotedToLock)Creates the counter in mutex promotion mode. The optimistic lock will be tried first usingthe given retry policy. If the increment does not succeed, a InterProcessMutex will betried with its own retry policyParameters:client - the clientcounterPath - path to hold the counter valueretryPolicy - the retry policy to usepromotedToLock - the arguments for the mutex promotion

可通过trySet()方法看出其内部实现

    AtomicValue<byte[]>   trySet(MakeValue makeValue) throws Exception    {        MutableAtomicValue<byte[]>  result = new MutableAtomicValue<byte[]>(null, null, false);        tryOptimistic(result, makeValue);        if ( !result.succeeded() && (mutex != null) )        {            tryWithMutex(result, makeValue);        }        return result;    }

基本操作

  1. 计数器上的操作

    • get() :获取当前值
    • increment() :加1
    • decrement():减1
    • add(): 增加特定的值
    • subtract(): 减去特定的值
  2. 检查结果

    • 操作返回时,必须首先检查succeeded()是否返回true。如果返回false,说明操作失败,该值没被更新。
    • 操作成功时,可通过preValue()获取修改之前的值,可通过postValue()获取操作后的值。

错误处理

每调用一次方法时,所有的原子实例都会连接zookeeper服务器。因此,会使用标准的重试机制,并且,每一次操作导致的错误都会抛出异常。

代码示例

package counter;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.framework.recipes.atomic.AtomicValue;import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;import org.apache.curator.retry.ExponentialBackoffRetry;import org.apache.curator.retry.RetryNTimes;import org.apache.curator.test.TestingServer;import com.google.common.collect.Lists;public class DistributedAtomicLongExample {private static final int QTY=5;private static final String PATH="/examples/counter";public static void main(String[] args) throws Exception{    try(TestingServer  server=new TestingServer ()){        CuratorFramework client=CuratorFrameworkFactory.newClient(server.getConnectString(), new ExponentialBackoffRetry(1000,3));        client.start();        List<DistributedAtomicLong> examples=Lists.newArrayList();        ExecutorService service=Executors.newFixedThreadPool(QTY);        for(int i=0;i<QTY;i++){            final DistributedAtomicLong count=new DistributedAtomicLong(client,PATH,new RetryNTimes(10,10));            examples.add(count);            Callable<Void> task=new Callable<Void>(){                public Void call() throws Exception{                    try{                        AtomicValue<Long> value=count.increment();                        System.out.println("succeed:"+value.succeeded());                        if(value.succeeded()){                            System.out.println("Increment : from "+value.preValue()+"  to "+value.postValue());                        }                    }catch(Exception e){                        e.printStackTrace();                    }                    return null;                }            };            service.submit(task);        }        service.shutdown();        service.awaitTermination(10,TimeUnit.MINUTES);    }}}

使用五个线程对计数器进行加一操作,如果成功,则将操作前后的值 打印
结果如下:

succeed:trueIncrement : from 0  to 1succeed:trueIncrement : from 1  to 2succeed:trueIncrement : from 2  to 3succeed:trueIncrement : from 3  to 4succeed:trueIncrement : from 4  to 5
0 0
原创粉丝点击