• 分布式协调组件Zookeeper


    分布式架构与集中式架构:

      集中式架构:就是把所有的程序,功能,模块,都集中在一个模块中,部署在一台服务器上,从而对外提供服务.(单体架构,单体服务,单体应用),基本上,一个war包闯天下.

      分步式架构:就是把所有的程序,功能,模块,拆分成不同的子项目,部署在多台不同的服务器上,这些子项目协同合作,共同对外提供服务.

    分布式的难题:

    随着社会的发展,单体应用已经不能满足人们对于大流量,高并发的需求,现在大型互联网项目基本都是分布式架构,但是分布式因此也带来了如下难题.

      1:如何合理的拆分系统,基于什么粒度?

      2拆分后各个子系统之间如何通信

      3:如何适应不断增长的业务需求,使其具有良好的扩展性?

      4:如何保证各个子系统的可靠性和数据一致性.

    由于分布式的复杂性,各个环节出现了很多优秀的开源解决方案,进行分布式架构就需要用到很多分布式架构.

    分布式应用程序协调服务:Zookeeper

      Zookeeper是什么?

        Zookeeper是一个开源的分布式应用程序协调服务,也是一个服务器.是Google的Chubby的一个开源实现,是Hadoop和Hbase的重要组件,在分布式领域应用广泛.

      Zookeeper可以做什么?

       配置管理:在上面存储项目的配置信息

       命名服务:服务名与ip的映射信息存在上面

       分布式锁:多个并发访问的协调

       集群管理:服务器的宕机感知与处理

    Zookeeper的运行环境:

      官网:http://zookeeper.apache.org

      下载地址:https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.6.1/apache-zookeeper-3.6.1-bin.tar  

         安装:直接解压缩即可  tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz  -C /usr/local

         配置:需要java运行环境,在Zookeeper的conf目录下:cp zoo_sample.cfg zoo.cfg,zookeeper启动时会读取该文件作为默认配置文件

      配置文件详解:

        tickTimeZookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,单位毫秒

        initLimit集群中的follower服务器与leader服务器之间初始连接时能容忍的最多心跳数(tickTime的数量)

        syncLimit集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)

        dataDir存储zookeeper的快照文件、pid文件,默认为/tmp/zookeeper,建议在zookeeper安装目录下创建data目录,将dataDir配置改为/usr/local/zookeeper-3.6.1/data

        clientPort客户端连接zookeeper的端口,即zookeeper对外的服务端口,默认为2181

      启动:(zk的bin目录下) ./zkServer.sh start

      关闭:(zk的bin目录下) ./zkServer.sh stop

      查看状态:(zk的bin目录下) ./zkServer.sh status

      常见命令:  ./zkServer.sh {start|start-foreground|stop|restart|status|print-cmd}

    Zookeeper的数据结构:

      Zookeeper的数据结构类似属性结构,每个节点称之为znode,可以存储信息,可以进行增删改查,可以增加子节点.

      节点分为四种类型:

        PERSISTENT 持久化目录节点           客户端与Zookeeper断开连接后,该节点依旧存在

        PERSITENT_SEQUENTIAL 持久化顺序目录节点 客户端与Zookeeper断开连接后,节点依旧存在,只是Zookeeper给该节点名称顺序编号

        EPHEMERAL 临时目录节点

        EPHEMERAL_SEQUENTIAL 临时顺序编号目录节点

     Zookeeper客户端

      图形界面客户端

        Zookeeper图形客户端工具下载地址:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

        下载解压即可使用,进入ZooInspectoruildzookeeper-dev-Zooinspector.jar 使用java -jar启动,需要java运行环境,运行时需耐性等待.

      命令行客户端

        使用zkCli连接Zookeeper , 默认直接连127.0.0.1:2181 ./zkCli.sh 命令行客户端主要通过命令对zookeeper进行操作,一般都是增删改查,输入无效命令会给出提示信息.

      查询:

         ls /path  列出path节点下的节点 例: ls /   列出根节点下节点

        get /path 查看节点数据   

        stat /path查看节点状态

      cZxid:节点创建时的zxid

      ctime:节点创建时间

      mZxid:节点最近一次更新时的zxid

      mtime:节点最近一次更新的时间

      pZxid:操作当前节点的子节点列表的事务id(这种操作包含增加子节点,删除子节点)

      cversion:子节点数据更新次数

      dataVersion:本节点数据更新次数

      aclVersion:节点ACL(授权信息)的更新次数

      ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id,如果该节点不是临时节点,ephemeralOwner值为0

      dataLength:节点数据长度

      numChildren:子节点个数

      增加

        create [-s] [-e] path data acl

        其中 -s 表示顺序节点,-e表示临时节点  -s 和 -e 可以同时结合使用 临时节点,当客户端会话结束后,临时节点会被删除;顺序节点特性可用于生成在分布式环境下的主键生成器;

        create /root 123456 创建节点并且指定数据;

        close 命令关闭当前会话;

        quit 命令退出连接的客户端命令行;

      修改

        set path data [version]

        比如:set /node1 998

      删除

        delete path [version]

        删除指定节点数据;注意:delete只能删除不包含子节点的节点,如果要删除的节点包含子节点,使用deleteall命令;

    Java客户端

      Zookeeper服务器有三种客户端

        Zookeeper:Zookeeper官方提供的原生java客户端;API文档: https://zookeeper.apache.org/doc/current/api/index.html

        ZkClient:在原生Zookeeper基础上进行扩展的开源第三方Java客户端;Github:https://github.com/sgroschupf/zkclient

        Curator:Netflix公司在原生Zookeeper基础上扩展的开源第三方客户端;官网:http://curator.apache.org Netflixhttps://github.com/Netflix/curator

     Zookeeper原生客户端操作:

      创建Maven项目,引入依赖:

      <!--zookeeper的官方客户端jar包依赖-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.5.5</version>
            </dependency>

    API实例代码

    package com.blue.client;
    
    import java.util.List;
    import java.util.concurrent.CountDownLatch;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.Watcher.Event.EventType;
    import org.apache.zookeeper.Watcher.Event.KeeperState;
    import org.apache.zookeeper.ZooDefs;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.Stat;
    
    
    public class ZookeeperClient01 {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String ROOT_NODE = "/root";
    
        private static CountDownLatch countDownLatch = new CountDownLatch(1);
    
    
        public static void main(String[] args) throws Exception {
    
            System.out.println("创建开始1");
    
            ZooKeeper zooKeeper = new ZooKeeper(ZK_ADDR, 3000, watchedEvent -> {
                //获取事件状态
                KeeperState state = watchedEvent.getState();
                //获取事件类型
                EventType type = watchedEvent.getType();
                //如果连接建立
                System.out.println("创建开始2");
                if (KeeperState.SyncConnected == state) {
                    if (EventType.None == type) {
                        System.out.println("连接成功");
                        countDownLatch.countDown();
                        System.out.println("创建开始3");
                    }
                }
            });
            System.out.println("创建开始4");
            countDownLatch.await();
            System.out.println("创建开始5");
    
            //创建节点:   /root ,数据:Zookeeper      开放的访问控制策略   持久化节点
            String nodePath = zooKeeper
                    .create(ROOT_NODE, "zookeeper".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                            CreateMode.PERSISTENT);
            System.out.println(nodePath);
    
            String nodePath1 = zooKeeper
                    .create(ROOT_NODE + "/home", "zookeeper-home".getBytes(), Ids.OPEN_ACL_UNSAFE,
                            CreateMode.PERSISTENT);
            System.out.println(nodePath1);
    
            //获取数据
            byte[] data = zooKeeper.getData(ROOT_NODE, false, null);
            System.out.println(new String(data));
    
            //修改数据
            Stat stat = zooKeeper.setData(ROOT_NODE, "修改数据了".getBytes(), -1);
            System.out.println(stat);
    
            //删除节点  -1标识任何版本
            zooKeeper.delete(ROOT_NODE, -1);
            Stat exists = zooKeeper.exists(ROOT_NODE, false);
            System.out.println(exists);
    
            //获取子节点
            List<String> list = zooKeeper.getChildren(ROOT_NODE, false);
            list.forEach(o -> System.out.println("遍历1:" + o.toString()));
    
    
        }
    
    
    }
    View Code

    ZkClient客户端操作:

     创建Maven项目,引入依赖:

       <!--zkclient客户端的jar包依赖-->
            <dependency>
                <groupId>com.101tec</groupId>
                <artifactId>zkclient</artifactId>
                <version>0.11</version>
            </dependency>

    API示例代码

    package com.blue.client;
    
    import java.util.List;
    import org.I0Itec.zkclient.ZkClient;
    import org.I0Itec.zkclient.exception.ZkMarshallingError;
    import org.I0Itec.zkclient.serialize.ZkSerializer;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;
    
    public class ZookeeperClient02 {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String ROOT_NODE = "/root";
    
        public static void main(String[] args) {
            //建立连接
            ZkClient client = new ZkClient(ZK_ADDR, 3000, 30000, new ZkSerializer() {
                @Override
                public byte[] serialize(Object o) throws ZkMarshallingError {
                    return o.toString().getBytes();
                }
    
                @Override
                public Object deserialize(byte[] bytes) throws ZkMarshallingError {
                    return new String(bytes);
                }
            });
    
            //创建持久化顺序节点
            client.create(ROOT_NODE, "this is test zk", CreateMode.PERSISTENT);
            client.create(ROOT_NODE + "/home", "this is test zk", CreateMode.PERSISTENT);
            //读取节点数据
            String nodeData = client.readData(ROOT_NODE);
            System.out.println(nodeData);
    
            //读取节点数据返回节点状态信息
            Stat test_home_node = client.writeDataReturnStat(ROOT_NODE + "/home", "test home node", -1);
            System.out.println(test_home_node);
    
            //查询节点是否存在?
            boolean exists = client.exists(ROOT_NODE);
            System.out.println(ROOT_NODE + "节点是否存在?->" + exists);
    
            //更新节点信息
            client.writeData(ROOT_NODE, "this is new Data");
            Object newData = client.readData(ROOT_NODE);
            System.out.println("节点更新数据是:" + newData);
    
            //删除节点
            boolean delete = false;
            List<String> children = client.getChildren(ROOT_NODE);
            if (children.size() > 0) {
                //递归删除当前节点和所有子节点
                delete = client.deleteRecursive(ROOT_NODE);
            } else {
                //删除当前节点,若有子节点报错
                delete = client.delete(ROOT_NODE);
            }
            System.out.println("删除节点是否成功?->" + delete);
    
        }
    }
    View Code

    Curator客户端

      创建Maven项目,引入依赖

      <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>4.2.0</version>
            </dependency>

    Curator客户端的监听  官网:http://curator.apache.org 

    Curator采用多种cache机制实现监听,简单来说,cache在客户端缓存了znode的各种状态,当监听了Zookeeper的znode状态变化,将触发event事件,注册的监听器会处理这些事件

    Curator支持Path CacheNode CacheTree Cache 三种cache类型;

    Path Cache

    Path Cache用来观察ZNode的子节点并缓存状态,如果ZNode的子节点被创建、更新或删除,那么Path Cache会触发事件给注册的监听器;

    Path Cache是通过PathChildrenCache类实现事件监听,监听器注册接口为PathChildrenCacheListener;

    Node Cache

    Node Cache用来观察ZNode自身,如果ZNode节点本身被创建、更新或删除,那么Node Cache触发事件给注册的监听器;

    Node Cache是通过NodeCache类来实现,监听器注册接口为NodeCacheListener;

    Tree Cache

    Tree Cache用来观察所有节点和所有数据的变化,监听器注册接口为TreeCacheListener;

    API示例代码

    package com.blue.client;
    
    import java.util.List;
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.api.CuratorEventType;
    import org.apache.curator.framework.imps.CuratorFrameworkState;
    import org.apache.curator.framework.recipes.cache.ChildData;
    import org.apache.curator.framework.recipes.cache.NodeCache;
    import org.apache.curator.framework.recipes.cache.PathChildrenCache;
    import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
    import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type;
    import org.apache.curator.framework.recipes.cache.TreeCache;
    import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
    import org.apache.curator.retry.RetryNTimes;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.data.Stat;
    
    public class ZookeeperClient03 {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String ROOT_NODE = "/root";
    
        public CuratorFramework client = null;
    
        //连接zk
        public ZookeeperClient03() {
            RetryPolicy retryPolicy = new RetryNTimes(3, 3000);
            //新版本
            //         client = CuratorFrameworkFactory.newClient(ZK_ADDR, retryPolicy);
            //老版本
            client = CuratorFrameworkFactory.builder().connectString(ZK_ADDR).sessionTimeoutMs(3000)
                    .connectionTimeoutMs(8000).retryPolicy(retryPolicy).build();
            //启动客户端
            client.start();
        }
    
        public static void main(String[] args) throws Exception {
            ZookeeperClient03 client03 = new ZookeeperClient03();
            System.out.println("Zookeeper连接成功:" + client03.client);
            CuratorFrameworkState state = client03.client.getState();
            System.out.println("Zookeeper连接状态信息:" + state);
    
            //创建节点 可以创建多层级节点信息
            String node = client03.client.create().creatingParentContainersIfNeeded()
                    .withMode(CreateMode.PERSISTENT)
                    .forPath(ROOT_NODE + "/home", "curator create".getBytes());
    
            System.out.println("创建节点返回节点路径信息:" + node);
    
            //读取节点数据
            byte[] bytes = client03.client.getData().forPath(ROOT_NODE + "/home");
            System.out.println("查询节点返回信息:" + new String(bytes));
    
            //查询子节点
            List<String> stringList = client03.client.getChildren().forPath(ROOT_NODE);
            stringList.forEach(str -> System.out.println("查询获取子节点:" + str));
    
            //监听节点事件,一次性监听------------------------------------------------------
            client03.client.getData().usingWatcher((Watcher) event -> {
                System.out.println("对节点" + event.getPath() + "进行了操作,操作事件:" + event.getType().name());
                System.out.println("catcher:" + Thread.currentThread().getId());
            }).forPath(ROOT_NODE);
    
            client03.client.create().inBackground((client, event) -> {
                //创建回调
                if (event.getType() == CuratorEventType.CREATE) {
                    System.out.println("监听到了创建:" + event.getResultCode());
                } else if (event.getType() == CuratorEventType.DELETE) {
                    System.out.println("监听到了删除:" + event.getResultCode());
                }
            }).forPath(ROOT_NODE);
    
            //监听节点,对具体节点进行监听,可以一直监听---------------------------------
            NodeCache nodeCache = new NodeCache(client03.client, ROOT_NODE);
            nodeCache.start(true);
            if (nodeCache.getCurrentData() != null) {
                ChildData childData = nodeCache.getCurrentData();
                byte[] data = childData.getData();
                System.out.println("持续监听缓存节点数据" + new String(data));
            } else {
                System.out.println("缓存节点数据是空的");
            }
            nodeCache.getListenable().addListener(() -> {
                System.out.println("监听到事件了........");
                ChildData childData2 = nodeCache.getCurrentData();
                byte[] bytes12 = childData2.getData();
                System.out.println("缓存节点数据1:" + new String(bytes12));
                System.out.println("缓存节点1:" + nodeCache.getPath());
            });
    
            //子节点监听----------------------------------------------------
            PathChildrenCache pathChildrenCache = new PathChildrenCache(client03.client, ROOT_NODE,
                    true);
            // 启动模式:POST_INITIALIZED_EVENT模式才可以监听
            pathChildrenCache.start(StartMode.POST_INITIALIZED_EVENT);
    
            pathChildrenCache.getListenable().addListener((client, event) -> {
                System.out.println("子节点监执行....");
                byte[] data = event.getData().getData();
                System.out.println("子节点监听数据" + new String(data));
                Type type = event.getType();
                System.out.println("子节点监听类型:" + type.name());
            });
    
            //对指定节点下的所有节点进行监听-------------------------------------
            TreeCache treeCache = new TreeCache(client03.client, ROOT_NODE);
            treeCache.start();
            //添加监听器
    
            treeCache.getListenable().addListener((client, event) -> {
                System.out.println("指定节点下所有子节点监听:..........");
                byte[] data = event.getData().getData();
                System.out.println("监听指定节点下所有子节点数据" + new String(data));
    
                TreeCacheEvent.Type type = event.getType();
                System.out.println("监听指定节点下所有子节点类型:" + type.name());
    
            });
    
            // 更新节点信息
            Stat stat = client03.client.setData().withVersion(-1)
                    .forPath(ROOT_NODE, "update curator zookeeper client".getBytes());
            System.out.println("更新节点信息" + stat);
    
            Stat stat1 = client03.client.setData().withVersion(-1)
                    .forPath(ROOT_NODE, "update curator zookeeper client1".getBytes());
            System.out.println("更新节点信息" + stat1);
    
            //删除节点,子节点一起删除
            client03.client.delete().
                    guaranteed().
                    deletingChildrenIfNeeded().
                    forPath(ROOT_NODE);
    
            Thread.sleep(1000000000);
        }
    
    
    }
    View Code

    Zookeeper之ACL

    ACL(Access Control List),Zookeeper 作为一个分布式协调框架,其内部存储的都是一些关于分布式系统运行时状态的元数据,默认状态下,所有应用都可以读写任何节点,在任何复杂应用中,这不太安全,ZK通过ACL机制来解决访问权限问题.

    Zookeeper节点权限模式:即Scheme;

    开发人员做多适用的如下四种权限模式:

    ip:    ip模式通过ip地址粒度进行权限控制模式,例如配置了:192.168.122.132即表示权限控制都是针对这个ip地址的,同时也支持按网段分配

    digest:  digest是最常用的权限控制模式,采用""username:password"形式的权限标识进行权限配置,Zk会对形成的权限标识先后进行两次加密处理,分别是SHA-1加密算法和Base64编码"

    world:  world是一种最开放的权限控制模式,这种模式可以看做是特殊的Digest,他仅仅是一个标识而已,有一个唯一的id,anyone,代表所有人,

    auth:  不使用任何id,代表任何已认证的用户.

    Zookeeper节点操作权限:权限就是指那些通过权限检测后可以被允许执行的操作,在Zk中,对数据的操作权限分为以下五大类

    create,delete,read,write,admin增删改查,管理权限,这五种权限简写为crwda

    命令行操作:

    1.

    增加认证用户

      addauth digest 用户名:密码明文:权限

      addauth digest zs:123456;cdrwa

    设置权限

      setAcl /path auth:用户名:密码明文:权限

      setAcl /root auth:zhangsan:123456:cdrwa

    查看Acl设置

      getAcl /root

    2.

    setAcl /root digest:用户名:密码密文:权限

    这里的加密规则是SHA1加密,然后base64编码

    节点权限API示例代码

    package com.blue.client;
    
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.imps.CuratorFrameworkState;
    import org.apache.curator.retry.RetryNTimes;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.ZooDefs.Perms;
    import org.apache.zookeeper.data.ACL;
    import org.apache.zookeeper.data.Id;
    import org.apache.zookeeper.data.Stat;
    import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
    
    public class ZookeeperClient04 {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String ROOT_NODE = "/acl";
    
        CuratorFramework client = null;
    
        public ZookeeperClient04() {
    
            //设置重试策略
            RetryPolicy retryPolicy = new RetryNTimes(3, 2000);
    
            //创建Zookeeper客户端,新版本
            client = CuratorFrameworkFactory.newClient(ZK_ADDR, retryPolicy);
            //老版本创建连接客户端
            client = CuratorFrameworkFactory.builder()
                    .authorization("digest", "zhangsan:123456".getBytes()).connectString(ZK_ADDR)
                    .sessionTimeoutMs(5000).connectionTimeoutMs(10000).retryPolicy(retryPolicy).build();
            client.start();
        }
    
        public static void main(String[] args) throws Exception {
            ZookeeperClient04 zookeeperClient04 = new ZookeeperClient04();
            CuratorFrameworkState state = zookeeperClient04.client.getState();
            System.out.println("状态信息:" + state);
            if (state == CuratorFrameworkState.STARTED) {
                System.out.println("启动成功:" + state);
            }
    
            List<ACL> aclList = new ArrayList<>();
            Id zhangsan = new Id("digest",
                    DigestAuthenticationProvider.generateDigest("zhangsan:123456"));
            Id lisi = new Id("digest", DigestAuthenticationProvider.generateDigest("lisi:123456"));
            Id wangwu = new Id("digest", DigestAuthenticationProvider.generateDigest("wangwu:123456"));
    
            aclList.add(new ACL(Perms.ALL, zhangsan));
            aclList.add(new ACL(Perms.READ, lisi));
            aclList.add(new ACL(Perms.READ | Perms.WRITE, wangwu));
    
            //完全开放的节点权限
            //        String s1 = zookeeperClient04.client.create().creatingParentsIfNeeded()
            //                .withMode(CreateMode.PERSISTENT).withACL(Ids.OPEN_ACL_UNSAFE) //指定ACL
            //                .forPath(ROOT_NODE + "/home", "curator open zookeeper".getBytes());
    
            // 指定访问控制权限的节点
            String s = zookeeperClient04.client.create().creatingParentsIfNeeded()
                    .withMode(CreateMode.PERSISTENT).withACL(aclList)
                    .forPath(ROOT_NODE + "/home4", "curator close zookeeper".getBytes());
    
            Stat stat = zookeeperClient04.client.setData()
                    .forPath(ROOT_NODE + "/home4", "1111111111111111".getBytes());
            byte[] bytes = zookeeperClient04.client.getData().forPath(ROOT_NODE + "/home3");
    
            System.out.println("当前数据节点" + new String(bytes));
        }
    
    }
    View Code

    分布式全局唯一ID生成:

    1.全局唯一,不能重复

    2,递增,下一个ID大于上一个ID(常规)

    3,信息安全,非连续id,避免恶意用户/竞争对手发现ID规则,从而猜出下一个ID或者根据ID总量猜出业务总量.

    4.高可用.高性能,低延迟.

    解决方案:

      1:UUID,randomUUID() ,多数场景不适用

      2.数据库自增,过度依赖数据库,数据库高并发易发生性能瓶颈.

      3.Redis方案,redis原子操作 INCR和INCRBY(redis自增)实现递增,同时可使用集群,设置每台初始值 1,2,3,4,5,递增5

      4:Twiitter的snowflake算法(https://github.com/twitter/snowflake)

      5:MongoDB的ObjectID

      6:Zookeeper方案: 持久顺序节点,节点版本号

    顺序节点&&节点版本号

    package com.blue.generator;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.RetryNTimes;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;
    
    
    public class IdGenerator {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String ID_NODE = "/id";
    
        CuratorFramework client = null;
    
        /**
         * 倒计数器
         */
        CountDownLatch countDownLatch = new CountDownLatch(1);
    
        public IdGenerator() {
            //重试策略
            RetryPolicy retryPolicy = new RetryNTimes(3, 3000);
            //创建Zookeeper
            //         client = CuratorFrameworkFactory.newClient(ZK_ADDR, retryPolicy);
            //老版本创建连接客户端
            client = CuratorFrameworkFactory.builder().connectString(ZK_ADDR).sessionTimeoutMs(5000)
                    .connectionTimeoutMs(10000).retryPolicy(retryPolicy).build();
            //启动客户端
            client.start();
        }
    
        /**
         * ID生成方法  基于顺序节点
         */
    
        public String idGenerator() throws Exception {
            if (null == client.checkExists().forPath(ID_NODE)) {
                //未创建 创建节点
                String s = client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(ID_NODE);
                client.delete().forPath(s);
                return s;
            }
            return null;
        }
    
        /**
         * ID生成方法  基于版本号
         */
        public long idGeneratorByVersion() throws Exception {
            if (null == client.checkExists().forPath(ID_NODE)) {
                //未创建 创建节点
                client.create().withMode(CreateMode.PERSISTENT).forPath(ID_NODE);
            }
            Stat stat = client.setData().withVersion(-1).forPath(ID_NODE);
    
            return stat.getVersion();
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            IdGenerator idGenerator = new IdGenerator();
            try {
                idGenerator.runThread();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            Thread.sleep(10000);
        }
    
        //多线程测试
        void runThread() {
            //创建线程池
            ExecutorService executorService = Executors.newFixedThreadPool(100);
            for (int i = 0; i < 16; i++) {
                executorService.submit(() -> {
                    try {
                        //所有线程全部在此等候
                        countDownLatch.await();
                        System.out.println(
                                "Thread:" + Thread.currentThread().getName() + ", time: " + System
                                        .currentTimeMillis());
                        //执行业务代码
                        String s = idGenerator();
                        String substring = s.substring(3);
                        System.out.println(substring);
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.out
                                .println("Thread:" + Thread.currentThread().getName() + e.getMessage());
                    }
    
    
                });
            }
            //倒计时完毕所有线程同时执行
            countDownLatch.countDown();
            try {
                // 传达完毕信号
                executorService.shutdown();
                // (所有的任务都结束的时候,返回TRUE)
                if (!executorService.awaitTermination(5 * 1000, TimeUnit.MILLISECONDS)) {
                    // 超时的时候向线程池中所有的线程发出中断(interrupted)。
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                // awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。
                System.out.println("awaitTermination interrupted: " + e);
                executorService.shutdownNow();
            }
        }
    }
    View Code

    Zookeeper分布式锁

      实现分布式锁有多重方式:

      第一种:基于数据库实现分布式锁:

          1.在数据库新建一个表(lock)

    create table lock(

      id

      method_name(唯一约束)

    ...

    )

          2.获取锁时向表中插入一条数据,由于有唯一约束,只会有一条线程插入成功,插入成功的线程获得锁,可以继续操作,没有插入成功的线程没有获得锁,不能操作.

          3.解锁时删除这条数据

    主要问题:

      可用性差,数据库故障会导致业务系统不可用;

      数据库性能存在瓶颈,不适合高并发场景

      删除锁失败容易导致死锁

      锁的失效时间难以控制

    第二种:基于Redis实现分布式锁

      加锁:

      setnx命令加锁,并设置锁的有效时间和持有人id标识;

      expire命令设置锁的过期时间

      解锁:检查是否持有锁,然后删除锁delete

      基于redis实现分布式锁,采用一个开源项目 http://redisson.org

    第三种:基于zookeeper实现分布式锁

      zookeepr是分布式系统的协调服务,实现原理主要是临时有序节点+监听来实现

    Curator客户端给我们提供现成的分布式互斥锁来实现分布式锁

    1、创建分布式互斥锁
    InterProcessMutex lock = new InterProcessMutex(zookeeperCuratorClient.client, "/storeLock");
    
    2、获取分布式互斥锁
    if (lock.acquire(10, TimeUnit.SECONDS))
    3、释放分布式互斥锁
    lock.release();

    具体示例,仅供参考

    package com.blue.lock;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.recipes.locks.InterProcessLock;
    import org.apache.curator.framework.recipes.locks.InterProcessMutex;
    import org.apache.curator.retry.RetryNTimes;
    
    
    public class CuratorLock {
    
        public static final String ZK_ADDR = "127.0.0.1:2181";
    
        public static final String LOCK_NODE = "/lock";
    
        CuratorFramework client = null;
    
        CountDownLatch countDownLatch = new CountDownLatch(1);
        public static int j = 1;
    
    
        public CuratorLock() {
            RetryPolicy retryPolicy = new RetryNTimes(3, 3000);
            //新版本
            //         client = CuratorFrameworkFactory.newClient(ZK_ADDR, retryPolicy);
            //老版本
            client = CuratorFrameworkFactory.builder().connectString(ZK_ADDR).sessionTimeoutMs(3000)
                    .connectionTimeoutMs(8000).retryPolicy(retryPolicy).build();
            //启动客户端
            client.start();
        }
    
        public static void main(String[] args) throws Exception {
            runThread();
    
        }
    
        //多线程测试
        static void runThread() {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            CuratorLock curatorLock = new CuratorLock();
            InterProcessLock lock = new InterProcessMutex(curatorLock.client, LOCK_NODE);
            //创建线程池
            ExecutorService executorService = Executors.newFixedThreadPool(100);
            for (int i = 0; i < 100; i++) {
                executorService.submit(() -> {
                    try {
                        //所有线程全部在此等候
                        countDownLatch.await();
                        if (lock.acquire(10, TimeUnit.SECONDS)) {
                            System.out.println("-----------执行了业务代码" + (j++));
                            lock.release();
                        }
                        System.out.println(
                                "Thread:" + Thread.currentThread().getName() + ", time: " + System
                                        .currentTimeMillis());
                        //执行业务代码
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.out
                                .println("Thread:" + Thread.currentThread().getName() + e.getMessage());
                    }
    
    
                });
            }
            //倒计时完毕所有线程同时执行
            countDownLatch.countDown();
            try {
                // 传达完毕信号
                executorService.shutdown();
                // (所有的任务都结束的时候,返回TRUE)
                if (!executorService.awaitTermination(50 * 1000, TimeUnit.MILLISECONDS)) {
                    // 超时的时候向线程池中所有的线程发出中断(interrupted)。
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                // awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。
                System.out.println("awaitTermination interrupted: " + e);
                executorService.shutdownNow();
            }
        }
    
    }
    View Code

     Zookeeper原生参考

    package com.blue.lock;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.TreeSet;
    import java.util.concurrent.CountDownLatch;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.Watcher.Event.EventType;
    import org.apache.zookeeper.Watcher.Event.KeeperState;
    import org.apache.zookeeper.ZooDefs.Ids;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.Stat;
    
    public class ZookeeperLock {
    
        public static final String ZK_ADDR = "127.0.0.1";
    
        public static final String ZK_NODE = "zkLocks";
    
        public static final int sessionTimeout = 10000;
    
        public static final byte[] bytes = new byte[0];
    
        /**
         * zookeeper原生客户端
         */
        private ZooKeeper zooKeeper;
    
        /**
         * 锁节点的名称
         */
        private String lockName;
    
        /**
         * 当前锁节点的名称
         */
        private String currentLockName;
    
        CountDownLatch countDownLatch = new CountDownLatch(1);
    
        /**
         * 构造方法
         *
         * @param lockName 锁节点的名称通过构造方法初始化
         */
        public ZookeeperLock(String lockName) {
            this.lockName = lockName;
            try {
                new ZooKeeper(ZK_ADDR, sessionTimeout, watchedEvent -> {
                    if (watchedEvent.getState() == KeeperState.SyncConnected) {
                        System.out.println("Zookeeepr连接上了");
                        countDownLatch.countDown();
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                //阻塞,等待Zookeeper连接上
                countDownLatch.await();
                //连接上之后 
                Stat exists = zooKeeper.exists(lockName, false);
                if (null == exists) {
                    //节点不存在  创建节点
                    zooKeeper.create(lockName, bytes, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * zookeeper分布式锁:加锁(获取分布式锁)
         */
        public void lock() {
    
            try {
                String myNode = zooKeeper.create(ZK_NODE + "/" + lockName, bytes, Ids.OPEN_ACL_UNSAFE,
                        CreateMode.EPHEMERAL_SEQUENTIAL);
                //拿到根节点下的所有临时有序子节点
                List<String> children = zooKeeper.getChildren(ZK_NODE, false);
                //所有根节点全部拿到
    
                //TreeSet
                TreeSet<String> sortNodes = new TreeSet<String>();
                for (String node : children) {
                    sortNodes.add(ZK_NODE + "/" + myNode);
                }
                //排好顺序的set集合中取
                String minNode = sortNodes.first();
                System.out.println("当前节点:" + myNode);
                System.out.println("最小节点:" + minNode);
    
                //当前节点若是最小节点,可以拿到分布式锁
                if (myNode.equals(minNode)) {
                    //当前节点是最小节点,赋值,返回
                    currentLockName = myNode;
                    return;
                }
                //拿到前一个节点
                String preNode = sortNodes.lower(myNode);
                System.out.println("前一个节点preNode=" + preNode);
    
                //其他进来的线程没有拿到分布式锁,创建的节点不是最小的,监听前一个节点的删除事件
    
                CountDownLatch countDownLatch = new CountDownLatch(1);
    
                if (null != preNode) {
                    //前一个节点不为空,监听前一个节点的删除事件
                    Stat exists = zooKeeper.exists(preNode, watchedEvent-> {
                        if (watchedEvent.getType() == EventType.NodeDeleted) {
                            countDownLatch.countDown();
                        }
                    });
                    if (null != exists) {
                        //等待监听到删除事件,执行
                        countDownLatch.await();
                        currentLockName = myNode;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 解锁操作
         */
        public void release() {
            //解锁 删除当前节点
            if (currentLockName != null) {
                try {
                    zooKeeper.delete(currentLockName, -1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (KeeperException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }
    
    
    }
    View Code

    Zookeeper集群:

    Zookeeper集群有两种工作的模式,一种是单机版本,另一种是集群方式;Zookeeper作为一个服务,本身也有可能发生故障,所以我们需要将Zookeeper进行集群,避免发生单点故障,以保证Zookeeper本身的高可用性;

    Zookeepr集群一般有三种角色:领导者(leader),跟随者(follower),一个观察者(observer),集群只要有半数以上工作,集群就整体可用,一般奇数台服务器最佳.

    下载三台纯净的zookeeper服务器,cp zoo_sample.cfg为zoo.cfg,添加以下配置,这样三台服务器就可以通过端口进行通信和投票,

    clientPort 每台服务器都要不一样,防止端口被占用.修改zoo.cfg

    clientPort=2181
    #server.myid=ip:通信端口:投票端口
    server.1=localhost:2888:3888
    server.2=localhost:2889:3889
    server.3=localhost:2890:3890

     #zookeeper内嵌的server服务器的端口,默认是8080

     admin.serverPort=3181

     #配置数据目录,此data目录若没有需要手动创建

     dataDir=/usr/local/apache-zookeeper-3.6.1-bin-01/data

     

     三台服务器创建data目录 mkdir data,创建myid文件,分别写入zoo.cfg中配置的server.后面对应的服务器编号  1 ,每台对应各自编号

    启动三台服务器,./zkServer.sh start

    查看集群状态 ./zkServer.sh status

    Mode: leader 领导者  Mode: follower 跟随者

    领导者:处理改变节点状态的请求,也叫作事务请求,事务请求的唯一调度者和处理者,保证集群事务处理的顺序性;

    跟随者:处理查询请求,事务请求会转发给领导者处理.

    观察者:特殊的跟随者,不参与投票,观察集群状态,同步数据,也可以处理非事务请求,转发事务请求给领导者.

    观察者配置:

    peerType=observer
    server.1=localhost:2888:3888
    server.2=localhost:2889:3889
    server.3=localhost:2890:3890
    server.4=localhost:2891:3891:observer

    观察者的出现是为了避免领导者挂掉,过多的服务器参与投票,导致选举过程缓慢,影响写性能而出现的.

  • 相关阅读:
    R12.2.4 ORA-01017: invalid username/password; logon denied
    VBA 判断一个TXT编码方式,再创建一个新的文件,复制数据进去
    查看功能所挂在的菜单
    EBS WebADI 存储过程增加参数
    用C++实现半透明按钮控件(PNG,GDI+)
    Linux五种IO模型性能分析
    用DirectX实现多视图渲染
    论YUV422(YUYV)与YUV420相互转换
    图文详解YUV420数据格式
    YUV422 YUV420 Planar Semi-Planar Interleaved YCbCr与YUV
  • 原文地址:https://www.cnblogs.com/zhaoletian/p/13292250.html
Copyright © 2020-2023  润新知