Apache Curator简单使用(二)

Zookeeper 专栏收录该内容
12 篇文章 0 订阅
转载自:http://www.chengxuyuans.com/Java+/72042.html

              http://ifeve.com/zookeeper-sharedcount/

分布式队列Queue

       分布式队列的基本特性,就是"生产者"或"消费者"跨越多个进程,且在此种环境中需要确保队列的push/poll的有序性。zookeeper本身并没有提供分布式队列的实现,只是recipse根据zookeeper的watcher和具有version标记的node,来间接的实现分布式queue。
       内部机制如下:
       如果是消费者(QueueConsumer),会创建一个类似于PathChildrenCache的实例用于监听queuePath下的子节点变更事件(单独的线程中).同时consumer处于阻塞状态,当有子节点变更事件时会被唤醒(包括创建子节点/删除子节点等);此时consumer获取子节点列表,并将每个节点信息封装成Runnable任务单元,提交到线程池中,Runnable中执行QueueConsumer.consumer()方法.
       如果是生产者,则发布一个message时recipes将会在queuePath下创建一个PERSISTENT_SEQUENTIAL节点,同时保存message数据。消费时,也将按照节点的顺序进行。发布消息并没有太多的问题仅仅是创建一个"有序"节点即可。但是对于消费者,那么需要考虑的因数就很多,比如:多个消费者同时消费时,需要确保消息不能重复且有序;消息消费时,如果网络异常,怎么办?
       对于QistributedQueue中,对上述问题的解决办法也非常粗糙,内部机制如下:
       如果使用了消费担保(即指定了lockPath),在调用consumer方法之前,首先创建一个临时节点(lockPath + 子节点),如果创建此临时节点失败也就意味着此消息被其他消费者,则忽略此消息。然后从子节点中获取数据,如果获取失败,意味着此节点已经被其他消费者删除,则忽略此消息。然后调用consumer()方法,如果此方法抛出异常,消息将会再次添加到队列中(删除旧的子节点,创建一个新的子节点)。如果消费正常,则删除节点。无论成败,则删除临时节点(lockPath + 子节点)。
       如果没有使用消费担保,则首先获取子节点的数据(getData),然后立即删除此子节点,调用consumer()方法。
       需要明确使用zookeeper作为分布式队列的场景: 1)队列深度较小;2)生产者和消费者的速度都非常的低且消费者消费速度更快,即单位时间内产生的消息很少;3)建议只有一个消费者。

       DistributedQueue是最普通的一种队列。 它设计以下四个类:QueueBuilder、QueueConsumer、QueueSerializer、DistributedQueue。
import org.apache.curator.framework.recipes.queue.QueueSerializer;
import java.nio.charset.Charset;

public class StringQueueSerializer implements QueueSerializer<String> {
    private static final Charset charset = Charset.forName("utf-8");

    //as producer
    @Override
    public byte[] serialize(String item) {
        return item.getBytes(charset);
    }

    //as consumer
    @Override
    public String deserialize(byte[] bytes) {
        return new String(bytes, charset);
    }
}
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.queue.QueueConsumer;
import org.apache.curator.framework.state.ConnectionState;
import static org.apache.curator.framework.state.ConnectionState.RECONNECTED;

public class DistributedQueueConsumer implements QueueConsumer<String> {
    @Override
    public void consumeMessage(String message) throws Exception {
        System.out.println("Consumer:" + message);
    }

    @Override
    public void stateChanged(CuratorFramework client, ConnectionState newState) {
        switch (newState) {
            case RECONNECTED: //当链接重建之后
                try {
                    System.out.println(RECONNECTED);
                } catch (Exception e) {
                }
                break;
            default:
                System.out.println(newState.toString());
        }
    }
}
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.queue.DistributedQueue;
import org.apache.curator.framework.recipes.queue.QueueBuilder;
import org.apache.curator.framework.recipes.queue.QueueConsumer;
import org.apache.curator.utils.CloseableUtils;

public class DistributedQueueProducer {
    private static CuratorFramework client;
    private static String queuePath;

    public static void main(String[] args) {
        QueueConsumer<String> consumer = new DistributedQueueConsumer();

        DistributedQueue<String> queue = QueueBuilder.builder(client,
                consumer,
                new StringQueueSerializer(),
                queuePath)
                .lockPath("queue-lock")//消费担保
                //.maxItems(1024);// 有界队列,最大队列深度,如果深度达到此值,将阻塞"生产者"创建新的节点.
                .buildQueue();

        try {
            queue.start();
            queue.put("test");
            Thread.sleep((long) (3 * Math.random()));
        } catch (Exception e) {
        } finally {
            CloseableUtils.closeQuietly(queue);
            CloseableUtils.closeQuietly(client);
        }
    }
}
       Recipse还提供了其他API( http://ifeve.com/zookeeper%EF%BC%8Dcurator/):
       1.DistributedIdQueue: 内部基于DistributedQueue的所有机制,只是除了指定queue中消息的内容之外,还可以指定一个ID,这个ID作为消息的标记,最终此ID值将作为znode的path后缀.此后可以通过ID去消费(dequeue)一个消息.队列的排序方式是根据ID的字典顺序--正序;
       2.DistributedProrityQueue: 有权重的队列,对队列中的元素按照优先级进行排序,在发布消息时,需要指定此消息的权重数字; priority越小,元素越靠前,越先被消费掉。
       3. DistributedDelayQueue:JDK中也有DelayQueue,DistributedDelayQueue也提供了类似的功能,元素有个delay值, 消费者隔一段时间才能收到元素。
       在DistributedQueue的开发中,必须在QueueConsumer中关注"链接失效"的事件.

计数器Counter

       利用ZooKeeper可以实现一个集群共享的计数器。 只要使用相同的path就可以得到最新的计数器值, 这是由ZooKeeper的一致性保证的。Curator有两个计数器,一个是用int来计数,一个用long来计数。
       SharedCount使用int类型来计数。 主要涉及三个类。SharedCount、SharedCountReader、SharedCountListener。计数器改变时此Listener可以监听到改变的事件,而SharedCountReader可以读取到最新的值,包括字面值和带版本信息的值VersionedValue。
import org.apache.curator.framework.CuratorFramework;
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;

public class SharedCountTest {
    private static CuratorFramework client;
    private static String countPath;

    public static void main(String[] args) throws Exception {
        final SharedCount baseCount = new SharedCount(client, countPath, 0);
        baseCount.addListener(new SharedCountListener() {
            @Override
            public void countHasChanged(SharedCountReader sharedCount, int newCount) throws Exception {
                System.out.println("count changed:" + newCount);
            }

            @Override
            public void stateChanged(CuratorFramework client, ConnectionState newState) {
                switch (newState) {
                    case RECONNECTED: //当链接重建之后,需要手动fresh
                        try {
                            Integer current = baseCount.getCount();
                            //reflush,无论更新成败,都会获取最新的值
                            baseCount.trySetCount(baseCount.getVersionedValue(), current);
                        } catch (Exception e) {
                        }
                        break;
                    default:
                        System.out.println(newState.toString());
                }
            }
        });

        //test,任意的SharedCount,只要使用相同的path,都可以得到计数值
        final SharedCount count = new SharedCount(client, countPath, 0);
        count.start();
        count.trySetCount(count.getVersionedValue(), count.getCount() + 5);
        //trySetCount尝试设置计数器,setCount是强制更新计数器的值
        count.close();

        baseCount.start();
        //counter.close();//取消watcher
    }
}
       在"计数器"中,还提供了DistributedAtomicInteger,DistributedAtomicLong两个分布式自增计数器。
  • 0
    点赞
  • 1
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p style="color:#666666;"> <span style="font-size:14px;">本门课程重实战,将基础知识拆解到项目里,让你在项目情境里学知识。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">这样的学习方式能让你保持兴趣、充满动力,时刻知道学的东西能用在哪、能怎么用。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">平时不明白的知识点,放在项目里去理解就恍然大悟了。</span> </p> <p style="color:#666666;"> <span></span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>一、融汇贯通</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">本视频采用了前后端分离的开发模式,前端使用Vue.js+Element UI实现了Web页面的呈现,后端使用Python 的Django框架实现了数据访问的接口,前端通过Axios访问后端接口获得数据。在学习完本章节后,真正理解前后端的各自承担的工作。</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>、贴近实战</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">本系列课程为练手项目实战:学生管理系统v4.0的开发,项目包含了如下几个内容:项目的总体介绍、基本功能的演示、Vuejs的初始化、Element UI的使用、在Django中实现针对数据的增删改查的接口、在Vuejs中实现前端增删改查的调用、实现文件的上传、实现表格的分页、实现导出数据到Excel、实现通过Excel导入数据、实现针对表格的批量化操作等等,所有的功能都通过演示完成、贴近了实战</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>三、课程亮点</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">在本案例中,最大的亮点在于前后端做了分离,真正理解前后端的各自承担的工作。前端如何和后端交互</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="color:#FF0000;font-size:14px;"><strong>适合人群:</strong></span> </p> <p style="color:#666666;"> <span style="font-size:14px;">1、有Python语言基础、web前端基础,想要深入学习Python Web框架的朋友;</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">2、有Django基础,但是想学习企业级项目实战的朋友;</span> </p> <p style="color:#666666;"> <span style="font-size:14px;">3、有MySQL数据库基础的朋友</span> </p> <p style="color:#666666;"> <span style="font-size:14px;"> </span> </p> <p style="color:#666666;"> <span style="font-size:14px;"><img alt="" src="https://img-bss.csdnimg.cn/202009070752197496.png" /><br /> </span> </p> <p style="color:#666666;"> <span style="font-size:14px;"><br /> </span> </p>
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值