ZooKeeper学习笔记八 ZooKeeper典型应用场景——命名服务

版权声明:(谢厂节的博客)博主文章绝大部分非原创,转载望留链接。 https://blog.csdn.net/xundh/article/details/80167727

《从Paxos到ZooKeeper分布式一致性原理与实践》 电子工业出版社

命名服务是分布式系统中比较常见的一类场景。命名服务是分布式系统最基本的公共服务之一。在分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等——这些我们都可以统称它们为名字(Name),其中较为常见的就是一些分布式服务框架(如RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据指定名字来获取资源的实体、服务地址和提供者的信息等。

Java中的JNDI便是一种典型的命名服务。JNDI是Java命名与目录接口(Java Naming and Irectory Interface)的缩写,是J2EE体系中重要的规范之一,标准的J2EE容器都提供了对JNDI规范的实现。因此,在实际开发中,开发人员常常使用应用服务窗口自带的JNDI实现来完成数据源的配置与管理——使用JNDI方式后,开发人员可以完全不需要关心数据库相关的任何信息,包括数据库类型、JDBC驱动类型及数据库账户等。

ZooKeeper的命名服务有两个应用方向:
1. ZooKeeper提供类似JNDI服务,都能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与实用。
2. 利用ZooKeeper顺序节点的特性,制作分布式的ID生成器。

分布式ID生成器示例:(使用ZkClient)
TestIdMaker

package zookeeper;

import zookeeper.IdMaker.RemoveMethod;

public class TestIdMaker {

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

            IdMaker idMaker = new IdMaker("127.0.0.1:2181",  "/NameService/IdGen", "ID");
            idMaker.start();

            try {
                for (int i = 0; i < 10; i++) {
                    String id = idMaker.generateId(RemoveMethod.DELAY);
                    System.out.println(id);
                }
            } finally {
                idMaker.stop();

            }
        }
}

IdMaker.java

package zookeeper;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;

public class IdMaker {

    private ZkClient client = null;
    private final String server;

    // zookeeper顺序节点的父节点
    private final String root;

    // 顺序节点的名称
    private final String nodeName;

    // 标识当前服务是否正在运行
    private volatile boolean running = false;
    private ExecutorService cleanExector = null;

    public enum RemoveMethod{
        NONE,IMMEDIATELY,DELAY
    }

    public IdMaker(String zkServer,String root,String nodeName){

        this.root = root;
        this.server = zkServer;
        this.nodeName = nodeName;

    }
    // 启动服务
    public void start() throws Exception {

        if (running)
            throw new Exception("server has stated...");
        running = true;

        init();

    }

    // 停止服务
    public void stop() throws Exception {

        if (!running)
            throw new Exception("server has stopped...");
        running = false;

        freeResource();

    }

    // 初始化服务资源
    private void init(){

        client = new ZkClient(server,5000,5000,new BytesPushThroughSerializer());
        cleanExector = Executors.newFixedThreadPool(10);
        try{
            client.createPersistent(root,true);
        }catch (ZkNodeExistsException e){

        }

    }

    // 释放服务器资源
    private void freeResource(){

        // 释放线程池
        cleanExector.shutdown();
        try{
            cleanExector.awaitTermination(2, TimeUnit.SECONDS);
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            cleanExector = null;
        }

        if (client!=null){
            client.close();
            client=null;

        }
    }

    // 检测当前服务是否正在运行
    private void checkRunning() throws Exception {
        if (!running)
            throw new Exception("请先调用start");

    }

    // 从顺序节点名中提取我们要的ID值
    private String ExtractId(String str){
        int index = str.lastIndexOf(nodeName);
        if (index >= 0){
            index+=nodeName.length();
            return index <= str.length()?str.substring(index):"";
        }
        return str;

    }

    // 生成ID
    public String generateId(RemoveMethod removeMethod) throws Exception{
        checkRunning();

        // 构造顺序节点的完整路径
        final String fullNodePath = root.concat("/").concat(nodeName);
        // 创建持久化顺序节点
        final String ourPath = client.createPersistentSequential(fullNodePath, null);

        // 避免zookeeper的顺序节点暴增,直接删除掉刚创建的顺序节点
        if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){ // 立即删除
            client.delete(ourPath);
        }else if (removeMethod.equals(RemoveMethod.DELAY)){ // 延迟删除
            cleanExector.execute(new Runnable() { // 用线程池执行删除,让generateId()方法尽快返回
                public void run() {
                    client.delete(ourPath);
                }
            });
        }
        //node-0000000000, node-0000000001
        return ExtractId(ourPath);
    }
}

代码来源于:
https://segmentfault.com/a/1190000012185902

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页