HDFS Ozone的Pipeline实现机制
来源:互联网 发布:知乎自媒体入住 编辑:程序博客网 时间:2024/05/21 22:50
前言
在现有HDFS中,每个副本块默认有3个副本,我们都知道这是为了容错而设计的。为了保持这3个副本的数据一致性,HDFS每次对数据进行写操作的时候,都是以Pipeline的方式进行更新。你可以理解为是一种流水线的方式:R1–>R2–>R3。中间如有一个环节出现问题,那么这次Pipeline更新就得重新来过。在HDFS对象存储服务Ozone中,同样会遇到多副本更新一致性的问题,所以它也需要有自己的Pipeline更新机制。此文,笔者就带领大家学习了解Ozone在这方面的实现。
Ozone的Pipeline概念
Pipeline概念是一个比较抽象的概念,你可以理解它是一个流水线模型。里面包含若干个“数据位置”信息。在HDFS中,这个Pipeline更新对象是block块,而在Ozone是,针对的数据单元是container。然后Ozone中的container再分配block块出去的。
在多数据副本的情况下,定义Pipeline的概念有什么用呢?答案是为了更新的数据一致性。前面也已经提过,Pipeline里面会包含目标位置信息,这个信息就是我们想要拿到的。知道这些信息,具体怎么更新,是另一回事,你可以自己实现一个更新策略,或者用外部开源框架实现。
下面是Ozone中的Pipeline类定义:
/** * A pipeline represents the group of machines over which a container lives. */public class Pipeline { // 容器名称 private String containerName; // 领导者id(外部副本更新机制框架可能会使用到) private String leaderID; // 数据副本所在节点 private Map<String, DatanodeID> datanodes; // pipeline内部允许包含的私有数据 private byte[] data; ...}
Ozone的Pipeline管理
下面我们来看Pipeline管理,Pipeline是用在做副本更新的,所以这里需要与Ozone现有的副本实现机制挂钩。目前Ozone可提供2种副本机制:
- 1.完全单副本方式,就是standalone模式。
- 2.用外部框架Apache Ratis(分布式一致性算法Raft的Java实现)实现多副本方式。
对应上面2种方式,衍生出2种Pipeline的管理类。下面是Pipeline的基类定义:
/** * Piepline管理接口 */public interface PipelineManager { /** * Pipeline获取方法 * * @param containerName container名称 * @param replicationFactor - 副本数 * @return a Pipeline. */ Pipeline getPipeline(String containerName, OzoneProtos.ReplicationFactor replicationFactor) throws IOException; /** * 创建Pipeline */ void createPipeline(String pipelineID, List<DatanodeID> datanodes) throws IOException;; /** * 关闭Pipeline */ void closePipeline(String pipelineID) throws IOException; /** * 列出Pipeline中的成员节点 * @return the datanode */ List<DatanodeID> getMembers(String pipelineID) throws IOException; /** * 用新节点更新Pipeline */ void updatePipeline(String pipelineID, List<DatanodeID> newDatanodes) throws IOException;}
针对第一种单副本的方式,作者是用了一个策略类来进行Pipeline节点的选择的。如下代码:
public class StandaloneManagerImpl implements PipelineManager { private final NodeManager nodeManager; private final ContainerPlacementPolicy placementPolicy; private final long containerSize; ... public Pipeline getPipeline(String containerName, OzoneProtos .ReplicationFactor replicationFactor) throws IOException { // 根据策略类进行指定数量节点的选择 List<DatanodeID> datanodes = placementPolicy.chooseDatanodes( replicationFactor.getNumber(), containerSize); // 根据选中节点,进行Pipeline对象的构造并返回 return newPipelineFromNodes(datanodes, containerName); }}
这里我们重点来看Pipeline如何进行构造的,进入newPipelineFromNodes方法。
private static Pipeline newPipelineFromNodes(final List<DatanodeID> nodes, final String containerName) { // 检查候选节点的数量 Preconditions.checkNotNull(nodes); Preconditions.checkArgument(nodes.size() > 0); // 选取第一个节点为领导节点 String leaderId = nodes.get(0).getDatanodeUuid(); Pipeline pipeline = new Pipeline(leaderId); // 进行成员节点的添加 for (DatanodeID node : nodes) { pipeline.addMember(node); } // 再设置container名称 pipeline.setContainerName(containerName); // 对象构造完毕,对象返回 return pipeline; }
同样对于Ratis的副本更新方式,也有对应的实现子类:RatisManagerImpl。它内部Pipeline获取方式如下:
public synchronized Pipeline getPipeline(String containerName, OzoneProtos.ReplicationFactor replicationFactor) { /** * 在ratis的方式下, * * 1. 如果有空闲的节点,创建一个全新的Pipeline(不会用到策略类) * * 2. 如果没有空闲节点,以轮询的方式复用现有的Pipeline. */ Pipeline pipeline = null; List<DatanodeID> newNodes = allocatePipelineNodes(replicationFactor); if (newNodes != null) { Preconditions.checkState(newNodes.size() == getReplicationCount(replicationFactor), "Replication factor " + "does not match the expected node count."); pipeline = allocateRatisPipeline(newNodes, containerName); } else { pipeline = findOpenPipeline(); } if (pipeline == null) { LOG.error("Get pipeline call failed. We are not able to find free nodes" + " or operational pipeline."); } return pipeline; }
这里具体细节笔者就不展开了。
在Ozone中,Pipeline是通过调用Pipeline选择器来创建Pipeline。代码如下:
public Pipeline getReplicationPipeline(ReplicationType replicationType, OzoneProtos.ReplicationFactor replicationFactor, String containerName) throws IOException { // 根据副本机制类型,获取Pipeline管理类 PipelineManager manager = getPipelineManager(replicationType); Preconditions.checkNotNull(manager, "Found invalid pipeline manager"); LOG.debug("Getting replication pipeline for {} : Replication {}", containerName, replicationFactor.toString()); // 传入副本数进行Pipeline的获取 return manager.getPipeline(containerName, replicationFactor); } private PipelineManager getPipelineManager(ReplicationType replicationType) throws IllegalArgumentException { switch(replicationType){ case RATIS: return this.ratisManager; case STAND_ALONE: return this.standaloneManager; case CHAINED: throw new IllegalArgumentException("Not implemented yet"); default: throw new IllegalArgumentException("Unexpected enum found. Does not" + " know how to handle " + replicationType.toString()); } }
最后在创建container的时候会触发到创建Pipeline的操作,代码如下:
public ContainerInfo allocateContainer(OzoneProtos.ReplicationType type, OzoneProtos.ReplicationFactor replicationFactor, final String containerName) throws IOException { ... lock.lock(); try { byte[] containerBytes = containerStore.get(containerName.getBytes(encoding)); if (containerBytes != null) { throw new SCMException("Specified container already exists. key : " + containerName, SCMException.ResultCodes.CONTAINER_EXISTS); } // 进行Pipeline对象的创建 Pipeline pipeline = pipelineSelector.getReplicationPipeline(type, replicationFactor, containerName); // pipeline信息设置到Container的信息中 containerInfo = new ContainerInfo.Builder() .setState(OzoneProtos.LifeCycleState.ALLOCATED) .setPipeline(pipeline) .setStateEnterTime(Time.monotonicNow()) .build(); containerStore.put(containerName.getBytes(encoding), containerInfo.getProtobuf().toByteArray()); } finally { lock.unlock(); } return containerInfo; }
在后续涉及到container的操作,都会用到这个Pipeline的对象信息。从上面还可以看出,Ozone在副本的设计上做的还是很灵活的,可以调整使用的策略,允许外部框架的介入,而不是说完全就自己设计死了一套,这里依靠2个概念的引入:ReplicationType和ReplicationFactor。笔者觉得这块设计确实令人眼前一亮。
Ozone利用Ratis实现副本一致性
最后我们来学习Ozone是如何借助外部框架实现副本数据一致性的,之前提到过Apache Ratis可以帮我们实现副本一致性,那么在Ozone它是如何使用的Ratis的呢?
其实使用的要点很简单:告诉它领导节点和成员节点即可,其它如何实现一致性要求,就是框架内部做的事情了。
而上述提到的领导节点和成员节点,在Pipeline定义是可以得到的。我们看下面的相关代码:
// 根据Pipeline信息对象初始化Raft客户端对象 static RaftClient newRaftClient(RpcType rpcType, Pipeline pipeline) { // 传入信息,领导节点,其它成员节点对 return newRaftClient(rpcType, toRaftPeerId(pipeline.getLeader()), toRaftPeers(pipeline)); } // 成员节点信息对 static List<RaftPeer> toRaftPeers(Pipeline pipeline) { return pipeline.getMachines().stream() .map(RatisHelper::toRaftPeer) .collect(Collectors.toList()); }
构造好这个Raft客户端之后,所有针对此Pipeline更新的操作都将由此客户端来调用,并且能保证更新的一致性。
- HDFS Ozone的Pipeline实现机制
- HDFS的新方向:Ozone对象存储
- 聊聊HDFS和Ozone的融合
- HDFS Ozone整体概述
- HDFS对象存储服务:Ozone的元数据管理
- HDFS对象存储:Ozone的块异步删除服务
- HDFS对象存储--Ozone架构设计
- Kafka到Hdfs的数据Pipeline整理
- Kafka到Hdfs的数据Pipeline整理
- 关于Tomcat的Pipeline机制
- Hadoop的HDFS文件存储实现机制
- pipeline机制
- 多线程的PipeLine实现实例
- gstreamer中pipeline的工作机制
- redis的pipeline机制解析与注意事项
- HDFS的数据通信机制
- Hadoop的HDFS机制
- HDFS的备份机制
- 理解java的三大特性之多态
- 【初探】 基数排序
- 三角形的外接圆
- 函数作为参数的使用
- java 快速获取当前日期,昨天日期,明天日期
- HDFS Ozone的Pipeline实现机制
- C++中 #ifdef 和#endif的作用
- LeetCode-Easy-Java-Longest Continuous Increasing Subsequence
- 深度学习——NIN
- mysql数据库mysql: [ERROR] unknown option '--skip-grant-tables'
- 大数据集群搭建之节点的网络配置过程(二)
- jquery ajax报Uncaught TypeError :Illegal invocation
- 洛谷 P2261 [CQOI2007]余数求和
- web项目的分层思想