精华内容
下载资源
问答
  • 重点:迭代剔除劣势策略,换位思考 给出情景: 有10种立场,分成1 2 3 4 5 6 7 8 9 10,每个立场有10%的投票权,如果1号候选人拉拢1号立场,那么他会得到1号立场的全部投票,如果2号候选人拉拢3号立场,那么他会...

    重点:迭代剔除劣势策略,换位思考

    给出情景:

    有10种立场,分成1 2 3 4 5 6 7 8 9 10,每个立场有10%的投票权,如果1号候选人拉拢1号立场,那么他会得到1号立场的全部投票,如果2号候选人拉拢3号立场,那么他会得到3号立场的全部投票,而2的投票会平均分给1号候选人和2号候选人,其他的4 5 6 7 8 9 10也会投票给2号候选人,因为离他们近。问如果你是候选人,那么你会拉拢哪堆人,使自己的投票最多。

     

    分析:

        如果你是1号候选人,那么你一定不会选1,因为你如果那么选的话,那么2号候选人将选择2

    设(1,2)= p,为1号候选人选择立场1,   2号候选人选择立场2,那么1号候选人得票率p = 10%

    (1 ,2) = 10%    (2, 1) = 90%,  可以看出2号立场严格优于1号立场,所以我们排除有候选人会选择劣势立场的可能

    此处1号可以对应10号立场, 2号立场对应9号立场,  9号立场严格优于10号立场

     

    因为上一步,1号立场被两个候选人放弃的话,那么两个候选人就只剩下8个立场,分别是 2, 3 ,4,5,6,7,8

    那么接下来两个候选人从这剩下的立场中选择,

    (2, 3) = 20%      (3, 2)= 80%,1号候选人选择2号立场,2号候选人选择3号立场,3号立场又严格优于2号立场(但是立场3本身并不严格优于立场2分析:如果1号候选人选择1号立场,2号候选人选择2号立场,2号候选人的得票率为90%,而如果2号候选人选择3号立场的话,得票率为 85%,我们只是从上一步中推论出1号候选人一定不会选择立场1 ,所以排除立场1后,3号立场严格优于2号立场)。

     

    所以最终得选择一定会在5号立场和6号立场中间选择,得票率分别为 50%

    展开全文
  • 在hibernate中,使用list的进行多对多关系映射时,中间表的主键并不是由两个外键作为联合主键的,而是由一个外键和一个索引列作为联合主键。那么该hibernate会使用那个外键来作为联合主键呢?这会根据是否双向多对多...

    在hibernate中,使用list的进行多对多关系映射时,中间表的主键并不是由两个外键作为联合主键的,而是由一个外键和一个索引列作为联合主键。 那么该hibernate会使用那个外键来作为联合主键呢?这会根据是否双向多对多,是否设置inverse属性和inverse属性设在哪一方而发生改 变。

    1.双向多对多

    考虑以下hiberante映射
    ManyDoo.hbml.xml:

        <class name="bean.ManyDoo" table="Doo_T">
            <list name="manyFoos" table="foos_doos" cascade="save-update">
                <key column="doo_id" not-null="true"/>
                <index column="xl"/>
                <many-to-many class="bean.ManyFoo" column="foo_id"/>
            </list>
        </class>

    ManyFoo.hbm.xml

        <class name="bean.ManyFoo" table="Foo_T">
            <list name="manyDoos" table="foos_doos" cascade="save-update">
                <key column="foo_id" not-null="true"/>
                <index column="xl"/>
                <many-to-many class="bean.ManyDoo" column="doo_id"/>
            </list>
        </class>

    1)没有设置inverse属性

    如果没有设置 inverse 属性,那么hibernate会使用 hibernate.cfg.xml 中顺序最后的那个映射配置的类的id作为联合主键。
    比如 hiberante.cfg.xml 中这样写:

    <mapping resource="bean/ManyDoo.hbm.xml"/>

    <mapping resource="bean/ManyFoo.hbm.xml"/>

    那么ManyFoo.hbm.xml 是在后面的,那么联合主键就是(foo_id, xl)
    如果这样:

    <mapping resource="bean/ManyFoo.hbm.xml"/>

    <mapping resource="bean/ManyDoo.hbm.xml"/>

    那么联合主键就是 (doo_id, xl)

    2)设置了inverse属性

    如果在一方设置了 inverse="true" ,那么另一方就成为联合主键的一部分。
    比如,如果 ManyDoo.hbm.xml 中设置了 inverse="true" ,那么联合主键就是 (foo_id, xl)
    如果 ManyFoo.hbm.xml 中设置了 inverse="true" ,那么联合主键就是 (doo_id, xl)

    2.单向多对多

    单向多对多很简单,设置了多对多的一方成为联合主键的一部分。
    比如, ManyFoo.hbm.xml 中设置了多对多, ManyDoo.hbm.xml 中没有设置,那么联合主键就是 (foo_id, xl)
    反之亦然。

    展开全文
  • HDFS副本放置策略

    万次阅读 2016-04-19 19:36:37
    但是其实一个文件Block块从最初的产生到最后的落盘,存储类型选择策略只是其中1步,因为存储类型选择策略只是帮你先筛选了一些符合存储类型要求的存储节点目录位置列表,通过这些候选列表,你还需要做进一步的筛选,这...

    前言


    前一篇文章中刚刚分析完HDFS的异构存储以及相关的存储类型选择策略,浏览量还是不少的,说明大家对于HDFS的异构存储方面的功能还是很感兴趣的.但是其实一个文件Block块从最初的产生到最后的落盘,存储类型选择策略只是其中1步,因为存储类型选择策略只是帮你先筛选了一些符合存储类型要求的存储节点目录位置列表,通过这些候选列表,你还需要做进一步的筛选,这就是本文所准备阐述的另外一个主题,HDFS的副本放置策略.在写本文之前,我搜过网上关于此方面的资料与文章,还是有许多文章写的非常不错的,所以我会在本文中涉及到其他相关方面的个人感觉有用的知识点与大家分享,不至于文章显得太千篇一律了.

    何为副本放置策略


    首先这里要花一些篇幅来介绍什么是副本放置策略, 有人也会叫他为副本选择策略,这源于此策略的名称, BlockPlacementPolicy.所以这个策略类重在block placement.先来看下这个策略类的功能说明:

    This interface is used for choosing the desired number of targets for placing block replicas.

    大意就是说选择期望的目标节点供副本block存放.

    现有副本放置策略


    目前在HDFS中现有的副本防止策略类有2大继承子类,分别为BlockPlacementPolicyDefault, BlockPlacementPolicyWithNodeGroup,其中继承关系如下所示:

    这里写图片描述

    我们日常生活中提到最经典的3副本策略用的就是BlockPlacementPolicyDefault策略类.3副本如何存放在这个策略中得到了非常完美的实现.在BlockPlacementPolicyDefault类中的注释具体解释了3个副本的存放位置:

     The class is responsible for choosing the desired number of targets
     for placing block replicas.
     The replica placement strategy is that if the writer is on a datanode,
     the 1st replica is placed on the local machine, 
     otherwise a random datanode. The 2nd replica is placed on a datanode
     that is on a different rack. The 3rd replica is placed on a datanode
     which is on a different node of the rack as the second replica.

    简要概况起来3点:

    • 1st replica. 如果写请求方所在机器是其中一个datanode,则直接存放在本地,否则随机在集群中选择一个datanode.
    • 2nd replica. 第二个副本存放于不同第一个副本的所在的机架.
    • 3rd replica.第三个副本存放于第二个副本所在的机架,但是属于不同的节点.

    所以总的存放效果图如下所示

    这里写图片描述

    这里橙色区域表示的就是writer写请求者,绿色区域就是1个副本.从这里可以看出,HDFS在容错性的设计上还是做了很多的思考的.从下文开始主要分析的就是BlockPlacementPolicyDefault默认放置策略,至于BlockPlacementPolicyWithNodeGroup也会稍微提一提,但是二者主要区别其实不大.

    BlockPlacementPolicyDefault默认副本放置策略的分析


    BlockPlacementPolicyDefault这个类中的选择目标节点的处理逻辑还是有些复杂的,我会尽量讲的简单化,如有不理解之处,读者可以自己对照源码进行进一步的学习.

    策略核心方法chooseTargets


    在默认放置策略方法类中,核心方法就是chooseTargets,但是在这里有2种同名实现方法,唯一的区别是有无favoredNodes参数.favoredNodes的意思是偏爱,喜爱的节点.这2个方法的介绍如下

      /**
       * choose <i>numOfReplicas</i> data nodes for <i>writer</i> 
       * to re-replicate a block with size <i>blocksize</i> 
       * If not, return as many as we can.
       *
       * @param srcPath the file to which this chooseTargets is being invoked.
       * @param numOfReplicas additional number of replicas wanted.
       * @param writer the writer's machine, null if not in the cluster.
       * @param chosen datanodes that have been chosen as targets.
       * @param returnChosenNodes decide if the chosenNodes are returned.
       * @param excludedNodes datanodes that should not be considered as targets.
       * @param blocksize size of the data to be written.
       * @return array of DatanodeDescriptor instances chosen as target
       * and sorted as a pipeline.
       */
      public abstract DatanodeStorageInfo[] chooseTarget(String srcPath,
                                                 int numOfReplicas,
                                                 Node writer,
                                                 List<DatanodeStorageInfo> chosen,
                                                 boolean returnChosenNodes,
                                                 Set<Node> excludedNodes,
                                                 long blocksize,
                                                 BlockStoragePolicy storagePolicy);
    
    /**
       * Same as {@link #chooseTarget(String, int, Node, Set, long, List, StorageType)}
       * with added parameter {@code favoredDatanodes}
       * @param favoredNodes datanodes that should be favored as targets. This
       *          is only a hint and due to cluster state, namenode may not be 
       *          able to place the blocks on these datanodes.
       */
      DatanodeStorageInfo[] chooseTarget(String src,
          int numOfReplicas, Node writer,
          Set<Node> excludedNodes,
          long blocksize,
          List<DatanodeDescriptor> favoredNodes,
          BlockStoragePolicy storagePolicy) {
          }

    在chooseTargets传入偏爱的节点参数会使得方法在选择节点时候优先选取偏爱节点参数中的节点.这是这个参数的最根本的影响.

    chooseTarget无favoredNodes参数实现


    我们先来分析 chooseTarget无favoredNodes参数的实现过程,最终会进入到真正的同名实现方法中.我将此过程分为了3个子阶段

    • 1.初始化操作

        /** This is the implementation. */
      private DatanodeStorageInfo[] chooseTarget(int numOfReplicas,
                                      Node writer,
                                      List<DatanodeStorageInfo> chosenStorage,
                                      boolean returnChosenNodes,
                                      Set<Node> excludedNodes,
                                      long blocksize,
                                      final BlockStoragePolicy storagePolicy) {
      // 如果目标完成副本数为0或机器节点数量为0,返回空
      if (numOfReplicas == 0 || clusterMap.getNumOfLeaves()==0) {
        return DatanodeStorageInfo.EMPTY_ARRAY;
      }
      // 创建黑名单列表集  
      if (excludedNodes == null) {
        excludedNodes = new HashSet<Node>();
      }
      // 计算每个机架所允许最大副本数
      int[] result = getMaxNodesPerRack(chosenStorage.size(), numOfReplicas);
      numOfReplicas = result[0];
      int maxNodesPerRack = result[1];
      ...
    • 2.选择目标节点

          ...
      // 将所选节点加入到结果列表中,同时加入到移除列表中,意为已选择过的节点
      final List<DatanodeStorageInfo> results = new ArrayList<DatanodeStorageInfo>(chosenStorage);
      for (DatanodeStorageInfo storage : chosenStorage) {
        // add localMachine and related nodes to excludedNodes
        addToExcludedNodes(storage.getDatanodeDescriptor(), excludedNodes);
      }
      // 计算是否需要避免旧的,未更新的节点
      boolean avoidStaleNodes = (stats != null
          && stats.isAvoidingStaleDataNodesForWrite());
      // 选择numOfReplicas规定副本数的目标机器,并返回其中第一个节点
      final Node localNode = chooseTarget(numOfReplicas, writer, excludedNodes,
          blocksize, maxNodesPerRack, results, avoidStaleNodes, storagePolicy,
      // 如果不像返回初始选中的目标节点,则进行移除
      if (!returnChosenNodes) {  
        results.removeAll(chosenStorage);
      }
      ...
    • 3.排序目标节点列表,形成pipeline

          ...  
      // sorting nodes to form a pipeline
      // 根据最短距离排序目标节点列表,形成pipeline
      return getPipeline(
          (writer != null && writer instanceof DatanodeDescriptor) ? writer
              : localNode,
          results.toArray(new DatanodeStorageInfo[results.size()]));

    在上述的3个子阶段中,第二阶段是其中最主要的策略选择操作同样也是最具复杂性的,所以这里先分析第三个阶段的工作,就是根据已经选择好的目标节点存放位置,然后形成pipeline进行返回.

    Pipeline节点的形成


    整个过程就是传入目标节点列表参数,经过getPipeline方法的处理,然后返回此pipeline.先来看getPipeline的注释:

       Return a pipeline of nodes.
       The pipeline is formed finding a shortest path that 
       starts from the writer and traverses all nodes
       This is basically a traveling salesman problem.

    关键是这句The pipeline is formed finding a shortest path that
    starts from the writer,就是说从writer所在节点开始,总是寻找相对路径最短的目标节点,最终形成pipeline,学习过算法的人应该知道,这其实也是经典的TSP旅行商问题.下面是具体的源码实现:

      private DatanodeStorageInfo[] getPipeline(Node writer,
          DatanodeStorageInfo[] storages) {
        if (storages.length == 0) {
          return storages;
        }
    
        synchronized(clusterMap) {
          int index=0;
          // 首先如果writer请求方本身不在一个datanode上,则默认选取第一个datanode作为起始节点
          if (writer == null || !clusterMap.contains(writer)) {
            writer = storages[0].getDatanodeDescriptor();
          }
          for(; index < storages.length; index++) {
            // 获取当前index下标所属的Storage为最近距离的目标storage
            DatanodeStorageInfo shortestStorage = storages[index];
            // 计算当前距离
            int shortestDistance = clusterMap.getDistance(writer,
                shortestStorage.getDatanodeDescriptor());
            int shortestIndex = index;
            for(int i = index + 1; i < storages.length; i++) {
              // 遍历计算后面的距离
              int currentDistance = clusterMap.getDistance(writer,
                  storages[i].getDatanodeDescriptor());
              if (shortestDistance>currentDistance) {
                shortestDistance = currentDistance;
                shortestStorage = storages[i];
                shortestIndex = i;
              }
            }
            //switch position index & shortestIndex
            // 找到新的最短距离的storage,并进行下标替换
            if (index != shortestIndex) {
              storages[shortestIndex] = storages[index];
              storages[index] = shortestStorage;
            }
            // 找到当前这一轮的最近的storage,并作为下一轮迭代的源节点
            writer = shortestStorage.getDatanodeDescriptor();
          }
        }
        return storages;
      }

    一句话概况来说,就是选出一个源节点,根据这个节点,遍历当前可选的下一个目标节点,找出一个最短距离的节点,作为下一轮选举的源节点,这样每2个节点之间的距离总是最近的,于是整个pipeline节点间的距离和就保证是足够小的了.那么现在另外一个问题还没有解决,如何定义和计算2个节点直接的距离,就是下面这行代码

    clusterMap.getDistance(writer,
                shortestStorage.getDatanodeDescriptor());

    要计算其中的距离,我们首先要了解HDFS中如何定义节点间的距离,其中涉及到了拓扑逻辑结构的概念,结构图如下:

    这里写图片描述

    这里显示的是一个三层结构的树形效果图,Root可以看出是一个大的集群,下面划分出了许多个机架,每个机架下面又有很多属于此机架的节点.在每个连接点中,是通过交换机和路由器进行连接的.每个节点间的距离计算方式是通过寻找最近的公共祖先所需要的距离作为最终的结果.比如Node1到Node2的距离是2,就是Node1->Rack1, Rack1->Node2.同理,Rack1的Node1到Rack2的Node1的距离就是4.大家有兴趣的可以学习一下相关算法LCA最近公共祖先算法.

    chooseTarget方法主逻辑


    下面介绍chooseTarget主要选择逻辑,因为个人感觉是最复杂的,所以放在最后分析.首先,务必要明确以下几个涉及参数的作用和所代表的意义:

    final Node localNode = chooseTarget(numOfReplicas, writer, excludedNodes,
            blocksize, maxNodesPerRack, results, avoidStaleNodes, storagePolicy,
            EnumSet.noneOf(StorageType.class), results.isEmpty());
    • numOfReplicas, 额外需要复制的副本数
    • excludedNodes,移除节点集合,此集合内的节点不应被考虑作为目标节点
    • results,当前已经选择好的目标节点集合
    • storagePolicy,存储类型选择策略

    OK,下面进入具体方法实现.

    首节点的选择


    我们可以对照上文提到的3副本的存放方式.首先是第一个节点的选择,第一个节点其实是最好选择的,因为他不用其他2个节点的位置影响,但是他同样要约束于请求方所在位置,这里满足2个原则:

    • 如果writer请求方本身位于集群中的一个datanode之上,则第一个副本的位置就在本地节点上,很好理解,这样直接就是本地写操作了.
    • 如果writer请求方纯粹来源于外界客户端的写请求时,则从已选择好的目标节点result列表中挑选第一个节点作为首个节点.
    • 如果result列表中还是没有任何节点,则会从集群中随机挑选1个node作为第一个localNode.

    后续还进行了如下的操作

        // 如果额外需要请求副本数为0,或者集群中没有可选节点
        if (numOfReplicas == 0 || clusterMap.getNumOfLeaves()==0) {
          // 如果writer请求者在其中一个datanode上则返回此节点,否则直接返回null
          return (writer instanceof DatanodeDescriptor) ? writer : null;
        }
        // 获取已经选择完成的节点数
        final int numOfResults = results.size();
        // 计算期望希望达到的副本总数
        final int totalReplicasExpected = numOfReplicas + numOfResults;
        // 如果writer为空或不在datanode上,则取出已选择好列表中的第一个位置所在节点,赋值给writer
        if ((writer == null || !(writer instanceof DatanodeDescriptor)) && !newBlock) {
          writer = results.get(0).getDatanodeDescriptor();
        }
    
        // Keep a copy of original excludedNodes
        // 做一份移除列表名单的拷贝
        final Set<Node> oldExcludedNodes = new HashSet<Node>(excludedNodes);
    
        // choose storage types; use fallbacks for unavailable storages
        // 根据存储策略获取副本需要满足的存储类型列表,如果有不可用的存储类型,会采用fallback的类型
        final List<StorageType> requiredStorageTypes = storagePolicy
            .chooseStorageTypes((short) totalReplicasExpected,
                DatanodeStorageInfo.toStorageTypes(results),
                unavailableStorages, newBlock);
        // 将存储类型列表进行计数统计,并存于map中
        final EnumMap<StorageType, Integer> storageTypes =
            getRequiredStorageTypes(requiredStorageTypes);
        if (LOG.isTraceEnabled()) {
          LOG.trace("storageTypes=" + storageTypes);
        }
        ...

    三副本位置的选取


    下面是非常巧妙的3副本存储位置的选取,需要与上图描述的存放方式进行对照,可能会好理解一些

        ...
        // 如果numOfReplicas或requiredStorageTypes大小为0,则抛出异常
        try {
          if ((numOfReplicas = requiredStorageTypes.size()) == 0) {
            throw new NotEnoughReplicasException(
                "All required storage types are unavailable: "
                + " unavailableStorages=" + unavailableStorages
                + ", storagePolicy=" + storagePolicy);
          }
          // 如果已选择的目标节点数量为0,则表示3副本一个都还没开始选,首先从选本地节点开始
          if (numOfResults == 0) {
            writer = chooseLocalStorage(writer, excludedNodes, blocksize,
                maxNodesPerRack, results, avoidStaleNodes, storageTypes, true)
                    .getDatanodeDescriptor();
            // 如果此时目标需求完成的副本数为降为0,代表选择目标完成,返回第一个节点writer
            if (--numOfReplicas == 0) {
              return writer;
            }
          }
          // 取出result列表第一个节点
          final DatanodeDescriptor dn0 = results.get(0).getDatanodeDescriptor();
          // 前面的过程已经完成首个本地节点的选择,此时进行不同机房的节点选择
          if (numOfResults <= 1) {
            // 选择1个不同于dn0所在机房的一个目标节点位置
            chooseRemoteRack(1, dn0, excludedNodes, blocksize, maxNodesPerRack,
                results, avoidStaleNodes, storageTypes);
            // 如果此时目标需求完成的副本数为降为0,代表选择目标完成,返回第一个节点writer
            if (--numOfReplicas == 0) {
              return writer;
            }
          }
          // 如果经过前面的处理,节点选择数在2个以内,需要选取第3个副本
          if (numOfResults <= 2) {
            // 取出result列表第二个节点
            final DatanodeDescriptor dn1 = results.get(1).getDatanodeDescriptor();
            // 如果dn0,dn1所在同机房,
            if (clusterMap.isOnSameRack(dn0, dn1)) {
              // 则选择1个不同于dn0,dn1所在机房的副本位置
              chooseRemoteRack(1, dn0, excludedNodes, blocksize, maxNodesPerRack,
                  results, avoidStaleNodes, storageTypes);
            } else if (newBlock){
              // 如果是新的block块,则选取1个于dn1所在同机房的节点位置
              chooseLocalRack(dn1, excludedNodes, blocksize, maxNodesPerRack,
                  results, avoidStaleNodes, storageTypes);
            } else {
              // 否则选取于writer同机房的位置
              chooseLocalRack(writer, excludedNodes, blocksize, maxNodesPerRack,
                  results, avoidStaleNodes, storageTypes);
            }
            // 如果此时目标需求完成的副本数为降为0,代表选择目标完成,返回第一个节点writer
            if (--numOfReplicas == 0) {
              return writer;
            }
          }
          // 如果副本数已经超过2个,说明设置的block的时候,已经设置超过3副本的数量
          // 则剩余位置在集群中随机选择放置节点
          chooseRandom(numOfReplicas, NodeBase.ROOT, excludedNodes, blocksize,
              maxNodesPerRack, results, avoidStaleNodes, storageTypes);

    如果看完这段逻辑,你还不理解的话,没有关系,只要明白经典的3副本存放位置,多余的副本随机存放的原理即可.当然在其间选择的过程中可能会发生异常,因为有的时候我们没有配置机架感知,集群中都属于一个默认机架的default-rack,则会导致chooseRemoteRack的方法出错,因为没有满足条件的其余机架,这时需要一些重试策略.

          if (retry) {
            for (DatanodeStorageInfo resultStorage : results) {
              addToExcludedNodes(resultStorage.getDatanodeDescriptor(),
                  oldExcludedNodes);
            }
            // 剔除之前完成的选择的目标位置,重新计算当前需要复制的副本数
            numOfReplicas = totalReplicasExpected - results.size();
            // 重新调用自身方法进行复制
            return chooseTarget(numOfReplicas, writer, oldExcludedNodes, blocksize,
                maxNodesPerRack, results, false, storagePolicy, unavailableStorages,
                newBlock);
          }

    chooseLocalStorage,chooseLocalRack,chooseRemoteRack和chooseRandom方法


    标题显示的四个选择目标节点位置的方法其实是一个优先级渐渐降低的方法,首先选择本地存储位置.如果没有满足条件的,再选择本地机架的节点,如果还是没有满足条件的,进一步降级选择不同机架的节点,最后随机选择集群中的节点,关系图如下

    这里写图片描述

    但是这里还是要区分一下,chooseLocalStorage方法,与其余的3个方法稍显不同,单独实现,而其余的方法是通过传入不同参数直接或间接调用
    chooseRandom方法.

    首先看下chooseLocalStorage方法实现

      protected DatanodeStorageInfo chooseLocalStorage(Node localMachine,
          Set<Node> excludedNodes, long blocksize, int maxNodesPerRack,
          List<DatanodeStorageInfo> results, boolean avoidStaleNodes,
          EnumMap<StorageType, Integer> storageTypes, boolean fallbackToLocalRack)
          throws NotEnoughReplicasException {
        // if no local machine, randomly choose one node
        if (localMachine == null) {
          // 如果本地节点为空,则降级选择1个随机节点
          return chooseRandom(NodeBase.ROOT, excludedNodes, blocksize,
              maxNodesPerRack, results, avoidStaleNodes, storageTypes);
        }
        if (preferLocalNode && localMachine instanceof DatanodeDescriptor) {
          DatanodeDescriptor localDatanode = (DatanodeDescriptor) localMachine;
          // otherwise try local machine first
          if (excludedNodes.add(localMachine)) { // was not in the excluded list
            for (Iterator<Map.Entry<StorageType, Integer>> iter = storageTypes
                .entrySet().iterator(); iter.hasNext(); ) {
              Map.Entry<StorageType, Integer> entry = iter.next();
              // 遍历本地节点可用的存储目录
              for (DatanodeStorageInfo localStorage : DFSUtil.shuffle(
                  localDatanode.getStorageInfos())) {
                StorageType type = entry.getKey();
                // 加入满足条件的存储目录位置
                if (addIfIsGoodTarget(localStorage, excludedNodes, blocksize,
                    maxNodesPerRack, false, results, avoidStaleNodes, type) >= 0) {
                  int num = entry.getValue();
                  if (num == 1) {
                    iter.remove();
                  } else {
                    entry.setValue(num - 1);
                  }
                  return localStorage;
                }
              }
            }
          } 
        }
    
        if (!fallbackToLocalRack) {
          return null;
        }
        // 本地节点没有满足条件的存储位置,则降级选取同机架的节点
        // try a node on local rack
        return chooseLocalRack(localMachine, excludedNodes, blocksize,
            maxNodesPerRack, results, avoidStaleNodes, storageTypes);
      }

    chooseLocalRack和chooseRemoteRack比较类似,

    • chooseLocalRack

          // no local machine, so choose a random machine
      if (localMachine == null) {
        return chooseRandom(NodeBase.ROOT, excludedNodes, blocksize,
            maxNodesPerRack, results, avoidStaleNodes, storageTypes);
      }
      // 获取本地机架名
      final String localRack = localMachine.getNetworkLocation();
      
      try {
        // choose one from the local rack
        // 将机架名作为scope参数传入
        return chooseRandom(localRack, excludedNodes,
            blocksize, maxNodesPerRack, results, avoidStaleNodes, storageTypes);
    • chooseRemoteRack

            // 获取本地机架名称,带上前缀字符~,作为scope参数传入
        chooseRandom(numOfReplicas, "~" + localMachine.getNetworkLocation(),
            excludedNodes, blocksize, maxReplicasPerRack, results,
            avoidStaleNodes, storageTypes);

    从这里我们可以看到,这里最明显的区别就是chooseRandom的scope参数的传入,scope参数的直接作用就是会选择出是否属于此机架下的节点列表

          DatanodeDescriptor chosenNode = 
              (DatanodeDescriptor)clusterMap.chooseRandom(scope);

    在NetworkTopology下有了具体的实现

      /** randomly choose one node from <i>scope</i>
       * if scope starts with ~, choose one from the all nodes except for the
       * ones in <i>scope</i>; otherwise, choose one from <i>scope</i>
       * @param scope range of nodes from which a node will be chosen
       * @return the chosen node
       */
      public Node chooseRandom(String scope) {
        netlock.readLock().lock();
        try {
          if (scope.startsWith("~")) {
            return chooseRandom(NodeBase.ROOT, scope.substring(1));
          } else {
            return chooseRandom(scope, null);
          }
        } finally {
          netlock.readLock().unlock();
        }
      }

    具体细节的实现,读者可以自行研究.根据机架选择好节点之后,同样会进行Storage存储位置的选择判断,然后加入到result目标列表中.

    目标Storage好坏的判断


    如果block放置节点位置已经初步选择好了,是否意味着此位置就可以加入最终的result列表中呢,答案是否定的,因为这里还要经过最后一道严谨的对于Storage的验证.(这里要明确一点:目标位置result类别存储的对象是DatanodeStorageInfo,这个类表示的是具体到节点存储磁盘目录级别的信息,并不是广义上的Node),需要满足以下几个条件

    • storage的存储类型是要求给定的存储类型
    • storage不能是READ_ONLY只读的
    • storage不能是坏的
    • storage所在机器不应该是已下线或下线中的节点
    • storage所在节点不应该是旧的,一段时间内没有更新心跳的节点
    • 节点内保证有足够的剩余空间能满足写Block要求的大小
    • 要考虑节点的IO负载繁忙程度
    • 要满足同机架内最大副本数的限制

    可见,验证的条件还是非常苛刻,具体代码见BlockPlacementPolicyDefault的isGoodTarget方法.

    chooseTargets的调用


    chooseTargets的调用分为有favoredNodes参数和无favoredNodes参数参数2类.
    无参数的chooseTargets主要被BlockManager对象所调用,如图

    这里写图片描述

    其中RepliactionWork主要做的就是集群中待复制的副本块.
    而带favoredNodes参数的调用则是外界主动设置进来的,调用场景如下

    这里写图片描述

    favoredNodes的源头是DFSClient客户端主动设置进来,然后创建到DFSOutputStream的DataStream中,然后被后续方法所调用.但是DFSClient在创建默认DFSOutputStream时是默认不带favoredNodes传入的

      public DFSOutputStream create(String src, 
                                 FsPermission permission,
                                 EnumSet<CreateFlag> flag, 
                                 short replication,
                                 long blockSize,
                                 Progressable progress,
                                 int buffersize,
                                 ChecksumOpt checksumOpt)
          throws IOException {
        return create(src, permission, flag, true,
            replication, blockSize, progress, buffersize, checksumOpt, null);
      }

    就是最后一个参数null.其实传入的favoredNodes更多的是一种期望,并不一定真正能被namenode最后真正存放,因为中间会经过很多因素的判断,而且在后面的Balance数据平衡的过程中,某些block还是会被挪走,就不会按照原来的位置存.

    BlockPlacementPolicyWithNodeGroup继承类


    BlockPlacementPolicyWithNodeGroup是BlockPlacementPolicyDefault的继承子类.前者与后者在原理上十分类似,不过在逻辑上从机架是否相同的判断变为了是否为同个Node-Group的判断.下面是其中的注释声明:

     The class is responsible for choosing the desired number of targets
     for placing block replicas on environment with node-group layer.
     The replica placement strategy is adjusted to:
     If the writer is on a datanode, the 1st replica is placed on the local 
         node (or local node-group), otherwise a random datanode. 
     The 2nd replica is placed on a datanode that is on a different rack with 1st
         replica node. 
     The 3rd replica is placed on a datanode which is on a different node-group
         but the same rack as the second replica node.

    他是一个4层层级结构,在Rack机架层下还多了Node-Group层,结构图如下:

    这里写图片描述

    由于与其父类的逻辑没有很大的差别,就不展开做阐述了.

    总结


    以上内容就是本文所要表达的HDFS放置策略的内容了,可能内容量上有点大,部分地方描述的可能也有不够好的地方,希望大家通过此文能对HDFS的3副本策略以及背后的HDFS的放置策略有更深的了解.

    参考链接


    1.http://www.ibm.com/developerworks/cn/data/library/bd-1505-hdfs-uilbps-optimize/index.html

    展开全文
  • 配置密码策略

    千次阅读 2020-01-15 16:01:07
    密码策略介绍 密码策略是操作系统针对系统安全提供的一种安全机制,就好像linux操作系统不提供超级用户登录一样,密码策略包括:密码最小长度、密码使用期限、历史密码、密码复杂度等,在企业里面都是要求对操作...

    密码策略介绍


    密码策略是操作系统针对系统安全提供的一种安全机制,就好像linux操作系统不提供超级用户登录一样,密码策略包括:密码最小长度、密码使用期限、历史密码、密码复杂度等,在企业里面都是要求对操作系统进行密码策略进行配置的,而且要求密码复杂度。企业中做等级保护测评2级以上都是要求有密码策略的,之前我有过一次做等保测评师的经历,在企业里面几乎没有任何一个企业在使用这个密码策略,很郁闷不知道是工程师们不知道还是什么原因,在这里我就为大家简单介绍一下如何设置密码策略

    根据相关要求密码需要满足以下几点要求:

    • 用户有责任和义务妥善保管其个人帐号和密码,不得在任何场合随意公开自己的帐号和密码,不得泄漏他人。由于密码泄漏造成的不良后果由帐号拥有人承担相关责任
    • 密码长度不得少于6位
    • 密码由数字、标点、大小写字母和特殊符号组成,并具有必要的组合复杂度,禁止使用连续或相同的数字、字母组合(如123456等)和其他易于破译的组合作为密码。
    • 密码不得以任何形式的明文存放在主机电子文档中,不得以明文形式在电子邮件、传真中传播。
    • 系统管理人员在建立帐号时,对所分配帐号的初始密码设置为第一次登录强制修改。对于不能强制修改密码的系统,系统管理员创建帐号后立即通知用户修改密码,用户在第一次登录系统并修改密码之后反馈系统管理员,并由系统管理员进行复核。
    • 用户应定期(至少每季度一次)进行密码的修改,并且同一密码不能反复使用。

        。。。。。。。。。。。。。。等,具体要求可以查看等保2.0密码技术应用分析


     windows设置密码策略 


    windows密码策略有以下这些设置:

    • 密码必须符合复杂性要求
    • 密码长度最小值
    •  密码最短使用期限
    • 密码最长使用期限
    • 强制密码历史
    • 用可还原的加密存储密码

    接下来对这些配置进行修改

    打开管理工具


    打开本地安全策略-账户策略-密码策略



    然后就可以根据自己的需求去进行调整,下面是我推荐大家进行设置的,仅供参考


    Linux系统设置密码策略

    对于linux可能大家都很少知道有密码策略这回事吧,好多人都认为linux安全机制已经很强大了,而且大多数linux采用可插拔密码认证来加强密码的安全策略,下面就来说说linux的密码策略,linux密码策略要比windows密码策略要强大许多

    linux密码策略有以下设置:

    • 密码的最大有效期
    • 密码最长使用时间
    • 密码最小长度
    • 密码失效前提前多少天进行提醒
    • 密码大小写以及数字、特殊字符等限制
    • 新旧密码不能相同
    • 新旧密码长度不能相同
    • 账号锁定时间
    • 账号自动解锁时间

    密码策略配置文件路径:

    • 在centos/redhat等系统中路径:/etc/pam.d/system-auth
    • ubuntu等系统中路径:/etc/pam.d/common-password

    文件内容如下:(版本不同,内容有一些差别)

    # /etc/pam.d/common-password - password-related modules common to all services
    # This file is included from other service-specific PAM config files,
    # and should contain a list of modules that define the services to be
    # used to change user passwords.  The default is pam_unix.

    # Explanation of pam_unix options:
    # The "sha512" option enables salted SHA512 passwords.  Without this option,
    # the default is Unix crypt.  Prior releases used the option "md5".
    # The "obscure" option replaces the old `OBSCURE_CHECKS_ENAB' option in
    # login.defs.
    # See the pam_unix manpage for other options.

    # As of pam 1.0.1-6, this file is managed by pam-auth-update by default.
    # To take advantage of this, it is recommended that you configure any
    # local modules either before or after the default block, and use
    # pam-auth-update to manage selection of other modules.  See
    # pam-auth-update(8) for details.

    # here are the per-package modules (the "Primary" block)
    password        [success=1 default=ignore]      pam_unix.so obscure sha512
    # here's the fallback if no module succeeds
    password        requisite                       pam_deny.so
    # prime the stack with a positive return value if there isn't one already;
    # this avoids us returning an error just because nothing sets a success code
    # since the modules above will each just jump around
    password        required                        pam_permit.so
    # and here are more per-package modules (the "Additional" block)
    # end of pam-auth-update config

    密码过期时间以及有效期等配置文件:/etc/login.defs,文件部分内容:

    PASS_MAX_DAYS:一个密码可使用的最大天数。

    PASS_MIN_DAYS:两次密码修改之间最小的间隔天数。

    PASS_MIN_LEN:密码最小长度。

    PASS_WARN_AGE:密码过期前给出警告的天数

    下面为大家举例说明linux用户密码策略:

    • 设置密码最大使用时间(PASS_MAX_DAYS)

    这个用来限制密码最大可以使用的天数。时间到了会强制进行密码锁定。如果忘记修改,那么就无法登录系统。需要使用管理员账户进行解锁后才能继续使用。这个可以在 /etc/login.defs 文件中的PASS_MAX_DAYS参数设置。在企业中一般把这个值设置为30天,也就是一个月修改一次密码。

    root@test:/etc#  vim login.defs

    PASS_MAX_DAYS  30         //单位为天数

    • 设置密码最小天数(PASS_MIN_DAYS)

    这个是限制多长时间无法进行密码修改。当值为15时,表示15天内无法修改密码,也就是两次密码修改中间最少隔15天,这个可以在 /etc/login.defs 文件中PASS_MIN_DAYS参数设置。企业中一般不对此项进行控制,这个根据自己需求进行修改,我这里设置10天。

    root@test:/etc#  vim login.defs

    PASS_MIN_DAYS    10            //单位为天数

    • 设置密码过期前警告(PASS_WARN_AGE

    这个用来提醒用户该进行密码修改了,也就是在密码即将过期的时候,会给用户一个警告提示,在未到最大密码使用时间,会每天进行提醒,这可以提醒用户在密码过期前修改他们的密码,否则我们就需要联系管理员来解锁密码。这个可以在 /etc/login.defs 文件中PASS_WARN_AGE参数设置。 这个企业中一般设置为3天,我这里就设置为3天

    root@test:/etc#  vim login.defs

    PASS_WARN_AGE     3           //单位为天数

    • 避免重复使用旧密码

    这个用来防止更改密码时设置为旧密码,寻找同时包含“password”和"pam_unix.so"的行,然后再这行后面加上“remember=天数”。这将防止N个最近使用过的密码被用来设置为新密码,这个配置文件是在 /ect/pam.d/common-password 文件中(主要,centos/redhat需要修改:/etc/pam.d/system-auth文件),这个在企业中一般设置为5,我这里就设置5

    ubuntu:

    root@test/etc# vim pam.d/common-password

    password        [success=1 default=ignore]      pam_unix.so obscure sha512   remember=5

    centos/redhat:

    root@test/etc# vim  pam.d/common-password

    password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=5

    • 设置密码复杂度

    这个用来控制密码的复杂程度的,应安全性要求,企业里面要求大小写、特殊字符、数字等最受三个进行组合并且长度最少为8。寻找同时包含“password”和“pam_cracklib.so”的一行,并在后面加上“ucredit=-1 lcredit=-2 dcredit=-1 ocredit=-1”。这将迫使你在密码中至少包括一个大写字母、两个小写字母、一个数字和一个符号。

    ubuntu:

    root@test/etc# vim pam.d/common-password

    password        requisite     pam_cracklib.so retry=3 minlen=10 difok=3 ucredit=-1 lcredit=-2 dcredit=-1 ocredit=-1

    centos/redhat:

    root@test/etc# vim pam.d/system-auth

    password        requisite     pam_cracklib.so retry=3 minlen=10 difok=3 ucredit=-1 lcredit=-2 dcredit=-1 ocredit=-1

    展开全文
  • VNPY - CTA策略模块策略回测

    千次阅读 2018-08-10 16:50:05
    通过回测历史数据可以验证策略的有效性,了解策略的历史收益、最大回撤和回撤时长,对策略参数进行优化等等。CTA策略模块的主要回测目标是验证交易信号是否正确,仓位大小的问题在实盘中则由交易员来确定。...
  • Linux策略路由

    千次阅读 2018-10-07 20:00:05
    前面讲的路由规则都是基于目标IP地址为匹配依据设置的路由规则,策略路由则更加灵活,它可以根据多个参数来配置路由。假设如下的使用场景: 在192.168.10.0子网中,除了主机192.168.10.123要访问因特网时是通过电信...
  • 常用算法策略总结

    万次阅读 2019-05-03 08:49:34
    常用算法策略总结 策略是面向问题的,算法是面向实现的。 一、不同算法策略特点小结 1、贪心策略 ​ 贪心策略一方面是求解过程比较简单的算法,另一方面它又是对能适用问题的条件要求最严格(即适用范围很小)的算法...
  • 反爬虫策略总结

    万次阅读 2017-06-29 21:10:33
    今日终于有点时间了,总结一下网络爬虫领域比较常见的反爬虫策略,希望在我们抓取数据过程中遇到问题时,提供解决方法。话不多说,开讲: 1、最为经典的反爬虫策略当属“验证码”了。因为验证码是图片,用户登录时只...
  • 开篇: 在魔蝎被警方带走调查后,目前还没有对该案件公开的法律定性定罪。...一方面了解爬虫数据在风控策略上的应用,一方面了解策略数据挖掘的分析思路。虽然一切过去时,但是方法,思路,技术是永恒的。 ...
  • 路由策略策略路由配置与管理-1

    万次阅读 多人点赞 2017-02-24 12:43:14
    “路由策略”与“策略路由”之间的区别就在于它们的主体(或者说“作用对象”)不同,前者的主体是“路由”,是对符合条件的路由(主要)通过修改路由属性来执行相应的策略动作(如允许通过、拒绝通过、接收、引入等...
  • 分布式主键生成策略

    千次阅读 2016-02-17 13:25:14
    在分布式高并发的情况下,分布式主键生成策略可参考mongodb的objectid实现 ObjectId是一种轻量型的,不同的机器不同的进程都能用全局唯一的同种方法生成它,而不是采用传统的自增的主键策略,因为在多台服务器上...
  • Dual Thrust 策略

    千次阅读 2019-02-18 14:04:27
    Dual Thrust策略是一个很经典的策略。常年排在国外前10大流行策略之一! 绩效测试(橡胶期货,2016/1/1~2018/1/4,主图1分K,子图30分K,回测换月跳空有处理些) 2017年策略陷入瓶颈,2016年的趋势年,策略表现非常...
  • 工程案例,设计模式——策略模式

    千次阅读 2014-06-26 17:10:24
    策略模式定义  策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 下面我将从xiang'mu
  • 学习四、集合点策略中间一些过程之前已经学习过,暂且不需要深入研究,这里有一点需要补充学习,就是设置集合点策略。我们在录制/编辑脚本的时候设置了集合点,但每次都等到所有vuser都集合有时候又太愚笨,所以在...
  • 在前面的【安卓缓存策略系列】安卓缓存之内存缓存LruCache和【安卓缓存策略系列】安卓缓存策略之磁盘缓存DiskLruCache这两篇博客中已经将安卓中的缓存策略的理论知识进行过详细讲解,还没看过这两篇博客的看官建议先...
  • 均线策略和新高策略【被diss版】

    千次阅读 2018-01-11 09:57:51
    基本策略目录1 均线策略1.1 C.J.Neely, P.A.Weller, Technical analysis in the foreign exchange market, Working Paper, Federal Reserve of St.Louis,2011.1) 策略2) 收益率回测1.2 Valeriy Zakamulin, Market ...
  • Linux 防火墙策略

    千次阅读 2013-09-11 11:01:32
    当基于简单的包过滤时,我们只检查到第三层和第四层中间(icmp)工作过程如下: ① 数据包从外网传送到防火墙后,防火墙抢在IP层向TCP层传送前,将数据包转发给包检查模块进行处理。 ② 首先与第一个过滤规则比较。 ...
  • Flink重启策略

    千次阅读 2020-02-15 17:06:41
    Flink学习笔记(十七):1.Flink 重启策略介绍        当介绍 Flink 重启策略时,就必须要先介绍一下 State、StateBackEnd、CheckPointing 这三个概念。 1.1 State 状态  &...
  • 算法策略的总结

    千次阅读 2013-07-01 16:04:06
    策略是面向问题的,算法是面向实现的。   一、不同算法策略特点小结 1、贪心策略  贪心策略一方面是求解过程比较简单的算法,另一方面它又是对能适用问题的条件要求最严格(即适用范围很小)的算法。 ...
  • Policy Gradient (策略梯度算法)

    千次阅读 2019-11-09 07:47:35
    Policy Gradient (策略梯度算法)前置基础知识: Reinforcement Learning 基本概念 Q Leaning算法原理 深度学习神经网络知识 Tensorflow、Pytorch、Python 概率论与数量统计、马尔可夫链、期望、方差 微积分...
  • Ribbon负载均衡策略详解

    千次阅读 2018-11-07 11:35:54
    目前主流的负载方案分为两种,一种是集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的,比如F5,也有软件的,比如Nginx。 另一种则是客户端自己做负载均衡,根据自己的请求情况做...
  • Ribbon负载均衡策略介绍

    千次阅读 2018-01-04 09:28:10
    目前主流的负载方案分为两种,一种是集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的,比如F5,也有软件的,比如Nginx。另一种则是客户端自己做负载均衡,根据自己的请求情况做负载...
  • 代数优化策略

    千次阅读 2020-03-01 21:39:02
    目的:减少中间关系 投影和选择操作同时进行 目的:避免重复扫描关系 投影和前后的双目运算结合 目的:减少扫描关系的遍数 选择和前面要执行的笛卡尔积结合成为一个连接运算 找出公共子表达式 ...
  • 通常,在图形上表现为上中下三条线,其中,上下两条线可以分别看成是压力线和支撑线,中间的线是N日的移动均线,通常情况下,价格的走势会在上下两条线之间。Boll指标的计算如下: 布林通道策略 布林通道策略源码...
  • 在2018年10月份发布的Nexus 3.14版中提供了CleanUp功能,这对于使用Nexus的用户是一个原本早就该提供的功能,在这个功能中提供了对于长时间不使用的二进制制品的清除策略。这篇文章主要就此项功能的使用方式进行整理...
  • 6.3.2 映射策略

    千次阅读 2008-05-11 14:57:00
    l 表关联:两个表的关系单独定义一个表中,通过一个中间表来关联。两种不同表设计的映射的配置也不同,首先看一下第一种策略外键关联如何进行映射配置。有关表关联策略的配置,将在本章的6.3.5小节中做详细介绍。
  • ETL数据抽取策略

    千次阅读 2011-02-04 23:21:00
    ETL的抽取策略 本文所提到的数据加载策略为OLTP系统作为源系统,并进行ETL数据加载到OLAP系统中所采用的一般数据加载策略。依循数据仓库的工作方式,原始资料由源数据库被抽取出来后,将在中间过程被写入...
  • 局域网内,金蝶K310.4客户端连接中间层,打开金蝶web系统配置工具webcnfg.exe或webcnfgN.exe,注册连接中间层,提示:连接中间层加密服务失败,请确认中间层加密服务已启动。 主要原因: 一、金蝶K3中间层本身...
  • CSP策略及绕过方法

    千次阅读 2017-06-26 10:45:31
    XSS的时候经常要绕过CSP,通过网上的资料进行学习CSP策略一个CSP头由多组CSP策略组成,中间由分号分隔,就像这样:Content-Security-Policy: default-src 'self' www.baidu.com; script-src 'unsafe-inline' 其中每...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 233,700
精华内容 93,480
关键字:

中间策略